问题:

  • 组件生命周期是何时绑定,如何被调用
  • 合成事件流程
    • 代码实现
    • 响应原生事件再到内部合成事件的流程?
  • immutable 的使用
  • redux 中 immutable 的使用
  • 每次 diff 是从根节点开始检查? 如果不是,那么是如何基于当前节点进行 diff 更新?
  • reselect ???
  • transactions 事务 指代什么???定义的周期是如何被调用的,
  • 源码内只使用了部分 ES6+ 的内容
  • 合成事件如何处理捕获阶段?
    • 文档都看的不仔细,我自己也是该啊

Base: 16.3

如何阅读源码呢??

  • Babel 片段代码编译
  • Create-react-app 快速创建应用(使用 Chrome 断点调试)
  • React 源码项目

参考:

codebase

https://reactjs.org/docs/codebase-overview.html

image.png-17.5kB

  • React Core
    • React.createElement()
    • React.Component
    • React.Children
    • React core only includes the APIs necessary to define components.
  • Renderers
    • Renderers manage how a React tree turns into the underlying platform calls.
    • React DOM Renderer
    • React Native Renderer
    • React Test Renderer
  • Reconcilers
    • declarative rendering, custom components, state, lifecycle methods, and refs work 多个 renderer 共享这部分的逻辑。
    • reconciliation algorithm
    • Stack Reconciler: React15 及更早的版本都使用 Stack
    • Fiber Reconciler: 这个自然是 React16 使用的新内核了。

Fiber

React Fiber架构是React核心算法的发展的再实现,是React团队两年来的研究高潮。

React Fiber目标是提高动画,布局和手势等领域的适应性,它的重要特征是增量渲染,能够将渲染工作划分为一块块,然后在多个帧frame中传播。

另外一个关键能力是在当有新的更新时,提供暂停,退出,或重用等能力,分配优先权给更新的不同类型和新的并发原语。

React Fiber在更新和渲染之间提供了调停reconciliation功能,原来React一旦有更新,就立即进行渲染,非常刚性,反而造成了更新和渲染之间的不同步,而调停reconciliation功能能异步进行渲染,或进行计划调用更新,保证动画和手势等对响应要求很快的应用有快速的流畅性。

关键点:
1.在UI界面中,不是所有更新需要立即渲染的,如果这么做会很浪费,引起帧frame丢失和降低用户体验。
2.不同类型的更新应该有不同优先级,一个动画更新应该比其他比如数据存储更新更快,更有优先权。
3.基于推的方式需要应用决定如何调用工作,而基于拉的方式则能够让react框架更加智能,将更多决定权放给你。这样一旦有更新,不再像以前采取推的方式,而是采取拉的方式。

Fiber 是新的 Reconciliation 算法。

Reconciliation 算法泛指 virtual DOM

image.png-171.4kB

fiber: 数据结构。带有与之相关的节点信息。Fiber 名字由此而来。
fiber架构首先在 fiber 类型中开了一个口子,支持N多种Fiber,原React只有4种虚拟DOM,现在多达14种

image.png-49.1kB

节点更新: 分为两个阶段。 stack 版本是由更新就会同步执行并且更新到 DOM。而 Fiber 的机制变了,分为两个阶段:

image.png-116.1kB

  • Phases 1
    • build fiber tree
    • 计算前后 Fiber tree 的差异
    • 这个阶段可中断,主线程就可以执行 Layout 等操作
    • 这个阶段完成后,产出 effect list: 实际 DOM 操作会影响到的 fiber 列表
    • 在这整个阶段有一个 schedule 的机制,详情见下面。
    • update 的阶段: fiber 可以重用: 到达节省内存,避免 GC 的效果
  • 进入 pending-commit 阶段
  • Phases 2
    • commit: 遍历 Phases 1 中的 effect list, 将改变应用到实际的 DOM 中
    • 此阶段不可中断

