当前位置: 首页 > article >正文

关于Vue/React中Diffing算法以及key的作用

       

目录

1. DIffing算法

 1.1 React中的Diffing算法

1.2 Vue中的Diffing算法

1.3 两者对比

2. React/Vue中key的作用(key的内部原理)

3. 正确解决方案


        最近在学习React和复习Vue相关知识的时候,又重新学习到了两者在循环遍历时key的相关知识内容,正好记录复习一下。

1. DIffing算法

        首先React和Vue作为两个前端目前比较热门的框架,两个框架都实现的是将虚拟DOM转换为真实DOM并渲染到页面上,并且有着相当高的效率,一提到效率我们就得先讲解一下两者框架的虚拟DOM中的Diffing算法

 1.1 React中的Diffing算法

        React 采用的是一种叫做 虚拟 DOM (Virtual DOM) 的技术,在进行 UI 更新时,React 会先创建一棵虚拟 DOM 树,然后在更新时通过 diffing 算法比较旧的虚拟 DOM 和新的虚拟 DOM。React 的 diffing 算法遵循一些优化原则来高效地进行比较。

核心原则

1. 单向数据流

React 通过单向数据流来简化 diffing 操作,即所有组件的状态和属性变化都会导致重新渲染。这使得 React 可以将整个应用的变化分解成小的组件更新,方便进行优化。

2. 元素类型不同

如果新旧两个虚拟 DOM 节点的类型不同,React 会直接销毁旧节点并创建一个新节点。例如,如果你从 <div> 改为 <span>,React 就会认为它们是两个完全不同的元素。

3. 相同类型的元素比较子节点

如果新旧节点类型相同,React 会比较它们的子节点。React 通过 深度优先遍历 来更新树的不同部分,递归地比较子节点,进行最小更新。

4. 节点复用与排序优化

React 假设相同类型的元素之间不会改变顺序。因此,对于相同类型的兄弟节点,React 会尽量复用它们。如果节点的顺序变化较大,React 会做出必要的调整来减少重渲染。

5. Keys

React 使用 key 来标识节点在列表中的位置,key 主要用于优化同级元素的更新。当有 key 时,React 会利用它们来决定哪些节点可以复用,从而避免不必要的删除和插入操作。

Diffing 过程简述:

• React 首先通过 浅比较 组件的 props 和 state,决定是否需要更新。

• 如果更新必要,React 会根据节点类型判断是否是完全不同的节点(例如标签不同)还是只是部分变化(例如文本内容变化)。

• 对于相同类型的节点,React 会比较子节点,并递归处理每一层的变化,采用 O(n) 的时间复杂度进行节点更新。

1.2 Vue中的Diffing算法

        Vue 的 diffing 算法也基于虚拟 DOM,但与 React 略有不同,Vue 的 diffing 更加注重性能优化,尤其是在节点更新的过程中。Vue 的实现依赖于其响应式系统和模板编译器,因此它的 diffing 算法可能会和 React 在一些细节上有所区别。

核心原则

1. 虚拟 DOM 与响应式系统

Vue 通过数据驱动视图的更新,采用响应式系统,当数据变化时,Vue 会自动触发组件的更新。Vue 的 diffing 算法基于它的 虚拟 DOM 实现,Vue 会在每次更新时构建一个新的虚拟 DOM 树,并与旧的虚拟 DOM 树进行对比。

2. 浅比较

Vue 采用的是 浅比较,与 React 类似。当虚拟 DOM 中的节点发生变化时,Vue 会只对节点的属性、事件等进行比较。如果节点类型不同,Vue 会重新创建节点。

3. “就地更新”与“节点复用”

Vue 使用了一种 就地更新 的策略,尽量避免无意义的 DOM 操作。如果某些节点的变化不会影响 DOM,Vue 会选择不更新这些节点,只更新发生变化的部分。Vue 会尽量保留现有节点,避免不必要的重渲染。

4. 动态节点与静态节点分离

Vue 会对节点进行分类,将 静态节点动态节点 分离。静态节点不会被重新渲染,而动态节点会根据数据的变化进行更新。Vue 会通过 树的比较 来优化更新过程,从而减少不必要的操作。

5. Keys

Vue 也支持使用 key 来优化 diffing 算法。当同一层级的节点发生变化时,Vue 会通过 key 来识别并更新节点的位置,以便提高渲染效率。

Diffing 过程简述:

• Vue 会通过构建新的虚拟 DOM 树并与旧的虚拟 DOM 树进行比较。

• 在比较过程中,Vue 会对比节点的 类型、属性、事件等,如果类型不同,Vue 会销毁旧节点并创建新节点。

• 对于相同类型的节点,Vue 会利用它的 优化策略(如静态节点的保留、动态节点的更新)来决定哪些节点需要更新。

• Vue 会依赖 key 来帮助优化同级节点的更新。

1.3 两者对比

特性

React

Vue

虚拟 DOM

是的,React 使用虚拟 DOM,进行最小化的 DOM 更新

是的,Vue 使用虚拟 DOM,并通过响应式系统触发更新

浅比较

是的,React 通过浅比较来决定是否更新节点

是的,Vue 也通过浅比较来判断节点的变化

节点类型变化

类型不同的节点会完全重新渲染

同 React,类型不同的节点会完全重新渲染

子节点比较

对于相同类型的节点,会比较子节点并递归更新

