React源码解析18(10)------ 实现多节点的Diff算法

这篇具有很好参考价值的文章主要介绍了React源码解析18(10)------ 实现多节点的Diff算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

摘要

在上一篇中,实现了多节点的渲染。但是之前写得diff算法,只能适用于单节点的情况,例如这种情况:

<div>
	<p>
 		<span></span>
	</p>
</div>

如果对于多节点的情况:

<ul>
	<li></li>
	<li></li>
	<li></li>
</ul>

之前实现的diff算法就不会有效果了,所以在这一篇中,我们主要实现针对于多节点的diff算法。

实现之前,我们先将index.js修改一下:

function App() {
  const [num, setNum] = useState(100)
  const click1 = () => {
    console.log(num);
    setNum(num + 1)
  }
  return num % 2 > 0 ? jsx("ul", {
    onClick: click1,
    key: 'ul',
    children: [jsx("li", {
      children: "1",
      key: "1"
    }), jsx("li", {
      children: "2",
      key: "2"
    }), jsx("li", {
      children: "3",
      key: "3"
    })]
  }): jsx("ul", {
    onClick: click1,
    key: 'ul',
    children: [jsx("li", {
      children: "2",
      key: "2"
    }), jsx("li", {
      children: "1",
      key: "1"
    }), jsx("li", {
      children: "3",
      key: "3"
    })]
  });
}

ReactDOM.createRoot(root).render(<App />)

1.修改beginWork流程

在reconcileChildren方法里面,我们判断了如果element为数组的情况,就是多节点。所以我们需要在这里进行diff算法的处理。