通过 schedule 机制: requestIdleCallBack 查询主线程是否有空余时间。有的话,回到 React 主线程,继续构建 work-in-process tree。因为可能整个 tree 很大,要避免主进程(Layout 等)阻塞。
这整个阶段又分为: unit of work (任务分块,用来实现 schedule)。

image.png-199.7kB

  • 调度
  • prioority 优先级
  • unit of work 任务分块
  • requestIdleCallBck

work loop:

image.png-130.3kB

每一次 updata 都会有优先级 priorities:

image.png-183.6kB

lifecycle:

image.png-139.3kB

上面比较大的点:

  • stream: 目前 React 加载整个文件,构建整个树; Fiber 可以最小化加载组件,实现 streaming render
  • parallel: tree 的处理模式

Diff

React Components, Elements, and Instances

理解 React 中 Element 的含义:

Element: An element is a plain object describing a component instance or DOM node and its desired properties. An element describing a component is also an element, just like an element describing the DOM node. They can be nested and mixed with each other.

Element 类型可以是 DOM 也可以是 React Component(自定义的组件): 最后都会遍历直到底层的 HTML 便签。

组件声明的两种方式:

  • functions component
  • classes component
    • Component
    • PureCompionent

instances: 只有基于 class 声明的组件才有 instace, 而且不用手动创建 instance, React 内部会自动创建实例。

Reconciliation

  • Reconciliation

  • algorithm: React 实现 O(n) 的复杂度的算法用来变换一颗树。实现基于两个假设:

    • 不同类型的两个 React Element 生产不同树。
    • 开发这通过 key 指出哪些 child Elements 会是稳定在不同的渲染下。
  • 算法实现结果:

    • Elements Of Different Types
      • 不管是 DOM Element 还是 React Component
      • root elements 根元素类型一旦改变,就会重建整棵树。包括子元素也会重建,会触发 componentWillUnmount 方法。
    • DOM Elements Of The Same Type
      • 比较同类型的 DOM Element 时,只会更新变化的属性
    • Component Elements Of The Same Type
      • 组件正常的运行时的声明周期方法
    • Recursing On Children
      • keys

Fiber 节点类型

  • ClassComponent
    就是应用层面的React组件。ClassComponent是一个继承自React.Component的类的实例。
  • HostRoot
    ReactDOM.render()时的根节点。
  • HostComponent
    React中最常见的抽象节点,是ClassComponent的组成部分。具体的实现取决于React运行的平台。在浏览器环境下就代表DOM节点,可以理解为所谓的虚拟DOM节点。HostComponent中的Host就代码这种组件的具体操作逻辑是由Host环境注入的。
  • text portal...

Basic Theoretical Concepts

Event

React 合成事件系统, 这个需要知道的非常清楚。

生命周期

setState

16.3 更新总结

简述:

  • 组件支持返回数组
  • React.ceatePortal
  • componentDidCatch,错误边界,一个新生命周期钩子,其他钩子,render, ref,组件contructor出错,它就会唤起。它会在componentDidMount/Update后执行。
  • <Fragment/>
  • 生命周期的大变动
  • new <React.StrictMode> wrapper to help prepare apps for async rendering。在严格模式下,声明被废弃的compoentWillXxx就不会被调用。
  • new Context API
    • 为什么需要? 与旧的相比较
    • Redux 的作用呢?
    • Redux 与 内部状态管理?
  • 推出createRef与forwardRef
    • 从组件中解耦出来
  • createRoot、、flushSync、 batchedUpdates、 deferredUpdates等涉及异步操作API依然在隐藏不露,这是改变React生态的核武。。。。

组件声明与挂载

先写一个简单的组件:

class Simple extends Component {
  render() {
    return (
      <h1>Simple test</h1>
    )
  }
}

console.log(<Simple />)  // 打印组件

在 chrome performance 中查看整个初始化过程:
image.png-45.4kB

接下来就 查看 React.js 源码,我们会看到所有暴露在 React 上的顶层 API。

![TIM截图20180330175940.png-82.9kB][32]

