从渲染原理到性能优化
黄琼(daisyhhuang) 腾讯前端工程师,IMWeb团队成员 企鹅辅导 IMWeb 社区 邮箱:daisyhhuang@tencent.com 微信:you-qiong-yue
一、JSX如何生成element 二、element如何生成真实DOM节点 三、性能优化 四、React 16异步渲染方案
一、JSX如何生成element 二、element如何生成realDOM 三、性能优化 四、React 16异步渲染方案
JSX return( React.createElement( 'div', { className: 'cn' }, Header, null, 'Hello, This is React' ), 'Start to learn right now!' 'Right Reserve' )) return( <div className="cn"> <Header> Hello, This is React </Header> <div>Start to learn right now!</div> Right Reserve. </div> ) Babel createElement的作用是生成element, 参数如下 1、type 2、attributes,如果没有的话,可以 为null 3、children
React.createElement 何时被执行? { type: 'div', props: { className: 'cn', children: [ { type: function Header, props: { children: 'Hello, This is React' } }, type: 'div', children: 'start to learn right now!' 'Right Reserve' ] } } React.createElement( 'div', { className: 'cn' }, Header, null, 'Hello, This is React' ), ‘Start to learn right now!' 'Right Reserve' ) Render 函数被调用的时候执行
Element Children现在看到有三种类型: 1、String, 2、原生DOM节点的element { type: 'div', props: { className: 'cn', children: [ { type: function Header, props: { children: 'Hello, This is React' } }, type: 'div', children: 'start to learn right now!' 'Right Reserve' ] } } Children现在看到有三种类型: 1、String, 2、原生DOM节点的element 3、React components – 自定义组件的element 不单单是Object类型 4、false, null,undefined,number 5、数组 – 使用map方法的时候
二、element如何生成realDOM 一、JSX如何生成element 二、element如何生成realDOM 三、性能优化 四、React 16异步渲染方案 我们知道React是函数式的UI编程方式,element实际就是描述你想要在屏幕上看到什么。所以我们来看看React是怎么帮助我们把它生成浏览器的真实DOM节点。
初始化element ReactDOMComponent ReactDOMComponent ReactCompositeComponentWrapper ReactCompositeComponentWrapper ReactDOMTextComponent ReactDOMTextComponent ReactDOMEmptyComponent ReactDOMEmptyComponent 私有类: React自己使用,不会暴露给用户,常用方 法:mountComponent, updateComponent等 公共类:自定义的组件
ReactDOMComponent 直接操作浏览器DOM元素 mountComponent(container): 会将element转成真实DOM节点, 并且插入到相应的container里,然后返回markup(real DOM)。 mountComponent(container) { const domElement = document.createElement(this._currentElement.type); const textNode = document.createTextNode(this._currentElement.props.children); domElement.appendChild(textNode); container.appendChild(domElement); return domElement; } { type: 'div', props: { className: 'cn', children: 'Hello world', }
ReactCompositeComponentWrapper mountComponent: 实例化自定义组件,最后是通过递归调用到ReactDOMComponent的mountComponent方法 来得到真实DOM。
首次渲染 Example <div>Hello World!</div>
ReactCompositeComponentWrapper 生命周期函数在哪被调用? React.render(<Example />, container) mountComponent: 1、实例化Example,得到instance对象 2、renderedElement = Instance.render(); 3、初始化renderedElement ,得到child 4、child.mountComponent(container) componentWillMount { type: function Example, props: { children: null } <div>Hello World!</div> componentDidMount { type: 'div', props: { children: 'Hello World' } ReactDOMComponent mountComponent: 根据element来生成对应的真实DOM节点 递归调用 生命周期
渲染更新 props 更新 state
shouldComponentUpdate setState 将传入的state放进pendingState的数组里 是 否 dirtyComponent 当前是否处于批量更新 遍历更新 dirtyComponent ReactCompositeComponentWrapper 生命周期函数在哪被调用? shouldComponentUpdate updateComponent 1、计算出nextState 2、render()得到nextRenderElement 3、与prevElement 进行Diff 比较,更新节点 componentWillUpdate componentDidUpdate ReactDOMComponent updateComponent 用于直接操作更新浏览器DOM元素 生命周期
Diff算法 1、两个相同的组件产生类似的DOM结构,不同组件产生不同DOM结构 2、对于同一层级的一组子节点,它们可以通过唯一的id区分
1、不同节点类型 Tips:保持DOM标签类型的稳定 D A D 更新 B B C C B C
2、相同节点类型 自定义组件 <div className="before" title="stuff”/> <div className="after" title="stuff”/> 自定义组件
3、子节点比较 Tips: 1、保持DOM结构的稳定性 2、map的时候,加key A A B C B D C C
一、JSX如何生成element 二、element如何生成realDOM 三、性能优化 四、React 16异步渲染方案
性能优化 -工具介绍 1、why-did-you-update
性能优化 -工具介绍 2、react-addons-perf
性能优化 1、Mount/Unmount Key 稳定性 - 保持标签的稳定 <div> -> <section> - 保持DOM结构的稳定
是否写组件的时候都直接使用PureComponent? 性能优化 是否写组件的时候都直接使用PureComponent? 2、避免重复渲染 shouldComponentUpdate PureComponent(immutable.js) 分离组件,只传入关心的值 3、使用Pure Functional Component recompose
目前react 性能优化的点主要集中在防止重复渲染,DOM稳定性的 方面: 但是大家看一个问题: B D E F G
一、JSX如何生成element 二、element如何生成realDOM 三、性能优化 四、React 16异步渲染方案
React 16 改动 1、比较阶段 – 可被打断 2、commit阶段 – 不可被打断
Stack reconciler Fiber reconciler 图片来源
React 16生命周期的变化 Phase 1 Reconciliation/render Phase 2 commit componentWillMount componentWillReceiveProps componentWillUpdate shouldComponentUpdate getDerivedStateFromProps Phase 1 Reconciliation/render Phase 2 commit componentDidMount componentDidUpdate componentWillUnmount
getDerivedStateFromProps getDerivedFromProps getDerivedStateFromProps 图片来源
Q & A
拓展阅读 1、function component 九个推荐使用functional component的原因 2、getDerivedFromProps 如何异步渲染开启后,我们要做的改变 你可能不需要使用getDerivedState 3、presentational/container components