function reconcileChildren(parent,element) {
  //其他代码。。。。
  }else if(Array.isArray(element) && element.length > 0) {
    const newChild = diffReconcileManyChildren(parent, element);
    if(newChild) {
      return newChild
    }
  //其他代码。。。。

所以我们的diff算法那主要是在diffReconcileManyChildren方法里面实现。

对于多节点的Diff,我们需要进行以下步骤。

  1. 创建变量lastIndex,用来标记索引
  2. 将旧的filberNode列表,转换为map结构,key为filberNode的key,value为filberNode
  3. 遍历新的element数组。
  4. 如果element.key可以在map中找到,lastIndex记录为找到的filberNode的index
  5. 如果找不到,创建新的FilberNode
  6. 继续遍历,如果又在map中找到filberNode,比较fiberNode的index和lastIndex.
  7. 如果index < lastIndex,给filberNode打上移动的标志

基于上面的步骤,实现diffReconcileManyChildren方法

function diffReconcileManyChildren(filberNode, element) {
  let firstChild = filberNode.child;
  if(!firstChild) {
    return;
  }
  const head = {
    sibling: null
  };
  const oldChildren = []
  while(firstChild) {
    oldChildren.push(firstChild);
    firstChild = firstChild.sibling;
  }
  const oldMap = new Map();
  oldChildren.forEach((item,index) => {
    item.index = index
    if(item.key) {
      oldMap.set(item.key, item)
    }else{
      oldMap.set(index, item)
    }
  })
  let lastIndex = 0;
  let empty = head
  for(let i=0; i<element.length; i++) {
    if(!element[i].key){
      continue;
    }
    const useFilber = oldMap.get(element[i].key);
    useFilber.sibling = null;
    if(useFilber) {
      if(useFilber.index < lastIndex) {
        useFilber.flags = 'insert'
      }
      useFilber.memoizedProps = element[i]
      lastIndex = useFilber.index;
      empty.sibling = useFilber;
      empty = empty.sibling;
      oldMap.delete(element[i].key)
    }else{
      const filberNode = new FilberNode(HostComponent, element[i].props, element[i].key) 
      filberNode.type = element[i].type
      empty.sibling = filberNode;
      empty = empty.sibling;
    }

  }
  return head.sibling;
}

经过上面的处理,beginWork流程结束,可复用的filberNode就不会重复创建。

2.修改completeWork流程

在beginWork中,可复用的节点已经被打上了insert的标志,所以在updateCompleteHsotComponent中,我们要判断是不是insert的标志,如果是,就不能无脑创建,而是通过移动DOM的位置来复用DOM。

同时,也要对同级的sibling进行递归处理。

function updateCompleteHostComponent(filberNode) {
  //其他代码。。。。
  if(element.key === filberNode.key && element.type === filberNode.type) {
    addPropsToDOM(filberNode.stateNode, filberNode.pendingProps);
    if(filberNode.flags === 'insert') {
      const parent = filberNode.return;
      parent.stateNode.insertBefore(filberNode.stateNode, filberNode.sibling?.stateNode)
    }
  //其他代码
  if(filberNode.sibling) {
    completeWork(filberNode.sibling)
  }
}

在对HostText的处理中,也要考虑,当前的操作是更新还是替换。文章来源地址https://www.toymoban.com/news/detail-661960.html

function completeHostText(filberNode) {
  //其他代码。。。。。
  if(parent && parent.stateNode && parent.tag === HostComponent) {
    if(!parent.stateNode) {
      parent.stateNode.appendChild(element);
    }else{
      parent.stateNode.replaceChildren(element);
    }
  }
  //其他代码。。。。
}

到了这里,关于React源码解析18(10)------ 实现多节点的Diff算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • React源码解析18(11)------ 实现多次setState的批处理

    在React中,如果涉及到了多次setState,组件render几次。setState是同步的还是异步的。这是一个很常见的面试题。 而本篇文章,就是主要实现React中,对于这部分的性能优化,我们称之为批处理。例如当我有下面的JSX。 对于当前的点击事件来说,只有最后的setNum(num + 3)是有效的。

    2024年02月11日
    浏览(45)
  • vue和react的diff算法源码

    Vue.js 的 Diff 算法主要基于 Snabbdom,以下是 Vue.js 中虚拟 DOM Diff 算法的简化版伪代码,以便说明其基本思想: 这里主要关注 patchVnode 函数,它是 Diff 算法的核心。Vue.js 的 Diff 算法采用了一种双端比较的策略,具体步骤如下: 1.同层级比较: 首先比较新旧节点的同层级,通过

    2024年03月15日
    浏览(53)
  • React源码解析18(5)------ 实现函数组件【修改beginWork和completeWork】

    经过之前的几篇文章,我们实现了基本的jsx,在页面渲染的过程。但是如果是通过函数组件写出来的组件,还是不能渲染到页面上的。 所以这一篇,主要是对之前写得方法进行修改,从而能够显示函数组件,所以现在我们在index.js文件中,修改一下jsx的写法。修改成函数组件

    2024年02月13日
    浏览(38)
  • React源码解析18(1)------ React.createElement 和 jsx

    我们知道在React17版本之前,我们在项目中是一定需要引入react的。 import React from “react” 即便我们有时候没有使用到React,也需要引入。原因是什么呢? 在React项目中,如果我们使用了模板语法JSX,我们知道它要先经过babel的转译。那babel会将JSX转换成什么样子的格式呢? 可

    2024年02月13日
    浏览(37)
  • React源码解析18(2)------ FilberNode,FilberRootNode结构关系

    在上一篇,我们实现了通过JSX转换为ReactElement的方法,也看到了转换后React元素的结构。但是这个React元素,并不能很清楚的表达组件之间的关系,以及属性的处理。 所以在React内部,会将所有的React元素转换为Filber树。而这一章节,主要就是简单描述一下FilberNode的结构。 首

    2024年02月13日
    浏览(34)
  • React源码解析18(4)------ completeWork的工作流程【mount】

    经过上一章,我们得到的FilberNode已经具有了child和return属性。一颗Filber树的结构已经展现出来了。 那我们最终是想在页面渲染真实的DOM。所以我们现在要在completeWork里,构建出一颗离屏的DOM树。 之前在说FilberNode的属性时,我们提到过一个属性stateNode。它就是用来保存每个

    2024年02月13日
    浏览(38)
  • React源码解析18(3)------ beginWork的工作流程【mount】

    OK,经过上一篇文章。我们调用了: 生成了FilberRootNode和HostRootFilber。 并且二者之间的对应关系也已经确定。 而下一步我们就需要调用render方法来讲react元素挂载在root上: 所以我们现在要实现ReactDOM.createRoot中返回的render方法。 首先我们思考一下,在React中,除了通过上面的

    2024年02月13日
    浏览(48)
  • React Diff算法原理

    👉点此(想要了解Diff算法) Diff算法原理 React Diff算法是React用于更新虚拟DOM树的一种算法。它通过比较新旧虚拟DOM树的差异,然后只对有差异的部分进行更新,从而提高性能。 React Diff算法的原理如下: 逐层比较:React Diff算法会从根节点开始逐层比较新旧虚拟DOM树的节点。

    2024年02月11日
    浏览(36)
  • React Diff算法

    diff算法,是虚拟DOM产生的一个概念,用来计算出虚拟DOM中真正变化的部分,并只针对该部分进行原生DOM操作,而非重新渲染整个页面,从而提高了页面的渲染效率。 渲染真实DOM的开销很大,有时候我们修改某个数据时,直接渲染到真实DOM上会引起整个DOM树的重绘和重排。我

    2024年02月11日
    浏览(60)
  • 后端程序员入门react笔记——react的diff算法(三)

    我们知道,react里面操作的都是虚拟dom,最后经过render渲染为真正的dom,那么为什么要提出虚拟dom这个概念呢?其实就是将逻辑和视图区分开,react的虚拟dom,就相当于mvc的c,将数据逻辑和真正的dom区分开,我们知道,对于前端来说dom操作是非常昂贵的,性能消耗最大的就是

    2024年02月22日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包