精华内容
下载资源
问答
  • React渲染原理

    2019-10-17 00:29:09
    渲染原理 渲染:生成用于显示的对象,以及将这些对象形成真实的DOM对象 React元素:React Element,通过React.createElement创建(语法糖:JSX) 例如: <div><h1>标题</h1></div> <...

    渲染原理

    渲染:生成用于显示的对象,以及将这些对象形成真实的DOM对象

    • React元素:React Element,通过React.createElement创建(语法糖:JSX)
      • 例如:
      • <div><h1>标题</h1></div>
      • <App />
    • React节点:专门用于渲染到UI界面的对象,React会通过React元素,创建React节点,ReactDOM一定是通过React节点来进行渲染的
    • 节点类型:
      • React DOM节点:创建该节点的React元素类型是一个字符串
      • React 组件节点:创建该节点的React元素类型是一个函数或是一个类
      • React 文本节点:由字符串、数字创建的
      • React 空节点:由null、undefined、false、true
      • React 数组节点:该节点由一个数组创建
    • 真实DOM:通过document.createElement创建的dom元素

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gvhp5cld-1571243620529)(assets/2019-07-25-13-51-08.png)]

    首次渲染(新节点渲染)

    1. 通过参数的值创建节点
    2. 根据不同的节点,做不同的事情
      1. 文本节点:通过document.createTextNode创建真实的文本节点
      2. 空节点:什么都不做
      3. 数组节点:遍历数组,将数组每一项递归创建节点(回到第1步进行反复操作,直到遍历结束)
      4. DOM节点:通过document.createElement创建真实的DOM对象,然后立即设置该真实DOM元素的各种属性,然后遍历对应React元素的children属性,递归操作(回到第1步进行反复操作,直到遍历结束)
      5. 组件节点
        1. 函数组件:调用函数(该函数必须返回一个可以生成节点的内容),将该函数的返回结果递归生成节点(回到第1步进行反复操作,直到遍历结束)
        2. 类组件:
          1. 创建该类的实例
          2. 立即调用对象的生命周期方法:static getDerivedStateFromProps
          3. 运行该对象的render方法,拿到节点对象(将该节点递归操作,回到第1步进行反复操作)
          4. 将该组件的componentDidMount加入到执行队列(先进先出,先进先执行),当整个虚拟DOM树全部构建完毕,并且将真实的DOM对象加入到容器中后,执行该队列
    3. 生成出虚拟DOM树之后,将该树保存起来,以便后续使用
    4. 将之前生成的真实的DOM对象,加入到容器中。
    const app = <div className="assaf">
        <h1>
            标题
            {["abc", null, <p>段落</p>]}
        </h1>
        <p>
            {undefined}
        </p>
    </div>;
    ReactDOM.render(app, document.getElementById('root'));
    

    以上代码生成的虚拟DOM树:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5nqzEhGi-1571243620530)(assets/2019-07-25-14-17-04.png)]

    
    function Comp1(props) {
        return <h1>Comp1 {props.n}</h1>
    }
    
    function App(props) {
        return (
            <div>
                <Comp1 n={5} />
            </div>
        )
    }
    
    const app = <App />;
    ReactDOM.render(app, document.getElementById('root'));
    

    以上代码生成的虚拟DOM树:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pqfBEa8A-1571243620532)(assets/2019-07-25-14-49-53.png)]

    class Comp1 extends React.Component {
        render() {
            return (
                <h1>Comp1</h1>
            )
        }
    }
    
    class App extends React.Component {
        render() {
            return (
                <div>
                    <Comp1 />
                </div>
            )
        }
    }
    
    const app = <App />;
    ReactDOM.render(app, document.getElementById('root'));
    

    以上代码生成的虚拟DOM树:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gzwBDe0w-1571243620536)(assets/2019-07-25-14-56-35.png)]

    更新节点

    更新的场景:

    1. 重新调用ReactDOM.render,触发根节点更新
    2. 在类组件的实例对象中调用setState,会导致该实例所在的节点更新

    节点的更新

    • 如果调用的是ReactDOM.render,进入根节点的对比(diff)更新
    • 如果调用的是setState
        1. 运行生命周期函数,static getDerivedStateFromProps
        1. 运行shouldComponentUpdate,如果该函数返回false,终止当前流程
        1. 运行render,得到一个新的节点,进入该新的节点的对比更新
        1. 将生命周期函数getSnapshotBeforeUpdate加入执行队列,以待将来执行
        1. 将生命周期函数componentDidUpdate加入执行队列,以待将来执行

    后续步骤:

    1. 更新虚拟DOM树
    2. 完成真实的DOM更新
    3. 依次调用执行队列中的componentDidMount
    4. 依次调用执行队列中的getSnapshotBeforeUpdate
    5. 依次调用执行队列中的componentDidUpdate

    对比更新

    将新产生的节点,对比之前虚拟DOM中的节点,发现差异,完成更新

    问题:对比之前DOM树中哪个节点

    React为了提高对比效率,做出以下假设

    1. 假设节点不会出现层次的移动(对比时,直接找到旧树中对应位置的节点进行对比)
    2. 不同的节点类型会生成不同的结构
      1. 相同的节点类型:节点本身类型相同,如果是由React元素生成,type值还必须一致
      2. 其他的,都属于不相同的节点类型
    3. 多个兄弟通过唯一标识(key)来确定对比的新节点

    key值的作用:用于通过旧节点,寻找对应的新节点,如果某个旧节点有key值,则其更新时,会寻找相同层级中的相同key值的节点,进行对比。

    key值应该在一个范围内唯一(兄弟节点中),并且应该保持稳定
    相同key值的组件,组件对象是通用的,会保留之前的状态。
    key值的作用:用来确定节点对比的目标

    找到了对比的目标

    判断节点类型是否一致

    • 一致

    根据不同的节点类型,做不同的事情

    空节点:不做任何事情

    DOM节点

    1. 直接重用之前的真实DOM对象
    2. 将其属性的变化记录下来,以待将来统一完成更新(现在不会真正的变化)
    3. 遍历该新的React元素的子元素,递归对比更新

    文本节点

    1. 直接重用之前的真实DOM对象
    2. 将新的文本变化记录下来,将来统一完成更新

    组件节点

    函数组件:重新调用函数,得到一个节点对象,进入递归对比更新

    类组件

    1. 重用之前的实例
    2. 调用生命周期方法getDerivedStateFromProps
    3. 调用生命周期方法shouldComponentUpdate,若该方法返回false,终止
    4. 运行render,得到新的节点对象,进入递归对比更新
    5. 将该对象的getSnapshotBeforeUpdate加入队列
    6. 将该对象的componentDidUpdate加入队列

    数组节点:遍历数组进行递归对比更新

    • 不一致

    整体上,卸载旧的节点,全新创建新的节点

    创建新节点

    进入新节点的挂载流程

    卸载旧节点

    1. 文本节点、DOM节点、数组节点、空节点、函数组件节点:直接放弃该节点,如果节点有子节点,递归卸载节点
    2. 类组件节点
      1. 直接放弃该节点
      2. 调用该节点的componentWillUnMount函数
      3. 递归卸载子节点

    没有找到对比的目标

    新的DOM树中有节点被删除

    新的DOM树中有节点添加

    • 创建新加入的节点
    • 卸载多余的旧节点
    展开全文
  • React之React渲染原理

    2020-10-20 19:25:12
    文章目录一、react如何将代码显示到页面上(1)JSX生成Element1. JSX语法2. 使用babelJSX语法进行转换3. 创建虚拟Dom(2)Element生成Dom 一、react如何将代码显示到页面上 每当我们新建react项目时,项目创建成功...

    一、react如何将代码显示到页面上

    每当我们新建react项目时,项目创建成功后,运行npm start后会出现如下界面:
    在这里插入图片描述
    可以观察到时app.js中对该页面进行的设计,代码如下:
    在这里插入图片描述
    观察index.js发现最终代码是在这里执行的:
    在这里插入图片描述
    React将代码显示出来主要有两个步骤:

    1. JSX转化为Element
    2. Element转化为Dom

    (1)JSX生成Element

    1. JSX语法

    可以看到第一个张图片中的render函数中return了一段html片段,这段片段就是JSX语法

    定义:JSX 即Javascript XML,它是对JavaScript 语法扩展。React 使用 JSX 来替代常规的 JavaScript。你也可以认为JSX其实就是JavaScript。当遇到<,JSX就当HTML解析,遇到{就当JavaScript解析。

    优点
    执行速度更快,因为它在编译为JavaScript代码后进行了优化

    2. 使用babel对JSX语法进行转换

    return (
        <div className="cn">
             <Header> Hello, This is React </Header>
             <div>Start to learn right now!</div>
             Right Reserve.
        </div>
    )
    

    通过babel编译成React.createElement的表达式。

    return (
        React.createElement(
            'div',
            { className: 'cn' },
            React.createElement(
                Header,
                null,
                'Hello, This is React'
            ),
            React.createElement(
                'div',
                null,
                'Start to learn right now!'
            ),
            'Right Reserve'
        )
    )
    

    当render函数被调用时,会执行React.createElement的表达式,返回Element元素

    3. 创建虚拟Dom

    调用React.createElement方法时,React 把真实的 DOM 树转换成 Javascript 对象树,会创建虚拟Dom树,也就是 Virtual Dom。

    每次数据更新后,重新计算 Virtual Dom ,并和上一次生成的 virtual dom 做对比,对发生变化的部分做批量更新。

    而 React 是通过创建与更新虚拟元素 Virtual Element 来管理整个Virtual Dom 的。


    虚拟元素可以理解为真实元素的对应,它的构建与更新都是在内存中完成的,并不会真正渲染到 dom 中去。

    虚拟dom 实际上是对实际Dom的一个抽象,是一个js对象。
    react所有的表层操作实际上是在操作Virtual dom。

    (2)Element生成Dom

    上一步通过JSX获得了虚拟Dom树,现在需要将虚拟Dom转化为真实的Dom,可以看到第二张图里的ReactDOM.render 方法。

    这时可以利用 ReactDOM.render 方法,传入一个 reactElement 和一个 作为容器的 DOM 节点。

    而这个方法查看到源码可以看见是调用了一个instantiateReactComponent函数,这个函数 创建了一个ReactComponent 的实例并返回,也可以看到 ReactDOM.render 最后返回的也是这个实例。
    代码如下:

        function instantiateReactComponent(node) {
          var instance;
        
          if (node === null || node === false) {
            instance = new ReactEmptyComponent(instantiateReactComponent);----------(1)
          } else if (typeof node === 'object') {
            var element = node;
            '
              如果 type的类型是string 则说明它是 普通的HTML标签,那么直接按照普通的方式生成 DOM
              
              而如果不是string的话,比如是
              ƒ ()  ƒ App(props)之类的,则说明他们是 自定义的组件。
              则要另行处理。
            '
            
            if (typeof element.type === 'string') {
                '
                ReactNativeComponent.createInternalComponent 方法是被注入进来的,注入的是 ReactDOMComponent 类。
                最终的结构是 new ReactDOMComponent(element.type,element.props)
        
                生成一个 ReactDOMComponent 的实例返回
                '
                
              instance = ReactNativeComponent.createInternalComponent(element);--------(2)
            } else if (isInternalComponentType(element.type)) {
              instance = new element.type(element);---------------(3)
            } else {
        
                '
                是我们自定义的类的时候 执行该方法来进行生成 instance 操作。
                '
              instance = new ReactCompositeComponentWrapper();----------(4)
              
            }
          } else if (typeof node === 'string' || typeof node === 'number') {
            instance = ReactNativeComponent.createInstanceForText(node);----------(5)
          } else {
            !false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Encountered invalid React node of type %s', typeof node) : invariant(false) : undefined;
          }
        
         
          instance.construct(node);
        
         
          instance._mountIndex = 0;
          instance._mountImage = null;
        
          return instance;
        }
    

    instantiateReactComponent 方法是初始化组件的入口函数,它通过判断 node 的类型来创建不同的react对象。

    • 当 node 为空的时候,初始化组件。
    • 当 node 为对象,类型 type 字段标记为是字符串,初始化 DOM 标签。否则初始化自定义组件。
    • 当 node 为字符串或者数字时,初始化文本组件。

    创建了 Component 实例后,调用 component 的 mountComponent 方法,注意到这里是会被批量 mount 的,然后就可以进行渲染。

    参考文章:https://cloud.tencent.com/developer/article/1520009

    展开全文
  • 刚开始出现问题时很懵,不知道该怎么解决,原因就是对React原理理解的不够透彻,不知道问题出在哪。在解决问题的过程中,也逐渐深入了解了React的一些原理,这篇文章就来分享一下我对React一些原理的理解。 注意...

    前言

    随着项目开发的深入,不可避免了遇到了一些问题。刚开始出现问题时很懵,不知道该怎么解决,原因就是对React的原理理解的不够透彻,不知道问题出在哪。在解决问题的过程中,也逐渐深入了解了React的一些原理,这篇文章就来分享一下我对React一些原理的理解。

    注意 这篇文章并不是教程,只是我对React原理的一些个人理解,欢迎与我一起讨论。文章不对的地方,还请读者费心指出-

    概述

    本文是《使用React技术栈的一些收获》系列文章的第二篇(第一篇在这里,介绍如何开始构建React大型项目),简单介绍了React一些原理,包括React合成事件系统、组件的生命周期以及setState()

    React合成事件系统

    React快速的原因之一就是React很少直接操作DOM,浏览器事件也是一样。原因是太多的浏览器事件会占用很大内存。

    React为此自己实现了一套合成系统,在DOM事件体系基础上做了很大改进,减少了内存消耗,简化了事件逻辑,最大化解决浏览器兼容问题。

    其基本原理就是,所有在JSX声明的事件都会被委托在顶层document节点上,并根据事件名和组件名存储回调函数(listenerBank)。每次当某个组件触发事件时,在document节点上绑定的监听函数(dispatchEvent)就会找到这个组件和它的所有父组件(ancestors),对每个组件创建对应React合成事件(SyntheticEvent)并批处理(runEventQueueInBatch(events)),从而根据事件名和组件名调用(invokeGuardedCallback)回调函数。

    因此,如果你采用下面这种写法,并且这样的P标签有很多个:

    listView = list.map((item,index) => {
        return (
            <p onClick={this.handleClick} key={item.id}>{item.text}</p>
        )
    })
    

    That's OK,React帮你实现了事件委托。我之前因为不了解React合成事件系统,还显示的使用了事件委托,现在看来是多此一举的。

    由于React合成事件系统模拟事件冒泡的方法是构建一个自己及父组件队列,因此也带来一个问题,合成事件不能阻止原生事件,原生事件可以阻止合成事件。用 event.stopPropagation() 并不能停止事件传播,应该使用 event.preventDefault()

    如果你想详细了解React合成事件系统,移步http://blog.csdn.net/u013510838/article/details/61224760

    组件的生命周期(以父子组件为例)

    为了搞清楚组件生命周期,构造一个父组件包含子组件并且重写各生命周期函数的场景:

    class Child extends React.Component {
      constructor() {
        super()
        console.log('Child was created!')
      }
      componentWillMount(){
        console.log('Child componentWillMount!')
      }
      componentDidMount(){
        console.log('Child componentDidMount!')
      }
      componentWillReceiveProps(nextProps){
        console.log('Child componentWillReceiveProps:'+nextProps.data )
      }
      shouldComponentUpdate(nextProps, nextState){
        console.log('Child shouldComponentUpdate:'+ nextProps.data)
        return true
      }
      componentWillUpdate(nextProps, nextState){
        console.log('Child componentWillUpdate:'+ nextProps.data)
      }
      componentDidUpdate(){
        console.log('Child componentDidUpdate')
      }
      render() {
        console.log('render Child!')
        return (      
          <h1>Child recieve props: {this.props.data}</h1>      
        );
      }
    }
    
    class Father extends React.Component {
      // ... 前面跟子组件一样
      handleChangeState(){
        this.setState({randomData: Math.floor(Math.random()*50)})
      }
      render() {
        console.log('render Father!')
        return (
          <div>
            <Child data={this.state.randomData} />
            <h1>Father State: { this.state.randomData}</h1>      
            <button onClick={this.handleChangeState}>切换状态</button>
          </div>
        );
      }
    }
    
    React.render(
      <Father />,
      document.getElementById('root')
    );
    

    结果如下:
    刚开始

    Alt text


    调用父组件的setState后:

    Alt text


    在Jsbin上试试看
    有一张图能说明这之间的流程(图片来源):

    Alt text

     

    setState并不奇怪

    有一个能反映问题的场景:

    ...
    state = {
        count: 0
    }
    componentDidMount() {
      this.setState({count: this.state.count + 1})
      this.setState({count: this.state.count + 1})
      this.setState({count: this.state.count + 1})
    }
    ...
    

    看起来state.count被增加了三次,但结果是增加了一次。这并不奇怪:

    React快的原因之一就是,在执行this.setState()时,React没有忙着立即更新state,只是把新的state存到一个队列(batchUpdate)中。上面三次执行setState只是对传进去的对象进行了合并,然后再统一处理(批处理),触发重新渲染过程,因此只重新渲染一次,结果只增加了一次。这样做是非常明智的,因为在一个函数里调用多个setState是常见的,如果每一次调用setState都要引发重新渲染,显然不是最佳实践。React官方文档里也说了:

    Think of setState() as a request rather than an immediate command to update the component.

    setState() 看作是重新render的一次请求而不是立刻更新组件的指令。

    那么调用this.setState()后什么时候this.state才会更新?
    答案是即将要执行下一次的render函数时。

    这之间发生了什么?
    setState调用后,React会执行一个事务(Transaction),在这个事务中,React将新state放进一个队列中,当事务完成后,React就会刷新队列,然后启动另一个事务,这个事务包括执行 shouldComponentUpdate 方法来判断是否重新渲染,如果是,React就会进行state合并(state merge),生成新的state和props;如果不是,React仍然会更新this.state,只不过不会再render了。

    开发人员对setState感到奇怪的原因可能就是按照上述写法并不能产生预期效果,但幸运的是我们改动一下就可以实现上述累加效果:
    这归功于setState可以接受函数作为参数:

    setState(updater, [callback])

    ...
    state = {
        score: 0
    }
    componentDidMount() {
        this.setState( (prevState) => ({score : prevState.score + 1}) )
        this.setState( (prevState) => ({score : prevState.score + 1}) )
        this.setState( (prevState) => ({score : prevState.score + 1}) )
      }
    }
    

    这个updater可以为函数,该函数接受该组件前一刻的 state 以及当前的 props 作为参数,计算和返回下一刻的 state。

    你会发现达到增加三次的目的了: 在Jsbin上试试看

    这是因为React会把setState里传进去的函数放在一个任务队列里,React 会依次调用队列中的函数,传递给它们前一刻的 state。

    另外,不知道你在jsbin上的代码上注意到没有,调用setStateconsole.log(this.state.score)输出仍然为0,也就是this.state并未改变,并且只render了一次。

    总结

    学习一个框架或者工具,我觉得应该了解以下几点:

    1. 它是什么?能做什么?
    2. 它存在的理由是什么?解决了什么样的问题、满足了什么样的需求?
    3. 它的适用场景是什么?优缺点是什么?
    4. 它怎么用?最佳实践是什么?
    5. 它的原理是什么?
    6. ...

    通过对React一些原理的简单了解,就懂得了React为什么这么快速的原因之一,也会在问题出现时知道错在什么地方,知道合理的解决方案。



    作者:莫凡_Tcg
    链接:https://www.jianshu.com/p/12daf3731f9e
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    展开全文
  • 【第1392期】React渲染原理到性能优化(二)-- 更新渲染 黄琼前端早读课今天 前言 没去2018 React Conf的童鞋,别错误今天的。今日早读文章由腾讯IMWeb@黄琼授权分享。 @黄琼,腾讯前端工程师,IMWeb团队...

    https://mp.weixin.qq.com/s/aM-SkTsQrgruuf5wy3xVmQ   原文件地址

     

    【第1392期】React从渲染原理到性能优化(二)-- 更新渲染

    黄琼 前端早读课 今天

    前言

    没去2018 React Conf的童鞋,别错误今天的。今日早读文章由腾讯IMWeb@黄琼授权分享。

    @黄琼,腾讯前端工程师,IMWeb团队成员,目前负责企鹅辅导

    正文从这开始~~

    很多人都使用过React,但是很少人能说出它内部的渲染原理。有人会说,会用就行了,知道渲染原理有必要么?其实渲染原理决定着性能优化的方法,只有在了解原理之后,才能完全理解为什么这样做可以优化性能。正所谓:知其然,然后知其所以然。

    废话不多说,下面我们就开始吧~

    本篇文章,将会分为四部分介绍:

    JSX如何生成element

    当我们写下一段JSX代码的时候,react是如何根据我们的JSX代码来生成虚拟DOM的组成元素element的。

    element如何生成真实DOM节点

    再生成elment之后,react又如何将其转成浏览器的真实节点。这里会通过介绍首次渲染以及更新渲染的流程来帮助大家理解这个渲染流程,

    性能优化

    结合渲染原理,通过实际例子,看看如何优化组件。

    React 16异步渲染方案

    到目前为止,这些优化组件的方法还不能解决什么问题,所以我们需要引入异步渲染,以及异步渲染的原理是什么。

    二、element如何生成真实DOM节点

    【第1386期】React从渲染原理到性能优化(一)介绍了首次渲染的过程,接下来我们来看更新渲染的过程。

    触发组件的更新有两种更新方式:props以及state改变带来的更新。本次主要解析state改变带来的更新。整个过程流程图如下:

    1、一般改变state,都是从setState开始,这个函数被调用之后,会将我们传入的state放进pendingState的数组里存起来,然后判断当前流程是否处于批量更新,如果是,则将当前组件的instance放进dirtyComponent里,当这个更新流程中所有需要更新的组件收集完毕之后(这里面涉及到事务的概念,感兴趣的可以自己去了解一下)就会遍历dirtyComponent这个数组,调用他们的uptateComponent对组件进行更新。当然,如果当前不处于批量更新的状态,会直接去遍历dirtyComponent进行更新。

    2、在我们这个例子中,由于Example是自定义组件,所以调用的是ReactCompositeComponentWrapper这个类的updateComponent方法,这个方法做三件事。

    • 计算出nextState

    • render()得到nextRenderElement

    • 与prevElement 进行Diff 比较(这个过程后面会介绍),更新节点

    最后这个需要去更新节点的时候,跟首次渲染一样,也需要调用ReactDOMComponent的updateComponent来更新。其中第二步render得到的也是自定义组件的话, 会形成递归调用。

    接下来,还是上次的问题:那么更新过程中的生命周期函数,shouldComponentUpdate,componentWillUpdate跟componentDidUpdate在哪被调用呢?

    shouldComponentUpdate

    由图可知,shouldComponentUpdate在第一步调用得到nextState之后调用,因为nextState也是它的其中一个参数嘛~这个函数很重要,它是我们性能优化的一个很关键的点:由图可以看到,当shouldComponentUpdate返回false的时候,下面的一大块都不会被去执行,包括已经被优化的diff算法。

    当shouldComponentUpdate返回true的时候,会先调用componentWillUpdate,在整个更新过程结束之后调用componentDidUpdate。

    以上就是更新渲染的过程。

    下面我们重点再来介绍这个过程中的Diff算法。

    Diff算法

    React基于两个假设:

    • 两个相同的组件产生类似的DOM结构,不同组件产生不同DOM结构

    • 对于同一层次的一组子节点,它们可以通过唯一的id区分

    发明了一种叫Diff的算法来比较两棵DOM tree,它极大的优化了这个比较的过程,将算法复杂度从O(n^3)降低到O(n)。

    同时,基于第一点假设,我们可以推论出,Diff算法只会对同层的节点进行比较。如图,它只会对颜色相同的节点进行比较。

    也就是说如果父节点不同,React将不会在去对比子节点。因为不同的组件DOM结构会不相同,所以就没有必要在去对比子节点了。这也提高了对比的效率。

    下面,我们具体看下Diff算法是怎么做的,这里分为三种情况考虑

    • 节点类型不同

    • 节点类型相同

    • 子节点比较

    • 不同节点类型

    对于不同的节点类型,react会基于第一条假设,直接删去旧的节点,新建一个新的节点。

    比如:

    <A>
     <C/>
    </A>
    // 由shape1到shape2<B>
     <C/>
    </B>

    React会直接删掉A节点(包括它所有的子节点),然后新建一个B节点插入

    为了验证这一点,我打印出了从shape1到shape2节点的生命周期,链接如下:

    https://codesandbox.io/s/lyop4w9x9mlyop4w9x9m - CodeSandboxlyop4w9x9m - CodeSandbox

    最后终端输出的结果是:

    Shape1 :
    A is created
    A render C is created
    C render C componentDidMount A componentDidMountShape2 :
    A componentWillUnmount C componentWillUnmount B is created
    B render C is created
    C render C componentDidMount B componentDidMount

    由此可以看出,A与其子节点C会被直接删除,然后重新建一个B,C插入。这样就给我们的性能优化提供了一个思路,就是我们要保持DOM标签的稳定性。

    打个比方,如果写了一个<div><List /></div>(List 是一个有几千个节点的组件),切换的时候变成了<section><List /></section>,,此时即使List的内容不变,它也会先被卸载在创建,其实是很浪费的。

    相同节点类型

    当对比相同的节点类型比较简单,这里分为两种情况,一种是DOM元素类型,对应html直接支持的元素类型:div,span和p,还有一种是自定义组件。

    DOM元素类型

    react会对比它们的属性,只改变需要改变的属性

    比如:

    <div className="before" title="stuff" />
    <div className="after" title="stuff" />

    这两个div中,react会只更新className的值

    <div style={{color: 'red', fontWeight: 'bold'}} />
    <div style={{color: 'green', fontWeight: 'bold'}} />

    这两个div中,react只会去更新color的值

    自定义组件类型

    由于React此时并不知道如何去更新DOM树,因为这些逻辑都在React组件里面,所以它能做的就是根据新节点的props去更新原来根节点的组件实例,触发一个更新的过程,最后在对所有的child节点在进行diff的递归比较更新。

    - shouldComponentUpdate
    - componentWillReceiveProps
    - componentWillUpdate
    - render
    - componentDidUpdate
    子节点比较
    <div>
     <A />
     <B />
    </div>
    // 列表一到列表二<div>
     <A />
     <C />
     <B />
    </div>

    因为React在没有key的情况下对比节点的时候,是一个一个按着顺序对比的。从列表一到列表二,只是在中间插入了一个C,但是如果没有key的时候,react会把B删去,新建一个C放在B的位置,然后重新建一个节点B放在尾部。

    你说什么就是什么咯?!不信的话,我们还是跑一边代码,看看生命周期验证一下,连接地址为:lpl52wy9vl - CodeSandbox

    列表一:
    A is created
    A render B is created
    B render A componentDidMount B componentDidMount列表二:
    A render B componentWillUnmount C is created
    C render B is created
    B render A componentDidUpdate C componentDidMount B componentDidMount

    当节点很多的时候,这样做是非常低效的。有两种方法可以解决这个问题:

    1、保持DOM结构的稳定性,我们来看这个变化,由两个子节点变成了三个,其实是一个不稳定的DOM结构,我们可以通过通过加一个null,保持DOM结构的稳定。这样按照顺序对比的时候,B就不会被卸载又重建回来。

    <div>
     <A />
     {null}  <B />
    </div>
    // 列表一到列表二<div>
     <A />
     <C />
     <B />
    </div>

    2、key

    通过给节点配置key,让React可以识别节点是否存在。

    配上key之后,在跑一遍代码看看。

    A render
    C is created
    C render B render A componentDidUpdate C componentDidMount B componentDidUpdate

    果然,配上key之后,列表二的生命周期就如我所愿,只在指定的位置创建C节点插入。

    这里要注意的一点是,key值必须是稳定(所以我们不能用Math.random()去创建key),可预测,并且唯一的。

    这里给我们性能优化也提供了两个非常重要的依据:

    • 保持DOM结构的稳定性

    • map的时候,加key

    参考文档:

    https://facebook.github.io/react/docs/reconciliation.htm

    关于本文

    作者:@黄琼
    原文:
    https://zhuanlan.zhihu.com/p/43566956

    转载于:https://www.cnblogs.com/chaoyuehedy/p/9638848.html

    展开全文
  • 前言以下,是我在2018 React Conf的分享内容,希望大家有所帮助。可以先在官网下载我的ppt...其实渲染原理决定着性能优化的方法,只有在了解原理之后,才能完全理解为什么这样做可以优化性能。正所谓:知其然,然...
  • 如今的前端,框架横行,出去面试问到框架是常有的事。我比较常用React, 这里就写了一篇 React 基础原理的内容, 面试基本上也就问这些, 分享给大家。React ...
  • 点击上方蓝字关注程序员成长指北,还可加入「技术交流群」共同进步如今的前端,框架横行,不掌握点框架的知识,出去面试都虚。我比较常用React, 这里就写了一篇 React...
  • 如今的前端,框架横行,出去面试问到框架是常有的事。我比较常用React, 这里就写了一篇 React 基础原理的内容, 面试基本上也就问这些, 分享给大家。React 是什么React...
  • 前言React Native 是最近非常火的一个话题,因为它的语法简介,跨平台等特性,赢得了各大平台的青睐,虽然前期是有一些坑。基本概念解释React 是一套可以用简洁的语法高效绘制 DOM 的框架,所谓的“高效”,是因为 ...
  • 我比较常用React, 这里就写了一篇 React 基础原理的内容, 面试基本上也就问这些, 分享给大家。React 是什么React是一个专注于构建用户界面的 Javascript Library.React做了什么?Virtual Dom模型生命周期管理...
  • 3. react渲染原理 大家都知道浏览器的重绘和回流都是比较昂贵的操作,如果每一次改变都直接DOM进行操作,这会带来性能问题,而批量操作只会触发一次DOM更新。 在React中,状态变化后React框架并不会立即去计算并...
  • React渲染原理到性能优化

    千次阅读 2018-08-19 17:20:57
    其实渲染原理决定着性能优化的方法,只有在了解原理之后,才能完全理解为什么这样做可以优化性能。正所谓:知其然,然后知其所以然。 本篇文章,将会分为四部分介绍: 一、JSX 如何生成 element 当我们写下一段...
  • react-router服务端渲染原理初探

    千次阅读 2017-04-22 11:26:05
    react-router服务端渲染原理初探
  • 我比较常用React, 这里就写了一篇 React 基础原理的内容, 面试基本上也就问这些, 分享给大家。React 是什么React是一个专注于构建用户界面的 Javascript Library.一、React做了什么?Virtual Dom模型生命周期管理...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,803
精华内容 4,321
关键字:

对react渲染原理的理解