React 注意点

代码分割,使首屏页面加载的更快

  • 根据情况采用代码分割(比如某个体积相对比较大的第三方库或插件(比如JS版的PDF预览库)只在单页应用(SPA)的某一个不是首页的页面使用了,这种情况就可以考虑代码分割,增加首屏的加载速度)
  • React 的懒加载
import React, { Suspense } from 'react';

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}

如上代码中,通过 import()React.lazySuspense 共同一起实现了 React 的懒加载,也就是我们常说了运行时动态加载,即 OtherComponent 组件文件被拆分打包为一个新的包(bundle)文件,并且只会在 OtherComponent 组件渲染时,才会被下载到本地。

Ref

  • React.createRef()

使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上,该ref的current属性将能拿到dom节点或组件的实例

class Child extends React.Component{
    constructor(props){
        super(props);
        this.myRef=React.createRef();
    }
    componentDidMount(){
        console.log(this.myRef.current);
    }
    render(){
        return <input ref={this.myRef}/>
    }
}
  • React.forwardRef

用来创建子组件,以传递ref

//子组件(通过forwardRef方法创建)
const Child=React.forwardRef((props,ref)=>(
  <input ref={ref} />
));

//父组件
class Father extends React.Component{
  constructor(props){
    super(props);
    this.myRef=React.createRef();
  }
  componentDidMount(){
    console.log(this.myRef.current);
  }
  render(){
    return <Child ref={this.myRef}/>
  }
}

context

生成全局的数据,不用通过props 一层一层传递,而是组件间可以共享数据,可用 Redux 代替

const MyContext = React.createContext(defaultValue);

  • **Context.provider ==> 提供值并传值 **

<MyContext.Provider value={/* 某个值 */}>

Provider 接收一个 value 属性,传递给消费组件

  • Context.Consumer ==> 使用值,并可以更改context的值
<MyContext.Consumer>
  {value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>

Fragments

当一个组件返回多个元素时,不想增加额外的标签如 div 进行包裹时,可用 Fragments

render() {
  return (
    <React.Fragment>
      <ChildA />
      <ChildB />
      <ChildC />
    </React.Fragment>
  );
}

高阶组件(HOC)

参数是组件,返回新的组件

Portals

Portal 将子节点渲染到存在于父组件以外的 DOM 节点即可以被放置在 DOM 树中的任何地方。

ReactDOM.createPortal(child, container)

第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个 DOM 元素。

一个 portal 的典型用例是当父组件有 overflow: hiddenz-index 样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框

export default const Portal = ({ children }) => {
    const el = useMemo(() => document.body, []);
    return ReactDOM.createPortal(children, el);
};

React中的Diffing算法

当对比两颗树时,React 首先比较两棵树的根节点。不同类型的根节点元素会有不同的形态。

如果根节点不同,React会拆卸原有的树,重新生成新的树,包括里面的组件都会全部销毁

比对同一类型的元素,React会保留Dom节点,仅比对及更新有改变的属性

对子节点进行递归时,在默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个 mutation。

  • 在子元素列表 末尾 增加元素时:
<ul>
  <li>first</li>
  <li>second</li>
</ul>

<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

这种情况的更变开销比较小,前面两个是一样的,不用变更,只需要在末尾增加一个就行了

  • 在子元素列表 头部 增加元素时:
<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

这种开销会比较大,React 会针对每个子元素进行 mutate,这种情况下会导致性能问题

解决:

给列表增加key属性,当子元素拥有key时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

现在 React 知道只有带着 '2014' key 的元素是新元素,带着 '2015' 以及 '2016' key 的元素仅仅移动了。这样就使得之前的低效变得高效

Tips:

当使用数组下标作为key时:那么修改顺序时会修改当前的 key,导致非受控组件的 state(比如输入框)可能相互篡改导致无法预期的变动

posted @ 2022-08-11 18:01  小蜗蜗蜗牛^o^  阅读(39)  评论(0编辑  收藏  举报