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.lazy
和Suspense
共同一起实现了 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: hidden
或 z-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(比如输入框)可能相互篡改导致无法预期的变动