# React原理

  1. 可以更深入理解react的内部实现原理
  2. 加深对react的理解
  3. 更高效的基于react开发业务代码
  4. 本文主要针对react核心知识点做一个总结
  5. 面试

1. 虚拟DOM(virtual document)

像reactjs、vuejs都是基于vdom进行二次开发框架(二者只是虚拟dom的定义属性不同)。那么为什么不约而同都采用这样一种方案?

  • 浏览器在进行页面加载的时候,除了需要对dom树,cssom树进行解析,最后渲染,对于dom树节点少的来说,增删改查,整个html结构的变化,所带来的性能消耗可以忽略不计,然而现代网站这种现象基本不存在,因此,构建一个中大型网站来说,频繁的遍历,更新,操作dom,性能开销会很大的,而采用虚拟dom(js对象)来映射真实dom,将是更高效的方案
  • 操作dom,需要获取当前节点,这中间可能会涉及样式、文档内容等变化、甚至增加或者消除,代码量比较大而且难于维护,虚拟dom带来的优势可以通过状态来控制视图,通过状态值的改变反映在页面上,对于开发维护来说更高效
  • 虚拟dom可以组件化的形式进行二次封装开发,复用性更好。

例子

image.png

image.png

2. 模板编译

reactjs拥有自己的模板编译语法即通过jsx语法通过render函数返回vdom,这里也比较下vue编译模板<template></template>

下面是babel转译后的js对象构成

babel编译

3. 虚拟dom渲染

  • vdom对象有了就需要对这些对象进行更深入操作,reactjs采用深度优先遍历的法则
  • 根据这些vdom对象不同的type类型,针对创建不同的节点
switch (vdom.tag) {
  case HostComponent:
    // 创建或更新 dom
  case HostText:
    // 创建或更新 dom
  case FunctionComponent: 
    // 创建或更新 dom
  case ClassComponent: 
    // 创建或更新 dom
}

1
2
3
4
5
6
7
8
9
10
11
  • 组件化创建虚拟dom
switch (vdom.tag) {
  case FunctionComponent: 
       const childVdom = vdom.type(props);
       
       render(childVdom);
       //...
  case ClassComponent: 
     const instance = new vdom.type(props);
     const childVdom = instance.render();
     
     render(childVdom);
     //...
} 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • react内部执行diff算法,最优化更新虚拟节点的创建

image.png

4. 视图状态机

react 是通过 setState 的 api 触发状态更新的,更新以后就重新渲染整个 vdom。 而 vue 是通过对状态做代理,get 的时候收集以来,然后修改状态的时候就可以触发对应组件的 render 了。

举个例子

从触发setState节点开始,先往上找到root最顶层根元素,然后往下根据已存在属性拷贝一份新的fiber,直到触发setState节点,再往下遍历调用子元素render,中间可以根据shouldComponentUpdate等方法跳过

多了一个往上找直到root开始遍历(因为是fiber链表结构没有层级概念,依然用树的那一套就会导致渲染重复),并且父、祖父元素都拷贝一份新fiber的步骤

React自从16.8开始使用了 fiber 架构

fiber使用了链表结构串联来虚拟dom树,主要的三个参数:child(子)、sibling(下一个兄弟)、return(父)

fiber1.png

fiber遍历过程就是找第一个元素一直找到底,然后找兄弟,没兄弟了往上

这个阶段分为两块,往下的过程是一些调用render或者克隆一个fiber节点的操作,往上的过程是生成effectupdateQueue更新内容的操作

假如在Text2内触发setState

fiber2.png

Text2标记更新字段lanes: 1,并一直往上找,找到root,并给沿途所以父节点设置childLanes: 0,然后从root根节点开始遍历

  • 当遍历到 lanes: 0 的时候:

    • childLanes: 0 表示子孙元素没有变动直接跳过,也等于跳过了diff
    • childLanes: 1 就只是从之前的fiber克隆一个新的fiber节点
  • 当遍历到 lanes: 1的时候

    • 如果shouldComponentUpdate或类似的操作不更新,则走到上面lanes: 0的流程
    • 调用render生成新的fiber

当到达最底部没有子元素的时候,开始compile生成updateQueue节点然后重复上面步骤(叶子节点为没有子元素的节点)叶子节点->兄弟->父->子->叶子,最终回到root结束

fiber3.png

然后commit阶段,这时候有个Effect链表(Effect链表只有变动的节点),遍历Effect拿到节点的updateQueue更新了哪些内容,将updateQueue渲染到dom上

5. fiber架构

  1. Fiber是react团队定义的一种数据结构(JS对象),一个dom节点或者组件对应一个fiber对象
  2. Fiber节点之间构成 单向链表 结构, 以实现前文提到的几个特性: 更新可暂停/恢复、可跳过、可设优先级.

见上图示例

6. 总结

  1. react 和 vue 都是基于 vdom 的前端框架,之所以用 vdom 是因为可以精准的对比关心的属性,而且还可以跨平台渲染。
  2. 学习react构建 (opens new window)
最后更新时间: 5/16/2023, 6:37:44 PM