iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Vue的diff算法原理是什么
  • 845
分享到

Vue的diff算法原理是什么

2023-06-29 13:06:59 845人浏览 独家记忆
摘要

这篇文章将为大家详细讲解有关Vue的diff算法原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。思维导图0. 从常见问题引入虚拟dom是什么?如何创建虚拟dom?虚拟dom如何渲染成真是dom?虚

这篇文章将为大家详细讲解有关Vue的diff算法原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

思维导图

Vue的diff算法原理是什么

Vue的diff算法原理是什么

Vue的diff算法原理是什么

Vue的diff算法原理是什么

Vue的diff算法原理是什么

Vue的diff算法原理是什么

Vue的diff算法原理是什么

0. 从常见问题引入

  • 虚拟dom是什么?

  • 如何创建虚拟dom?

  • 虚拟dom如何渲染成真是dom?

  • 虚拟dom如何patch(patch)

  • 虚拟DOM的优势?(性能)

  • Vue中的key到底有什么用,为什么不能用index?

  • Vue中的diff算法实现

  • diff算法是深度还是广度优先遍历

1. 生成虚拟dom

1. h方法实现

virtual dom ,也就是虚拟节点

它通过js的Object对象模拟dom中的节点

再通过特定的render方法将其渲染成真实的dom节点

eg:

<div id="wrapper" class="1">    <span >hello</span>    world</div>

如果利用h方法生成虚拟dom的话:

h('div', { id: 'wrapper', class: '1' }, h('span', { style: { color: 'red' } }, 'hello'), 'world');

对应的js对象如下:

let vd = {    type: 'div',    props: { id: 'wrapper', class: '1' },    children: [        {            type: 'span',            props: { color: 'red' },            children: [{}]        },        {            type: '',            props: '',            text: 'world'        }    ]}

