文档详情

react diff 算法实现思路及原理解析.docx

发布:2025-06-14约5.01千字共8页下载文档
文本预览下载声明

react?diff?算法实现思路及原理解析

目录事例分析diff特点diff思路实现diff算法修改入口文件实现React.Fragment我们需要修改children对比前面几节我们学习了解了react的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带--diff算法,了解如何优化和复用dom操作的,还有我们常见的key的作用。

diff算法使用在子都是数组的情况下,这点和vue是一样的。如果元素是其他类型的话直接替换就好。

事例分析

按照之前的diff写法,如果元素不同我们是直接删了a再插入的:

按照上面图的结构,我们需要知道那个元素变化了,其实右边相对左边只是把A做了移动,没有dom元素的删除和新增。

diff特点

同级对比On类型不一样销毁老的,创建新的通过key标识

key这里需要标识,主要是为了列表中有删除新增时有优化效果,如果纯静态列表,只是展示作用,key意义不大。

diff思路

使用map存储节点状态,格式如下:

letmap={

keyA:ADOM,

keyB:BDOM

}

定义lastPlacedIndex记录上一个不需要移动的老节点

默认lastPlacedIndex=0,上一个不需要移动的节点,在循环新的子虚拟dom时,如果老节点的挂载索引小于当前值,则改变lastPlacedIndex。这里有点类似vue的最长递增子序列,最大的保证不变的dom元素,只是判断方式不同。

循环新数组

先出A,map中如果有A,表示可以复用

判断A的老挂载索引和lastPlacedIndex对比,如果索引值大,A节点不需要移动,更新lastPlacedIndex的值;否则循环到B,挂载索引小,需要移动B;循环到G,map中没有值,需要新增;新的数组节点循环完,未用到的老节点全部删除。

实现diff算法

修改入口文件

//src/index.js

classCounterextendsReact.Component{

constructor(props){

super(props)

this.state={list:[A,B,C,D,E,F]}

handleClick=()={

this.setState({

list:[A,C,E,B,G]

render(){

//使用空标签

returnReact.Fragment

{this.state.list.map(item={

//这里使用key标识

returnlikey={item}{item}/li

/ul

buttonthis.handleClick}add1/button

/React.Fragment

}

实现React.Fragment

Fragment就是代码片段,不占用dom结构。简写/,对应dom操作为createDocumentFragment。

是用原生库打印,看结构

可以发现就是一个简单的Symbol,所以需要定义新的类型:

为什么一个简单的Symbol可以被渲染成片段呢?依赖于babel解析。

//src/constants.js

exportconstREACT_FRAGMENT=Symbol(react.fragment)//React.Fragment标签

//备用,diff时做patch的type定义

//新的插入

exportconstPLACEMENT=PLACEMENT

//复用的移动

exportconstMOVE=MOVE

在创建元素的时候进行类型判断,记得react.js中导出

//src/react-dom.js

//createDOM方法

elseif(type===REACT_FRAGMENT){

//fragment片段

dom=document.createDocumentFragment()

//updateElement方法

elseif(oldVdom.type===REACT_FRAGMENT){

//fragment不需要对比,直接对比子就可以了

constcur

显示全部
相似文档