资讯专栏INFORMATION COLUMN

React源码解析

Freeman / 2513人阅读

摘要:生命周期当首次挂载组件时,按顺序执行和。由于是通过构造函数进行管理的,所以也是整个生命周期中先开始执行的加载组件若存在,则执行。一组及方法称为一个。因此,允许用户通过来判断该组件是否需要进行算法分析。

生命周期

 当首次挂载组件时,按顺序执行 getDefaultProps、getInitialState、componentWillMount、 render 和 componentDidMount。
 当卸载组件时,执行 componentWillUnmount。
 当重新挂载组件时,此时按顺序执行 getInitialState、componentWillMount、render 和 componentDidMount,但并不执行 getDefaultProps。
 当再次渲染组件时,组件接受到更新状态,此时按顺序执行 componentWillReceiveProps、 shouldComponentUpdate、componentWillUpdate、render 和 componentDidUpdate

创建自定义组件

class MyComponent extends React.Component 其实就 是调用内部方法 createClass 创建组件
调用getDefaultProps。
由于 getDefaultProps 是通过构造函数进行管理的,所以也是整个生命周期中先开始执行的

MOUNTING(加载组件)

若存在 componentWillMount,则执行。如果此时在 componentWillMount 中调用 setState 方法,不会触发re-render,会进行state合并,并且this.state不是最新的。

RECEIVE_PROPS(更新组件)

如果此时在 componentWillReceiveProps 中调 用 setState,是不会触发 re-render的,而是会进行 state 合并。
updateComponent 本质上也是通过递归渲染内容的,由于递归的特性,父组件的 component- WillUpdate 是在其子组件的 componentWillUpdate 之前调用的,而父组件的 componentDidUpdate 也是在其子组件的 componentDidUpdate 之后调用的
禁止在 shouldComponentUpdate 和 componentWillUpdate 中调用 setState,这会造成循环调用,直至耗光浏览器内存后崩溃

UNMOUNTING(卸载组件)

如果存在 componentWillUnmount,则执行并重置所有相关参数、更新队列以及更新状态,如 果此时在 componentWillUnmount 中调用 setState,是不会触发 re-render 的,这是因为所有更新 队列和更新状态都被重置为 null

无状态组件
只有一个render方法

setState

调用setState -> 新state进入队列 -> 合并更新队列 -> 判断是否在批量更新 -> 如果在,component放入dirtyComponent等待下一次更新;如果不在,进行批量更新

 ReactComponent.prototype.setState = function(partialState, callback) {
  this.updater.enqueueSetState(this, partialState); //更新state
  if (callback) {//回调函数
    this.updater.enqueueCallback(this, callback, "setState");
  }
 };

enqueueSetState中,合并更新队列,调用enqueueUpdate

 function enqueueUpdate(component) {
  // 不处于批量更新模式,进行更新
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component); //批处理更新
    return;
  }
  // 处于批量更新模式
  dirtyComponents.push(component);
 }

事务(Transaction)
事务就是将需要执行的方法使用 wrapper 封装起来,再通过事务提供的 perform 方法执行。 而在 perform 之前,先执行所有 wrapper 中的 initialize 方法,执行完 perform 之后(即执行 method 方法后)再执行所有的 close 方法。一组 initialize 及 close 方法称为一个 wrapper。
而要使用事务的模 块,除了需要把 mixin 混入自己的事务实现中外,还要额外实现一个抽象的 getTransactionWrappers 接口。这个接口用来获取所有需要封装的前置方法(initialize)和收尾方法(close), 因此它需要返回一个数组的对象,每个对象分别有 key 为 initialize 和 close 的方法

perform (func, scope,a,b,c,d,e,f){
  this.initializeAll(0);
  method.call(scope, a, b, c, d, e, f);
  this.closeAll(0);
}

Diff算法

Diff算法本质上是对javascript对象之间的比较,只有在React更新阶段(调用了setState)才会有Diff算法的运用。
流程:

this.setState(partialState) 更新state

this.replaceState(completeState) 合并state

this._receivePropsAndState(this.props,nextState,transaction)收到新的props和state,决定是否更新组件

_receivePropsAndState: function(nextProps, nextState, transaction)

if (!this.shouldComponentUpdate ||  //没有定义shouldComponentUpdate函数
this.shouldComponentUpdate(nextProps, nextState)) {  //shouldComponentUpdate函数返回true
  this._performComponentUpdate(nextProps, nextState, transaction);
} else {  
//shouldComponentUpdate函数返回了false,不进行DOM更新,只更新props和state的值
  this.props = nextProps;
  this.state = nextState;
}

4.this._performComponentUpdate(nextProps, nextState, transaction);
调用this.componentWillUpdate
this.updateComponent(transaction);
调用this.componentDidUpdate

5.this.updateComponent(transaction)