自己实现一个h方法

 function createElement(type, props = {}, ...children) {    // 防止没有传值的话就赋值一个初始值    let key;    if (props.key) {        key = props.key        delete props.key    }    // 如果孩子节点有字符串类型的,也需要转化为虚拟节点    children = children.map(child => {        if (typeof child === 'string') {            // 把不是节点类型的子节点包装为虚拟节点            return vnode(undefined, undefined, undefined, undefined, child)        } else {            return child        }    })    return vNode(type, props, key, children)}function vNode(type, props, key, children, text = undefined) {    return {        type,        props,        key,        children,        text    }}

2. render方法实现

render的作用:把虚拟dom转化为真实dom渲染到container容器中去

export function render(vnode, container) {    let ele = createDomElementFrom(vnode) //通过这个方法转换真实节点    if (ele) container.appendChild(ele)}

把虚拟dom转化为真实dom,插入到容器中,如果虚拟dom对象包含type值,说明为元素(createElement),否则为节点类型(createTextnode),并把真实节点赋值给虚拟节点,建立起两者之间的关系

function createDomElementFrom(vnode) {    let { type, key, props, children, text } = vnode    if (type) {//说明是一个标签        // 1. 给虚拟元素加上一个domElemnt属性,建立真实和虚拟dom的联系,后面可以用来跟新真实dom        vnode.domElement = document.createElement(type)        // 2. 根据当前虚拟节点的属性,去跟新真实dom的值        updateProperties(vnode)        // 3. children中方的也是一个个的虚拟节点(就是递归把儿子追加到当前元素里)        children.forEach(childVnode => render(childVnode, vnode.domElement))    } else {//说明是一个文本    }    return vnode.domElement}function updateProperties(newVnode, oldProps = {}) {    let domElement = newVnode.domElement //真实dom,    let newProps = newVnode.props; //当前虚拟节点中的属性    // 如果老的里面有,新的里面没有,说明这个属性被移出了    for (let oldPropName in oldProps) {        if (!newProps[oldPropName]) {            delete domElement[oldPropName] //新的没有,为了复用这个dom,直接删除        }    }    // 如果新的里面有style,老的里面也有style,style可能还不一样    let newStyleObj = newProps.style || {}    let oldStyleObj = oldProps.style || {}    for (let propName in oldStyleObj) {        if (!newStyleObj[propName]) {            domElement.style[propName] = ''        }    }    // 老的里面没有,新的里面有    for (let newPropsName in newProps) {        // 直接用新节点的属性覆盖老节点的属性        if (newPropsName === 'style') {            let styleObj = newProps.style;            for (let s in styleObj) {                domElement.style[s] = styleObj[s]            }        } else {            domElement[newPropsName] = newProps[newPropsName]        }    }}

根据当前虚拟节点的属性,去更新真实dom的值
由于还有子节点,所以还需要递归,生成子节点虚拟dom的真实节点,插入当前的真实节点里去

Vue的diff算法原理是什么

3. 再次渲染

刚刚可能会有点不解,为什么要把新的节点和老的节点属性进行比对,因为刚刚是首次渲染,现在讲一下二次渲染

比如说现在构建了一个新节点newNode,我们需要和老节点进行对比,然而并不是简单的替换,而是需要尽可能多地进行复用

首先判断父亲节点的类型,如果不一样就直接替换

如果一样

文本类型,直接替换文本值即可

元素类型,需要根据属性来替换

这就证明了render方法里我们的oldProps的必要性,所以这里把新节点的真实dom赋值为旧节点的真实dom,先复用一波,待会再慢慢修改

updateProperties(newVnode, oldVNode.props)

export function patch(oldVNode, newVnode) {    // //判断类型是否一样,不一样直接用新虚拟节点替换老的    if (oldVNode.type !== newVnode.type) {        return oldVNode.domElement.parentNode.replaceChild(            createDomElementFrom(newVnode), oldVNode.domElement        )    }    // 类型相同,且是文本    if (oldVNode.text) {        return oldVNode.document.textContent = newVnode.text    }    // 类型一样,不是文本,是标签,需要根据新节点的属性更新老节点的属性    // 1. 复用老节点的真实dom    let domElement = newVnode.domElement = oldVNode.domElement    // 2. 根据最新的虚拟节点来更新属性    updateProperties(newVnode, oldVNode.props)    // 比较儿子    let oldChildren = oldVNode.children    let newChildren = newVnode.children    // 1. 老的有儿子,新的有儿子    if (oldChildren.length > 0 && newChildren.length > 0) {        // 对比两个儿子(很复杂)    } else if (oldChildren.length > 0) {        // 2. 老的有儿子,新的没儿子        domElement.innerhtml = ''    } else if (newChildren.length > 0) {        // 3. 新增了儿子        for (let i = 0; i < newChildren.length; i++) {            // 把每个儿子加入元素里            let ele = createDomElementFrom(newChildren[i])            domElement.appendChild(ele)        }    }}

2. diff算法

刚刚的渲染方法里,首先是对最外层元素进行对比,对于儿子节点,分为三种情况

老的有儿子,新的没儿子(那么直接把真实节点的innerHTML设置为空即可)

老的没儿子,新的有儿子(那么遍历新的虚拟节点的儿子列表,把每一个都利用createElementFrom方法转化为真实dom,append到最外层真实dom即可)

老的有儿子,新的有儿子,这个情况非常复杂,也就是我们要提及的diff算法

1. 对常见的dom做优化

  • 前后追加元素

  • 正序和倒序元素

  • 中间插入元素

以最常见的ul列表为例子

旧的虚拟dom

let oldNode = h('div', {},    h('li', { style: { background: 'red' }, key: 'A' }, 'A'),    h('li', { style: { background: 'blue' }, key: 'B' }, 'A'),    h('li', { style: { background: 'yellow' }, key: 'C' }, 'C'),    h('li', { style: { background: 'green' }, key: 'D' }, 'D'),);
情况1:末尾追加一个元素(头和头相同)

新的虚拟节点

Vue的diff算法原理是什么

let newVnode = h('div', {},    h('li', { style: { background: 'red' }, key: 'A' }, 'A'),    h('li', { style: { background: 'blue' }, key: 'B' }, 'B'),    h('li', { style: { background: 'yellow' }, key: 'C' }, 'C1'),    h('li', { style: { background: 'green' }, key: 'D' }, 'D1'),    h('li', { style: { background: 'black' }, key: 'D' }, 'E'),);

eg:

// 比较是否同一个节点function isSameVnode(oldVnode, newVnode) {    return oldVnode.key == newVnode.key && oldVnode.type == newVnode.type}// difffunction updateChildren(parent, oldChildren, newChildren) {    // 1. 创建旧节点开头指针和结尾    let oldStartIndex = 0    let oldStartVnode = oldChildren[oldStartIndex];    let oldEndIndex = oldChildren.length - 1    let oldEndVnode = oldChildren[oldEndIndex];    // 2. 创建新节点的指针    let newStartIndex = 0    let newStartVnode = newChildren[newStartIndex];    let newEndIndex = newChildren.length - 1    let newEndVnode = newChildren[newEndIndex];    // 1. 当从后面插入节点的时候,希望判断老的孩子和新的孩子 循环的时候,谁先结束就停止循环    while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {        // 注意:比较对象是否相等,你不能用==,因为指向的位置可能不一样,可以用type和key        if (isSameVnode(oldStartVnode, newStartVnode)) {            //patch比对更新            patch(oldStartVnode, newStartVnode)            // 移动指针            oldStartVnode = oldChildren[++oldStartIndex]            newStartVnode = newChildren[++newStartIndex]        }    }    if (newStartIndex <= newEndIndex) {        for (let i = newStartIndex; i <= newEndIndex; i++) {            parent.appendChild(createDomElementFrom(newChildren[i]))        }    }}

Vue的diff算法原理是什么

情况2:队首添加一个节点(尾和尾)

Vue的diff算法原理是什么

Vue的diff算法原理是什么

头和头+尾和尾的处理方法:

我们通过parent.insertBefore(createDomElementFrom(newChildren[i]), beforeElement)使得末尾添加和头部添加采用同一种处理方法

    // 如果是从前往后遍历说明末尾新增了节点,会比原来的儿子后面新增了几个    // 也可以时从后往前遍历,说明比原来的儿子前面新增了几个    if (newStartIndex <= newEndIndex) {        for (let i = newStartIndex; i <= newEndIndex; i++) {            // 取得第一个值,null代表末尾            let beforeElement = newChildren[newEndIndex + 1] == null ? null : newChildren[newEndIndex + 1].domElement   parent.insertBefore(createDomElementFrom(newChildren[i]), beforeElement)        }    }

图解:

Vue的diff算法原理是什么

MVVM=>数据一变,就调用patch

情况3:翻转类型(头和尾)

Vue的diff算法原理是什么

尾和头就不画图了

else if (isSameVnode(oldStartVnode, newEndVnode)) {                // 头和尾巴都不一样,拿老的头和新的尾巴比较                patch(oldStartVnode, newEndVnode)                // 把旧节点的头部插入到旧节点末尾指针指向的节点之后一个                parent.insertBefore(oldStartVnode.domElement, oldEndVnode.domElement.nextSibling)                // 移动指针                oldStartVnode = oldChildren[++oldStartIndex]                newEndVnode = newChildren[--newEndIndex]            } else if (isSameVnode(oldEndVnode, newStartVnode)) {                // 头和尾巴都不一样,拿老的头和新的尾巴比较                patch(oldEndVnode, newStartVnode)                // 把旧节点的头部插入到旧节点末尾指针指向的节点之后一个                parent.insertBefore(oldEndVnode.domElement, oldStartVnode.domElement)                // 移动指针                oldEndVnode = oldChildren[--oldEndIndex]                newStartVnode = newChildren[++newStartIndex]            } else {
情况4: 暴力比对复用

Vue的diff算法原理是什么

else {                // 都不一样,就暴力比对                // 需要先拿到新的节点去老的节点查找是否存在相同的key,存在则复用,不存在就创建插入即可                // 1. 先把老的哈希                let index = map[newStartVnode.key]//看看新节点的key在不在这个map里                console.log(index);                if (index == null) {//没有相同的key                    // 直接创建一个,插入到老的前面即可                    parent.insertBefore(createDomElementFrom(newStartVnode),                        oldStartVnode.domElement)                } else {//有,可以复用                    let toMoveNode = oldChildren[index]                    patch(toMoveNode, newStartVnode)//复用要先patch一下                    parent.insertBefore(toMoveNode.domElement, oldStartVnode.domElement)                    oldChildren[index] = undefined                    // 移动指正                }                newStartVnode = newChildren[++newStartIndex]            }// 写一个方法,做成一个哈希表{a:0,b:1,c:2}function createMapToIndex(oldChildren) {    let map = {}    for (let i = 0; i < oldChildren.length; i++) {        let current = oldChildren[i]        if (current.key) {            map[current.key] = i        }    }    return map}

对于key的探讨

1. 为什么不能没有key

Vue的diff算法原理是什么

2. 为什么key不能是index

Vue的diff算法原理是什么

3. diff的遍历方式

采用的是深度优先,只会涉及到dom树同层的比较,先对比父节点是否相同,然后对比儿子节点是否相同,相同的话对比孙子节点是否相同

Vue的diff算法原理是什么

关于“Vue的diff算法原理是什么”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

--结束END--

本文标题: Vue的diff算法原理是什么

本文链接: https://www.lsjlt.com/news/325058.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Vue的diff算法原理是什么
    这篇文章将为大家详细讲解有关Vue的diff算法原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。思维导图0. 从常见问题引入虚拟dom是什么如何创建虚拟dom虚拟dom如何渲染成真是dom虚拟do...
    99+
    2023-06-29
  • Vue的diff算法原理你真的了解吗
    目录思维导图0. 从常见问题引入1. 生成虚拟dom1. h方法实现2. render方法实现3. 再次渲染2. diff算法1. 对常见的dom做优化情况1:末尾追加一个元素(头和...
    99+
    2024-04-02
  • Vue中Virtual DOM和Diff原理及实现方法是什么
    本篇内容介绍了“Vue中Virtual DOM和Diff原理及实现方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. v...
    99+
    2023-07-05
  • React中diff算法是什么
    这篇文章主要介绍了React中diff算法是什么,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。React中diff算法的理解diff算法用来计算出Virtual DOM中改变...
    99+
    2023-06-15
  • vue怎么实现diff算法
    这篇文章主要介绍“vue怎么实现diff算法”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“vue怎么实现diff算法”文章能帮助大家解决问题。 模块路径:src\...
    99+
    2024-04-02
  • Vue2 的 diff 算法规则原理详解
    目录前言算法规则diff 优化策略老数组的开始与新数组的开始老数组的结尾与新数组的结尾老数组的开始与新数组的结尾老数组的结尾与新数组的开始以上四种情况都没对比成功推荐在渲染列表时为节...
    99+
    2024-04-02
  • vue.js diff算法原理详细解析
    目录diff算法的概念虚拟Domh函数diff对比规则patchpatchVnodeupdateChildren总结diff算法的概念 diff算法可以看作是一种对比算法,...
    99+
    2024-04-02
  • chatgpt的算法原理是什么
    这篇“chatgpt的算法原理是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“chatgpt的算法原理是什么”文章吧。I...
    99+
    2023-07-05
  • Vue的双端diff算法怎么实现
    这篇文章主要介绍了Vue的双端diff算法怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue的双端diff算法怎么实现文章都会有所收获,下面我们一起来看看吧。前言Vue 和 React 都是基于 vd...
    99+
    2023-07-02
  • Vue中的双端diff算法怎么应用
    这篇文章主要讲解了“Vue中的双端diff算法怎么应用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Vue中的双端diff算法怎么应用”吧!Vue 和 React 都是基于 vdom 的前端...
    99+
    2023-07-02
  • 简单谈谈Vue中的diff算法
    目录概述 虚拟Dom(virtual dom) 原理 实现过程 patch方法 sameVnode函数 patchVnode函数 updateChildren函数 结语 概述 di...
    99+
    2024-04-02
  • react的diff方法是什么
    本教程操作环境:Windows10系统、react18.0.0版、Dell G3电脑。react的diff方法是什么?一、Diff算法的作用 渲染真实DOM的开销很大,有时候我们修改了某个数据,直接渲染到真实dom上会引起整个...
    99+
    2023-05-14
    React
  • 一文详解Vue 的双端 diff 算法
    目录前言diff 算法简单 diff双端 diff总结前言 Vue 和 React 都是基于 vdom 的前端框架,组件渲染会返回 vdom,渲染器再把 vdom 通过增删改的 ap...
    99+
    2024-04-02
  • Python DQN算法原理是什么
    本篇内容主要讲解“Python DQN算法原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python DQN算法原理是什么”吧!1 DQN算法简介Q-learning算法采用一个Q-t...
    99+
    2023-06-25
  • react diff 算法实现思路及原理解析
    目录事例分析diff 特点diff 思路实现 diff 算法修改入口文件实现 React.Fragment我们需要修改 children 对比前面几节我们学习了解了 react 的渲...
    99+
    2024-04-02
  • React Diff算法不采用Vue的双端对比原因详解
    目录前言React 官方的解析Fiber 的结构Fiber 链表的生成React 的 Diff 算法第一轮,常见情况的比对第二轮,不常见的情况的比对重点如何协调更新位置信息小结图文解...
    99+
    2024-04-02
  • Vue 2.5中怎么实现一个Diff算法
    Vue 2.5中怎么实现一个Diff算法,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1.VNode对象一个VNode的实例包含了以下属性,这...
    99+
    2024-04-02
  • linux中diff的用法是什么
    在Linux中,diff命令用于比较两个文件的内容并显示它们之间的差异。diff命令的基本语法如下: diff file1 file...
    99+
    2024-04-09
    Linux
  • Vue.$nextTick的原理是什么
    这篇文章主要介绍了Vue.$nextTick的原理是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue.$nextTick的原理是什么文章都会有所收获,下面我们一起来看看吧。Vue中DOM更新机制当你气势...
    99+
    2023-07-05
  • 怎样深入理解vue中的虚拟DOM和Diff算法
    怎样深入理解vue中的虚拟DOM和Diff算法,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。真实DOM的渲染在讲虚拟DOM之前,先说一下真实DOM的渲染。浏览器真实DOM渲...
    99+
    2023-06-22
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作