1. Portal
官方解释:Portals  提供了一个最好的在父组件包含的DOM结构层级外的DOM节点渲染组件的方法。其实它的作用我们可以认为是传送门, 它能够帮助我们在父组件的外部渲染出子组件,应用场景例如:当子组件需要从视觉上“跳出”其容器时,譬如对话框、悬浮卡、提示框等。
 
例如antUI组件库中的弹出框:(在点击的瞬间干组件下弹出一个新的div)
 

- 我们也许会说,使用
css样式中的定位就可以实现,但其实这种方法是存在弊端的,因为在使用css样式的过程中必然会存在父子之间的层级关系,那么此时当父组件的dom元素有  overflow:hidden 或者z-inde 样式,子层级就会受到它的影响从而出现问题,但是Portal不会有这种问题,因为他是可以指定位置的渲染html结构,也就是所可以避免出现父子层级结构的出现! 
案例展示:
APP.js(根组件)
 import React, { Component } from 'react' import './App.css' import PortalDialog from './compoents/PortalDialog' export default class App extends Component {          state = {         isShow: false     }     render() {         return (             <div className="box" onClick={() => {                 // portal支持事件冒泡                 /*                 	虽然通过portal渲染的元素在父组件的盒子之外,                 	但是渲染的dom节点仍在React的元素树上,在那个dom元素上                    	的点击事件仍然能在dom树中监听到。                 */                 console.log("box身上监听的事件")             }}>                 {/* 左边盒子 */}                 <div className="left"></div>                 {/* 右边盒子 */}                 <div className="right">                     <button onClick={() => {                         this.setState({                             isShow: true                         })                     }}>点击</button>                     {                         this.state.isShow && <PortalDialog onClose={() => {                             // 子传父通信使用props传递回调函数                             this.setState({                                 isShow: false                             })                         }}>                             <div>插槽元素1</div>                             <div>插槽元素2</div>                             <div>插槽元素3</div>                         </PortalDialog>                     }                 </div>             </div>         )     } }
 
  | 
 
Portal组件(遮罩层)
 import React, { Component } from 'react' import { createPortal } from 'react-dom'
 
 
 
 
 
 
  export default class Dialog extends Component {     render() {         return createPortal(                          <div style={{                 width: '100%', height: '100%', position: 'fixed',                 left: 0, top: 0, background: 'rgba(0,0,0,0.7)',                 color: "white"             }}>                 Dialog-                 <div>loading-正在加载中</div>                 {/* 支持使用插槽 */}                 {this.props.children}                 <button onClick={this.props.onClose}>close</button>             </div>,                          document.body)     } }
 
  | 
 
样式代码:
*{     margin:0;     padding:0; }
  .left,.right{     height: 100vh; }
  .box{     display:flex; }
  .left{     width: 200px;     background:yellowgreen;     position: relative; }
  .right{     flex:1;     background:skyblue;     position: relative; }
  | 
 
结果展示:

2. 懒加载(Lazy 和 Suspense)
- 懒加载是什么?预加载又是什么? 我们在项目中正常的引入一些资源或者组件的时候通过是使用
import或者require来直接引入的,这会导致项目在启动的时候就会全部加载我们使用的资源或组件,这就是预加载,但是当资源或者组组件过大的时候就会出现项目启动时间过程,用户体验就不好了,这时我们就可以使用React中的懒加载,所谓懒加载其实我们可以认为是动态加载资源或组件,当我们用到它的时候就加载它,这就可以优化当项目的内容丰富,页面多的时候,首屏同时加载过多的内容,会导致卡顿不流畅响应速度慢、用户等待时间过长等问题。 
基本使用:
 import React, { Component, Suspense } from 'react'
 
 
 
 
 
  const Nowplaying = React.lazy(() => import('./components/Nowplaying')) const Comingsoon = React.lazy(() => import('./components/Comingsoon')) export default class App extends Component {     state = {         type: 1     }     render() {         return (             <div>                 {/* 点击修改状态 */}                 <button onClick={() => {                     this.setState({                         type: 1                     })                 }}>正在热映</button>                 <button onClick={() => {                     this.setState({                         type: 2                     })                 }}>即将上映</button>
                  {/* 需要使用Suspense来结合使用,不然会报错,且fallback必须要有内容 */}                 <Suspense fallback={<div>正在加载中....</div>}>                     {                         this.state.type === 1 ?                             <Nowplaying></Nowplaying>                             :                             <Comingsoon></Comingsoon>                     }                 </Suspense>
              </div>         )     } }
 
  | 
 
结果展示:

总结:
单独使用React.lazy时项目是会报错的,因为在React使用了 lazy 之后,会存在一个加载中的空档期,React不知道在这个空档期中该显示什么内容,所以需要我们指定。接下来就要使用到suspense加载指示器,且注意,在Suspense 使用的时候, fallback 一定是存在且有内容的(可以为html片段,或者一段jsx代码), 否则会报错。
3. forwordRef(透传Ref)
- 引用传递(
Refforwading)是一种通过组件向子组件自动传递 引用ref 的技术。对于应用者的大多数组件来说没什么作用。但是对于有些重复使用的组件,可能有用。例如某些input组件,需要控制其focus,本来是可以使用ref来 控制,但是因为该input已被包裹在组件中,这时就需要使用Ref forward来透过组件获得该`input的引用。可以透传多层 
案例展示:
- 在父组件中使用
ref获取子组件中的input的DOM节点 
1. 未使用forwardRef
 
 
 
 
 
 
  import React, { Component } from 'react'
 
  export default class App extends Component {     mytext = null     render() {         return (             <div>                 <button onClick={()=>{                     // console.log('子组件的DOM节点',this.mytext)                     this.mytext.current.focus()//获取子组件中input的焦点                     this.mytext.current.value=""//清空它的内容                 }}>获取焦点</button>
                  <Child callback={(el)=>{                     // 将子组件传递过来的DOM节点赋值给父组件中的临时变量                     this.mytext = el                 }}/>             </div>         )     } }
 
  class Child extends Component{     mytext= React.createRef()
           componentDidMount() {         this.props.callback(this.mytext)     }          render(){         return <div style={{background:"yellow"}}>             {/* 使用ref属性获取请指定的DOM元素 */}             <input defaultValue="11111111" ref={this.mytext}/>         </div>     } }
 
  | 
 
2. 使用forwardRef(透传属性)后
 
 
 
 
 
 
 
 
 
 
 
 
 
  import React, { Component, forwardRef } from 'react'
 
  export default class App extends Component {     mytext =React.createRef()
      render() {         return (             <div>                 <button onClick={()=>{                     console.log('获取到子组件中的DOM元素',this.mytext)                     this.mytext.current.value=""// 获取到子组件中input的value属性                     this.mytext.current.focus()// 获取到子组件中input的焦点                 }}>获取焦点</button>
                  {/* 直接给子组件传递ref属性获取子组件内部的DOM节点 */}                 <Child ref={this.mytext}/>             </div>         )     } }
 
 
 
 
 
 
  const Child = forwardRef((props,ref)=>{     return <div style={{background:"yello"}}>         {/* 标上父子组件传递过来的ref属性 */}         <input ref={ref} defaultValue="1111"/>     </div> })
 
  | 
 
结果展示:

注意:使用forwardRef属性只能透传一个Ref,想要透传多个的话只能使用上面的常规方法了!
4. memo
一般来讲,有父子层级的组件,父组件在重新渲染的时候,里面的子组件也会重新渲染,也就是说:只要父组件的某一个状态改变,父组件下面所有的子组件不论是否使用了该状态,都会进行重新渲染。这就导致了一个问题: 父组件中的状态发生改变,重新渲染是无可避免的,但是如果此时子组件中并没有发生变化,那么此时子组件也重新渲染这个操作就显得多余了,而且还浪费性能!此时就引出了memo高阶组件,与之类似的还有PureComponent,两者同样是用于优化子组件频繁渲染的问题的
 
memo的作用: memo是react的一种缓存技术,这个函数可以检测从父组件接收的props,并且在父组件改变state的时候比对这个state是否是本组件在使用,如果不是,则拒绝重新渲染。
 
案例展示:
- 在父组件中设置两个按钮,点击第一个按钮,重新渲染父组件但是保持子组件不重新渲染,第二个按钮,重新渲染子组件
 
  import React, { Component, memo } from 'react'
 
 
 
 
   
 
  export default class App extends Component {     state ={         name:"lam",         title:"aaaaaa"     }     render() {         return (             <div>                 {/* 第一个按钮,点击修改name状态(在父组件中展示) */}                 <button onClick={()=>{                     this.setState({                         name:"肥林"                     })                 }}>点击更新name属性(不更新子组件)</button>
                  {/* 第二个按钮,点击修改title状态(在子组件中展示) */}                 <button onClick={()=>{                     this.setState({                         title:"bbbbbbbbbb"                     })                 }}>点击更新title属性(更新子组件)</button><br/>
                  {/* 展示name属性 */}                 {this.state.name}                 {/* 使用props将title状态传给子组件 */}                 <Child title={this.state.title}/>             </div>         )     } }
   
 
 
  const Child = memo((props)=>{     console.log('子组件重新更新了!') return <div>子组件-{props.title}</div> })
   | 
 
结果展示:

memo与PureComponent区别
PureComponent 只能用于class组件,memo 用于functional 组件 
memo存在的问题:
memo只能进行浅拷贝来校验决定是否触发重新渲染。例如,当我们在父组件给子组件props传递一个数组(对象)时,我们修改数组中的一个数组(如push或者删除),那么此时的子组件中的props改变了,理论上来讲,子组件应该会重新渲染,但实际上不会! 那是因为我们修改数组时,理论上看似数组变了,实际上,变的只是堆中的数据,而我们的props是指向存在栈中数据的地址,修改数组时地址是不会改变的,因此memo检测到的是指向栈的地址没有改变,检测不到栈中的数据有没有改变, 因此要想解决这个问题,就需要向props传递一个新的数组。
 
由此可见,memo只能进行浅拷贝来校验决定是否触发重新渲染。所以改变数组(对象)的props时候记得返回一个全新的数组(对象)
 
const [list,setList] = useState([1,2,3,4,5]);
  setList(list.push(1)); 
  setList([...list,1]); 
 
   |