外贸网站策划,在线直播免费服务器,新材料 东莞网站建设,搜狗收录批量查询一. 发生更新的时机以及顺序## image.png props/state改变render函数重新执行产生新的VDOM树新旧DOM树进行diff计算出差异进行更新更新到真实的DOM 二. React更新流程## React将最好的O(n^3)的tree比较算法优化为O(n)。 同层节点之间相互比较#xff0c;不跨节点。不同类型的节… 一. 发生更新的时机以及顺序## image.png props/state改变render函数重新执行产生新的VDOM树新旧DOM树进行diff计算出差异进行更新更新到真实的DOM 二. React更新流程## React将最好的O(n^3)的tree比较算法优化为O(n)。 同层节点之间相互比较不跨节点。不同类型的节点产生不同的树结构如果该节点不同会将旧tree中该节点的子树全部删掉。直接生成新的子树挂载到DOM中。开发中可以通过key来指定哪些节点在不同的渲染下保持稳定。 三. 不同情况## 情况一对比不同类型的元素 当节点为不同的元素React会拆卸原有的树并且建立起新的树。 当一个元素改变会触发一个完整的重建流程。当卸载一棵树时对应的DOM节点也会被销毁组件实例将执行componentWillUnmount()方法。当建立一棵新树时对应得DOM节点会被创建以及插入到DOM中组件实例将执行componentWillMount()方法紧接着componentDidMount()方法。 子树销毁元素不会复用。#### 情况二对比同一类型的元素 当对比两个相同类型的React元素时React会保留DOM节点仅比对及更新有改变的属性 比如下面例子 image.png React知道只需要修改DOM元素上的className属性。 image.png -当 更新style属性时React仅更新有所改变的属性没有变化的属性不会变。 如果是同类型的组件元素组件会保持不变React会更新该组件的props并且调用componentWillReceiveProps()和componentWillUpdate()方法下一步调用render()方法diff算法将在之前的结果以及新的结果中进行递归。 情况三对子节点进行递归 image.png 默认条件下当递归DOM节点的子元素时React会同时遍历两个子元素的列表当产生差异时生成一个mutation。如上图前两个比较相同不会有mutation。最后一个比较产生一个mutation将其插入到新的DOM树中即可。当然这是理想情况 image.png 如果我们在中间插入一条数据 React会对每一个子元素产生一个mutation而不是保持其不变。这种方式会有一定的性能问题。 所以这时需要key来优化### 四. key优化## 在尾部添加数据 有无key意义并不大。 在前面插入数据 这种情况在没有key的情况下所有li都需要进行修改。当子元素拥有key时React使用key来匹配原有树上的子元素以及最新树上的子元素这种情况下原有的元素只是发生了位移。 render() {return (divh2电影列表/h2ul{this.state.movies.map((item,index) {return li key{item}{item}/li})}/ulbutton onClick{e this.insertMovie()}添加电影/button/div)}key的注意事项 key应该是唯一的。key不要使用随机数随机数在下一次render时会重新生成一个数字。使用index作为key对性能是没有优化的id比较合适。 五. 组件嵌套的render调用## import React, { Component } from react// Header
function Header() {console.log(Header被调用);return h2我是Header组件/h2
}// Banner
class Banner extends Component {render() {console.log(Banner的render函数被调用);return h3我是bannner组件/h3}
}function ProductList() {console.log(ProductList被调用);return (ulli商品列表1/lili商品列表2/lili商品列表3/lili商品列表4/lili商品列表5/li/ul)
}
// Main
class Main extends Component {render() {console.log(Main render函数被调用);return (divBanner /ProductList //div )}
}
// Footer
function Footer() {console.log(Footer被调用);return h2我是Footer组件/h2
}export default class App extends Component {constructor(props) {super(props);this.state {counter: 0,}}render() {console.log(App render函数被调用);return (divh2当前计数:{this.state.counter}/h2button onClick{ethis.increment()}1/buttonHeader /Main /Footer //div)}increment(){this.setState({counter: this.state.counter 1})}
} 调用一个无关的函数界面改变时按理来说不应该让别的没有改变的东西重新render。这个例子中我们在前面插入了一个h2和button标签现在点击按钮时全局都会重新渲染。现在对其进行优化 六. 组件嵌套的render调用的优化## 调用完setState后不想render时阻断其渲染。使用shouldComponentUpdate() {}这个生命函数默认情况下其返回true也就是重新渲染手动设置为false后将不会重新渲染但不影响初始化的渲染。我们的目的是想要阻断时阻断事件发生后与界面没有依赖不想阻断时渲染如下代码。 shouldComponentUpdate(nextProps, nextState){if(this.state.counter ! nextState.counter){return true;}return false;}以上为简单情况当组件变多后情况将很复杂函数/类组件都需要考虑到### 每个类都设置该生命周期函数太麻烦。我们通过继承PureComponent而不是Component来进行简化其会对state和props进行比较来决定是否重新render。shouldComponentUpdate在源码中进行更新时决定是否需要render。回溯到源码ReactFiberClassComponent中时有如下方法 function checkShouldComponentUpdate(workInProgress,ctor,oldProps,newProps,oldState,newState,nextContext,
) {const instance workInProgress.stateNode;
// 判断有无该生命周期函数if (typeof instance.shouldComponentUpdate function) {
startPhaseTimer(workInProgress, shouldComponentUpdate);
// -----------------
// 核心代码const shouldUpdate instance.shouldComponentUpdate(newProps,newState,nextContext,);stopPhaseTimer();return shouldUpdate;}
// -----------------if (ctor.prototype ctor.prototype.isPureReactComponent) {return (!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState));}return true;
}该方法最终返回true/false。这个对应React中PureComponent的特点 image.png 其中isPureReactComponent属性对应上面放出来源代码中对原型中isPureReactComponent属性的判断。如果有isPureReactComponent属性则对oldPropsoldState和newPropsnewState进行一个浅层比较。通过浅层比较来判断是否发生了改变。 追溯到shallowEqual方法源码中 import is from ./objectIs;const hasOwnProperty Object.prototype.hasOwnProperty;/*** Performs equality by iterating through keys on an object and returning false* when any key has values which are not strictly equal between the arguments.* Returns true when the values of all keys are strictly equal.*/
function shallowEqual(objA: mixed, objB: mixed): boolean {if (is(objA, objB)) {return true;}if (typeof objA ! object ||objA null ||typeof objB ! object ||objB null) {return false;}const keysA Object.keys(objA);const keysB Object.keys(objB);if (keysA.length ! keysB.length) {return false;}// Test for As keys different from B.for (let i 0; i keysA.length; i) {if (!hasOwnProperty.call(objB, keysA[i]) ||!is(objA[keysA[i]], objB[keysA[i]])) {return false;}}return true;
}export default shallowEqual;先判断两个对象相同则true返回后取反表示不需要更新。接着分别判断两个对象不是对象或者为null时返回false强制刷新然后将两个对象中的keys取出来若长度不想等则返回false若相等则对其中属性进行比较不相等则返回false进行刷新。 这就回到我们案例中只有AppHeaderFooter的render被调用。 PureComponent对props和state进行shallowEqual 。MainBannerProductList没有依赖任何props/state所以没有重新渲染。 开发中只需要shallowEqual深层比较非常浪费性能。 PureComponent可以解决类组件的render调用但解决不了函数式组件 七. memo的使用优化函数式组件## memo为高阶组件。 const MemoHeader memo(function Header() {console.log(Header被调用);return h2我是Header组件/h2
})image.png 我们将原来的函数组件传入memo函数中生成一个新的组件类型。将Footer也进行转换这样只有App重新渲染了但我们没有更改ProductList其也没有重新渲染原因是在Main中重新渲染已经被阻止了。为了以防万一也可以用memo优化。 理论上建议所有类组件都用PureComponent所有函数组件都包裹memo © 著作权归作者所有,转载或内容合作请联系作者 喜欢的朋友记得点赞、收藏、关注哦