继续看 Component 的定义:

function Component(props, context, updater) {   
 ...
}

Component.prototype.isReactComponent = {};
Component.prototype.setState = function(partialState, callback) { ... }
Component.prototype.forceUpdate = function(callback) { ... };

我们使用 React 使用 JSX,Babel 会帮我们转换: 那么看下 Babel 转完后的:

var Simple = (function(_React$Component2) {

  // 继承 Component 的属性和方法
  _inherits(Simple, _React$Component2);

  function Simple() {
    _classCallCheck(this, Simple);

    return _possibleConstructorReturn(
      this,
      (Simple.__proto__ || Object.getPrototypeOf(Simple)).apply(this, arguments)
    );
  }

  _createClass(Simple, [
    {
      key: "render",
      value: function render() {
        return _react2.default.createElement("h1", null, "Simple text");
      }
    }
  ]);

  return Simple;
})(_react2.default.Component);


// 直接打印的是 createElement 返回的对象: 包括当前节点的所有信息。 即: `render` 方法
console.log(_react2.default.createElement(Simple, null));

// IIFE : 实际返回 构造函数。
exports.default = Simple;

实际打印的信息:
image.png-34.9kB

对应到源码文件: ReactElement.js, 这个是创建 React 组件的核心部分:

function createElement(type, config, children) { ... }

回到调用栈上看: HostRoot ClassComponent HostComponent 遍历生成 React Node Tree。

  • HostRoot 对应 根节点
  • ClassComponent 对应 Simple
  • HostComponent 对应 <h1 />

在 React Element 创建完后: 初始化属性并验证属性: 包括设置这里得 text 再然后绑定事件。

组件的挂载

ReactDOM.render(component,mountNode)

找到对饮文件: client/ReactDOM.js

顶层 ReactDOM 包含的属性和方法: (不在这一一列出)
image.png-87.4kB

可以看到 Render 方法是调用的: legacyRenderSubtreeIntoContainer

初始化挂载会创建一个内部的 Root 对象: 通过 legacyCreateRootFromDOMContainer 方法返回 new ReactRoot() 实例对象, 再调用原型上的 ReactRoot.prototype.render 方法。

image.png-99.6kB

再看 render 方法里: DOMRendererReactFiberReconciler, 到这里终于接触到我们的 react-reconciler 内核了。

代码流程:

const DOMRenderer = ReactFiberReconciler({ ... })

legacyRenderSubtreeIntoContainer => legacyCreateRootFromDOMContainer =》 ReactRoot =》 DOMRenderer =》 DOMRenderer.updateContainer => DOMRenderer.unbatchedUpdates

通过 ReactTypes.js:查看到所有的 React Element 节点类型:

image.png-74.9kB

6]

react-reconciler:

  • ReactFiberClassComponent.js 处理生命周期

其实在ES5中通过React.createClass({})方法创建的组件,与ES6中是完全一样的,同样可以通过控制台打印输出组件结果进行验证,此处不再赘述。

区别: 写法不同(这个自然是了), this 的指向: ES6 需要手动绑定。

performance

当我们谈论性能时,需要明白 React 自身带来的性能好处,也需要知道实际业务的性能优化。

  • 先通过 React blog 简单了解下性能优化方面
    • 基于工具的打包优化
    • 基于 chrome 的性能分析
    • 长列表优化
    • Avoid Reconciliation: 避免每次都跑一次 render, 再 diff 判断更新
      • shouldComponentUpdate
      • React.PureComponent
      • 更新机制: shouldComponentUpdate 默认 true , 会执行 render 方法,然后比较 vDOM(比较当前同级节点)进行更新; 如果 shouldComponentUpdate 返回 false,则不会执行 render, 也不会比较 vDOM
    • immutable data
      • 需要理解不可变数据
      • 为什么需要, 解决了什么问题?
    • 代码编写
      • 内联函数

Points

浅比较

http://imweb.io/topic/598973c2c72aa8db35d2e291