updateComponent: function(transaction) {
var currentComponent = this._renderedComponent;  //原组件
var nextComponent = this._renderValidatedComponent();  //新组件
//两个组件的类相同(构造函数一样)
if (currentComponent.constructor === nextComponent.constructor) {
  if (!nextComponent.props.isStatic) {
    currentComponent.receiveProps(nextComponent.props, transaction); //更新组件
  }
} else {
  // 两个组件的类不一样,直接替换
  var thisID = this._rootNodeID;
  var currentComponentID = currentComponent._rootNodeID;
  //卸载原组件
  currentComponent.unmountComponent();
  //加载新组件
  var nextMarkup = nextComponent.mountComponent(thisID, transaction);
  ReactComponent.DOMIDOperations.dangerouslyReplaceNodeWithMarkupByID(
    currentComponentID,
    nextMarkup
  );
  this._renderedComponent = nextComponent;
}

}

currentComponent.receiveProps(nextComponent.props, transaction)

有三种类型的component:
①文本 ReactTextComponent

receiveProps: function(nextProps, transaction) {
//text不一样直接替换
if (nextProps.text !== this.props.text) {
  this.props.text = nextProps.text;
  ReactComponent.DOMIDOperations.updateTextContentByID(
    this._rootNodeID,
    nextProps.text
  );
}
}

②React自定义组件
调用componentWillReceiveProps
再次调用this._receivePropsAndState(nextProps, nextState, transaction);

tree diff
比较两棵DOM树,如果某个节点不存在,则该节点及其子节点会被完全删除,不会进一步比较。 React只会简单地考虑同层级节点的位置变换
component diff
如果是同一类型组件,继续比较
如果不是,替换整个组件
对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切知道这点,那 么就可以节省大量的 diff 运算时间。因此,React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff 算法分析。
element diff
1.比较新旧集合元素的key,如果有相同key,说明旧集合中有新集合的元素。
2.如果该元素在旧集合的index < lastIndex (lastindex指的是访问过的元素在旧集合中最大的index),移动该元素到nextIndex,否则不移动。
3.如果新集合里的元素在旧集合不存在,创建新元素到当前index。
4.更新lastIndex, nextIndex++

存在的缺陷
《深入React技术栈》作者观点是:
如果旧集合是A,B,C,D, 新集合是D,A,B,C
D不会移动,而ABC都要依次移动。
实际上D只要移动到C后面
本人认为:A,B,C的index都已经发生变化,所以肯定会有移动操作,避免不了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98068.html

相关文章

  • react解析: render的FiberRoot(三)

    摘要:查看创建核心函数源码行调用函数创建是相关,不用管源码行这个指的是调用创建,下面我们将会说到对象源码行源码行函数中,首先创建了一个,然后又创建了一个,它们两者还是相互引用。 感谢 yck: 剖析 React 源码解析,本篇文章是在读完他的文章的基础上,将他的文章进行拆解和加工,加入我自己的一下理解和例子,便于大家理解。觉得yck写的真的很棒 。React 版本为 16.8.6,关于源码的...

    muddyway 评论0 收藏0
  • React源码解析ReactDOM.render()

    摘要:一更新的方式有三种渲染接下来,我们就来看下源码二作用在提供的里渲染一个元素,并返回对该组件的引用常见的用法是这个官网网址源码服务端使用方法渲染节点是让服务端尽可能复用节点,提高性能元素容器应用渲染结束后,调用的函数错误抓取方法本质是返回 showImg(https://segmentfault.com/img/remote/1460000020064414?w=1240&h=641);...

    iKcamp 评论0 收藏0
  • React源码解析React.createRef()/forwardRef()

    摘要:一作用获取目标的实例使用源码可修改的不可变的对象没见过这种写法初始化对象,属性初始值为解析源码比较简单,就是返回了带有属性的二作用从父组件中获取子组件是的实例使用是没有实例的,因为它是,所以没有,所以不能通过来拿到实例将的传给子组件,并绑定 showImg(https://segmentfault.com/img/remote/1460000019877636); 一、React.cr...

    aisuhua 评论0 收藏0
  • Luy 1.0 :一个React-like轮子的诞生

    摘要:司徒正美的一款了不起的化方案,支持到。行代码内实现一个胡子大哈实现的作品其实就是的了源码学习个人文章源码学习个人文章源码学习个人文章源码学习个人文章这几片文章的作者都是司徒正美,全面的解析和官方的对比。 前言 在过去的一个多月中,为了能够更深入的学习,使用React,了解React内部算法,数据结构,我自己,从零开始写了一个玩具框架。 截止今日,终于可以发布第一个版本,因为就在昨天,我...

    codecook 评论0 收藏0
  • React源码解析React.children.map()

    摘要:一例子看到一个有趣的现象,就是多层嵌套的数组经过后,平铺成了,接下来以该例解析二作用源码进行基本的判断和初始化后,调用该方法就是重命名了,即解析注意,该数组在里面滚了一圈后,会结果三作用的包裹器源码第一次第二次如果字符串中有连续多个的话 showImg(https://segmentfault.com/img/remote/1460000019968077?w=1240&h=698);...

    kuangcaibao 评论0 收藏0

发表评论

0条评论

Freeman

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<