同样,Vue 会递归比较子节点,尤其注重静态与动态节点的区分

key 的使用

使用 key 来优化列表更新,帮助避免不必要的 DOM 操作

使用 key 来优化同层节点的更新

更新优化

React 假设节点顺序不会频繁变化,利用双指针算法减少更新

Vue 更加注重节点复用和静态节点的分离,优化树的

2. React/Vue中key的作用(key的内部原理)

        当数据发生变化时,React会根据新的数据生成新的虚拟DOM,随后React进行diff算法比较,主要规则如下:

a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:

         (1) 若虚拟DOM中内容没变,直接使用之前的真实DOM

         (2) 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉旧的真实DOM

b. 旧虚拟DOM中未找到与新虚拟DOM相同的key:

         根据数据创建新的真实DOM,随后渲染到页面

        但是如果我们简单的使用index作为key的话,那么请看以下代码:

class Person extends React.Component {
        state = {
            persons: [
                {id:1, name: 'stephen', age:18},
                {id:2, name: 'Jack', age:21}
            ]
        }

        render() {
            return (
                <div>
                    <h2>Show Informations</h2>
                    <button onClick={this.add}>Add James</button>
                    <ul>
                        {
                            this.state.persons.map((person, index) => {
                                return <li key={index}>{person.name} -- {person.age}</li>
                            })
                        }
                    </ul>
                </div>
            )
        }

        add = () => {
            const {persons} = this.state
            const p = {id: persons.length+1, name: 'James', age: 20}
            {/* 注意此时的添加顺序是逆序的 */}
            this.setState({persons: [p, ...persons]})
        }
    }

    ReactDOM.render(<Person/>, document.getElementById('test'))

并且请注意我此时添加的顺序是逆序的,那么React在生成虚拟DOM进行diffing算法对比时就会出现问题:

当进行Diffing对比时,按照规则对比key时,发现对应不上,也就是说React又重新帮我们重新创建出三个虚拟DOM,其实我们只添加了一条数据但是却要创建多个虚拟DOM,如果我们本身的数据量很大,那么带来了一定的资源浪费,这是我们需要避免的。

但是其实有人也发现了:那如果我不逆序添加不就没有问题了吗,对于上述例子来说这种说法是正确的。但是问题又来了,这仅仅是数据的展示,如果每一个<li>标签下又存在一个<input>标签,我们需要为每一个<li>标签添加对应的<input>值,如果此时我们使用index作为key,后续生成新的DOM时会发现原先对应的数据顺序全部混乱,这在我们开发时是十分重大错误。产生这个原因在于我上述所讲到的React 在diffing算法时的原理,按照每一个标签进行渲染。

3. 正确解决方案

        我们在开发过程中使用key尽量避免用index以免产生错误或者效率问题。我们应该选用数据的唯一标识,例如后端服务器传回来的data中,可能会含有Primary Key的字段,那么我们可以使用该字段作为key来避免一些问题的出现,以下是合理代码的改造:

class Person extends React.Component {
        state = {
            persons: [
                {id:1, name: 'stephen', age:18},
                {id:2, name: 'jack', age:21}
            ]
        }

        render() {
            return (
                <div>
                    <h2>Show Informations</h2>
                    <button onClick={this.add}>Add James</button>
                    <ul>
                        {
                            this.state.persons.map((person) => {
                                return <li key={person.id}>{person.name} -- {person.age}</li>
                            })
                        }
                    </ul>
                </div>
            )
        }

        add = () => {
            const {persons} = this.state
            const p = {id: persons.length+1, name: 'James', age: 20}
            this.setState({persons: [p, ...persons]})
        }
    }

    ReactDOM.render(<Person/>, document.getElementById('test'))


http://www.kler.cn/a/581597.html

相关文章:

  • C/S架构与B/S架构
  • C++设计模式-观察者模式:从基本介绍,内部原理、应用场景、使用方法,常见问题和解决方案进行深度解析
  • 【算法】BFS(最短路径问题、拓扑排序)
  • ScanPy - Preprocessing and clustering 3k PBMCs (legacy workflow)工作复现
  • 【亲测有效】Mac系统升级或降级Node.js版本,Mac系统调整node.js版本
  • alibaba EasyExcel的使用说明
  • 基于Ollama平台部署的Qwen大模型实现聊天机器人
  • git规范提交之commitizen conventional-changelog-cli 安装
  • 《灵珠觉醒:从零到算法金仙的C++修炼》卷三·天劫试炼(27)混元幡遮天机 - 第一个错误版本(二分边界)
  • golang从入门到做牛马:第十四篇-Go语言结构体:数据的“定制容器”
  • CSS中相对定位使用详情
  • 力扣热题 100:贪心算法专题经典题解析
  • 【干货教程】在Windows计算机部署DeepSeek大模型,给在实验室无外网的同事们用(基于Ollama和OpenWebUI)
  • Java直通车系列23【Spring Boot】(了解 Spring Boot 概念与优势)
  • Camel AI Owl + 阿里云QWQ 本地部署
  • Ubuntu 下 nginx-1.24.0 源码分析 (1)
  • 桂云OSG:桂链是什么?
  • Html5学习教程,从入门到精通, HTML5 Canvas 全攻略:从入门到精通(19)
  • 《苍穹外卖》SpringBoot后端开发项目核心知识点整理(DAY1 to DAY3)
  • 使用外挂工具,简化教师资格面试的纸质试题打印操作