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

广州 营销型网站建设公司凡科建站靠谱吗

广州 营销型网站建设公司,凡科建站靠谱吗,想要网站导航推广页,深圳做美颜相机的公司Vue3源码解析 diff比较patch(diff过程)vue2 diff 双端比较patch函数patchVnode函数updateChildren实现思路 Vue3最长递增子序列 diff比较 patch(diff过程) 为什么在 VNode 层面上做 Patch ,而不是在VDOM层面上做 Patch 第一,要保证跨平台,c…

Vue3源码解析

  • diff比较
    • patch(diff过程)
      • vue2 diff 双端比较
        • patch函数
        • patchVnode函数
        • updateChildren
          • 实现思路
      • Vue3最长递增子序列

diff比较

patch(diff过程)

为什么在 VNode 层面上做 Patch ,而不是在VDOM层面上做 Patch
第一,要保证跨平台,cross platform
第二,不需要那么多的属性attribute,只针对当前所希望获取到的那部分属性去消费就好了

因此这里通过 虚拟对象(虚拟VDOM) 来进行判断,判断到底该用什么,怎么用它

<div><p><span>123</span></p>
</div>
  1. 同层比较
  2. 同一层级的同一元素比较,类型不一样,直接当前节点&所有子节点 销毁
  3. 类型一样,借助key

初始化时候,mount
然后进行 patch(diff)

vue2 diff 双端比较

数组的头和尾去比较

patch函数
function patch(oldVnode, vnode, hydrating, removeOnly) {// 判断新的vnode是否为空if (isUndef(vnode)) {// 如果老的vnode不为空 卸载所有的老vnodeif (isDef(oldVnode)) invokeDestroyHook(oldVnode)return}let isInitialPatch = false// 用来存储 insert钩子函数,在插入节点之前调用const insertedVnodeQueue = []// 如果老节点不存在,直接创建新节点if (isUndef(oldVnode)) {isInitialPatch = truecreateElm(vnode, insertedVnodeQueue)} else {// 是不是元素节点const isRealElement = isDef(oldVnode.nodeType)// 当老节点不是真实的DOM节点,并且新老节点的type和key相同,进行patchVnode更新工作if (!isRealElement && sameVnode(oldVnode, vnode)) {patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)} else {// 如果不是同一元素节点的话// 当老节点是真实DOM节点的时候if (isRealElement) {// 如果是元素节点 并且在SSR环境的时候 修改SSR_ATTR属性if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {// 就是服务端渲染的,删掉这个属性oldVnode.removeAttribute(SSR_ATTR)hydrating = true}// 这个判断里是服务端渲染的处理逻辑if (isTrue(hydrating)) {if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {invokeInsertHook(vnode, insertedVnodeQueue, true)return oldVnode}}// 如果不是服务端渲染的,或者混合失败,就创建一个空的注释节点替换 oldVnodeoldVnode = emptyNodeAt(oldVnode)}// 拿到 oldVnode 的父节点const oldElm = oldVnode.elmconst parentElm = nodeOps.parentNode(oldElm)// 根据新的 vnode 创建一个 DOM 节点,挂载到父节点上createElm(vnode,insertedVnodeQueue,oldElm._leaveCb ? null : parentElm,nodeOps.nextSibling(oldElm))// 如果新的 vnode 的根节点存在,就是说根节点被修改了,就需要遍历更新父节点// 递归 更新父占位符元素// 就是执行一遍 父节点的 destory 和 create 、insert 的 钩子函数if (isDef(vnode.parent)) {let ancestor = vnode.parentconst patchable = isPatchable(vnode)// 更新父组件的占位元素while (ancestor) {// 卸载老根节点下的全部组件for (let i = 0; i < cbs.destroy.length; ++i) {cbs.destroy[i](ancestor)}// 替换现有元素ancestor.elm = vnode.elmif (patchable) {for (let i = 0; i < cbs.create.length; ++i) {cbs.create[i](emptyNode, ancestor)}// #6513// invoke insert hooks that may have been merged by create hooks.// e.g. for directives that uses the "inserted" hook.const insert = ancestor.data.hook.insertif (insert.merged) {// start at index 1 to avoid re-invoking component mounted hookfor (let i = 1; i < insert.fns.length; i++) {insert.fns[i]()}}} else {registerRef(ancestor)}// 更新父节点ancestor = ancestor.parent}}// 如果旧节点还存在,就删掉旧节点if (isDef(parentElm)) {removeVnodes([oldVnode], 0, 0)} else if (isDef(oldVnode.tag)) {// 否则直接卸载 oldVnodeinvokeDestroyHook(oldVnode)}}}// 执行 虚拟 dom 的 insert 钩子函数invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)// 返回最新 vnode 的 elm ,也就是真实的 dom节点return vnode.elm}

核心是这句:

// 当老节点不是真实的DOM节点,并且新老节点的type和key相同,进行patchVnode更新工作
if (!isRealElement && sameVnode(oldVnode, vnode)) {patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
}
patchVnode函数

前面所作的事情:将oldNode节点上的事件全都挂载到新的上处理
从if (isDef(data) && isPatchable(vnode)) 这句开始就比较重要了

function patchVnode(oldVnode, // 老的虚拟 DOM 节点vnode, // 新节点insertedVnodeQueue, // 插入节点队列ownerArray, // 节点数组index, // 当前节点的下标removeOnly) {// 新老节点对比地址一样,直接跳过if (oldVnode === vnode) {return}if (isDef(vnode.elm) && isDef(ownerArray)) {// clone reused vnodevnode = ownerArray[index] = cloneVNode(vnode)}const elm = vnode.elm = oldVnode.elm// 如果当前节点是注释或 v-if 的,或者是异步函数,就跳过检查异步组件if (isTrue(oldVnode.isAsyncPlaceholder)) {if (isDef(vnode.asyncFactory.resolved)) {hydrate(oldVnode.elm, vnode, insertedVnodeQueue)} else {vnode.isAsyncPlaceholder = true}return}// 当前节点是静态节点的时候,key 也一样,或者有 v-once 的时候,就直接赋值返回if (isTrue(vnode.isStatic) &&isTrue(oldVnode.isStatic) &&vnode.key === oldVnode.key &&(isTrue(vnode.isCloned) || isTrue(vnode.isOnce))) {vnode.componentInstance = oldVnode.componentInstancereturn}let iconst data = vnode.dataif (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {i(oldVnode, vnode)}const oldCh = oldVnode.childrenconst ch = vnode.childrenif (isDef(data) && isPatchable(vnode)) {// 遍历调用 update 更新 oldVnode 所有属性,比如 class,style,attrs,domProps,events...// 这里的 update 钩子函数是 vnode 本身的钩子函数for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)// 这里的 update 钩子函数是我们传过来的函数if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)}// 如果新节点不是文本节点,也就是说有子节点if (isUndef(vnode.text)) {// 如果新老节点都有子节点if (isDef(oldCh) && isDef(ch)) {// 如果新老节点的子节点不一样,就执行 updateChildren 函数,对比子节点if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)} else if (isDef(ch)) {// 如果新节点有子节点的话,就是说老节点没有子节点// 如果老节点是文本节点,就是说没有子节点,就清空if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')// 添加新节点addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)} else if (isDef(oldCh)) {// 如果新节点没有子节点,老节点有子节点,就删除removeVnodes(oldCh, 0, oldCh.length - 1)} else if (isDef(oldVnode.text)) {// 如果老节点是文本节点,就清空nodeOps.setTextContent(elm, '')}} else if (oldVnode.text !== vnode.text) {// 如果老节点的文本和新节点的文本不同,就更新文本nodeOps.setTextContent(elm, vnode.text)}if (isDef(data)) {if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)}}

这里最核心的内容:是这句:

 // 如果新老节点的子节点不一样,就执行 updateChildren 函数,对比子节点
if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
updateChildren

这个过程才是真正的双端比较,双端比较的过程就是所谓的指针的判断
也是当前的子节点进行双端比较

实现思路

dom上举例:
旧的 DOM(prevChildren):

<div id="app"><ul><li key="1">Item A</li><li key="2">Item B</li><li key="3">Item C</li><li key="4">Item D</li></ul>
</div>

新的 DOM(nextChildren):

<div id="app"><ul><li key="2">Item B</li><li key="3">Item C</li><li key="4">Item D</li><li key="5">Item E</li></ul>
</div>

类比下面的a,b,c,d

第一步:
在这里插入图片描述

function vue2Diff(prevChildren, nextChildren, parent) {let oldStartIndex = 0,oldEndIndex = prevChildren.length - 1,newStartIndex = 0,newEndIndex = nextChildren.length - 1;let oldStartNode = prevChildren[oldStartIndex],oldEndNode = prevChildren[oldEndIndex],newStartNode = nextChildren[newStartIndex],newEndNode = nextChildren[newEndIndex];while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {if (oldStartNode.key === newStartNode.key) {patch(oldStartNode, newStartNode, parent)oldStartIndex++newStartIndex++oldStartNode = prevChildren[oldStartIndex]newStartNode = nextChildren[newStartIndex]} else if (oldEndNode.key === newEndNode.key) {patch(oldEndNode, newEndNode, parent)oldEndIndex--newndIndex--oldEndNode = prevChildren[oldEndIndex]newEndNode = nextChildren[newEndIndex]} else if (oldStartNode.key === newEndNode.key) {patch(oldvStartNode, newEndNode, parent)oldStartIndex++newEndIndex--oldStartNode = prevChildren[oldStartIndex]newEndNode = nextChildren[newEndIndex]} else if (oldEndNode.key === newStartNode.key) {patch(oldEndNode, newStartNode, parent)oldEndIndex--newStartIndex++oldEndNode = prevChildren[oldEndIndex]newStartNode = nextChildren[newStartIndex]}}
}

老新:头头,尾尾,头尾,尾头的情况
头尾一样的情况:
老的头放到新的尾
尾头一样的情况:
老的尾放到新的头上
在这里插入图片描述

第二步:移动
尾头一样的情况:
老的尾放到新的头上

在这里插入图片描述

function vue2Diff(prevChildren, nextChildren, parent) {// ...while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {if (oldStartNode.key === newStartNode.key) {// ...} else if (oldEndNode.key === newEndNode.key) {// ...} else if (oldStartNode.key === newEndNode.key) {// ...} else if (oldEndNode.key === newStartNode.key) {patch(oldEndNode, newStartNode, parent)// 移动到旧列表头节点之前parent.insertBefore(oldEndNode.el, oldStartNode.el)oldEndIndex--newStartIndex++oldEndNode = prevChildren[oldEndIndex]newStartNode = nextChildren[newStartIndex]}}
}

第三步:移动
头尾一样的情况:
老的头放到新的尾
在这里插入图片描述

function vue2Diff(prevChildren, nextChildren, parent) {// ...while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {if (oldStartNode.key === newStartNode.key) {// ...} else if (oldEndNode.key === newEndNode.key) {// ...} else if (oldStartNode.key === newEndNode.key) {patch(oldStartNode, newEndNode, parent)parent.insertBefore(oldStartNode.el, oldEndNode.el.nextSibling)oldStartIndex++newEndIndex--oldStartNode = prevChildren[oldStartIndex]newEndNode = nextChildren[newEndIndex]} else if (oldEndNode.key === newStartNode.key) {//...}}
}

第三步,如果以上情况都没匹配到,就只能是纯找了

function vue2Diff(prevChildren, nextChildren, parent) {//...while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {if (oldStartNode.key === newStartNode.key) {//...} else if (oldEndNode.key === newEndNode.key) {//...} else if (oldStartNode.key === newEndNode.key) {//...} else if (oldEndNode.key === newStartNode.key) {//...} else {// 在旧列表中找到 和新列表头节点key 相同的节点let newKey = newStartNode.key,oldIndex = prevChildren.findIndex(child => child.key === newKey);}}
}

找到的情况:
在这里插入图片描述

function vue2Diff(prevChildren, nextChildren, parent) {//...while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {if (oldStartNode.key === newStartNode.key) {//...} else if (oldEndNode.key === newEndNode.key) {//...} else if (oldStartNode.key === newEndNode.key) {//...} else if (oldEndNode.key === newStartNode.key) {//...} else {// 在旧列表中找到 和新列表头节点key 相同的节点let newtKey = newStartNode.key,oldIndex = prevChildren.findIndex(child => child.key === newKey);if (oldIndex > -1) {//找到了情况let oldNode = prevChildren[oldIndex];patch(oldNode, newStartNode, parent)parent.insertBefore(oldNode.el, oldStartNode.el)prevChildren[oldIndex] = undefined}newStartNode = nextChildren[++newStartIndex]}}
}

没有找到,就创建一个新的:
在这里插入图片描述

function vue2Diff(prevChildren, nextChildren, parent) {//...while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {if (oldStartNode.key === newStartNode.key) {//...} else if (oldEndNode.key === newEndNode.key) {//...} else if (oldStartNode.key === newEndNode.key) {//...} else if (oldEndNode.key === newStartNode.key) {//...} else {// 在旧列表中找到 和新列表头节点key 相同的节点let newtKey = newStartNode.key,oldIndex = prevChildren.findIndex(child => child.key === newKey);if (oldIndex > -1) {let oldNode = prevChildren[oldIndex];patch(oldNode, newStartNode, parent)parent.insertBefore(oldNode.el, oldStartNode.el)prevChildren[oldIndex] = undefined} else {mount(newStartNode, parent, oldStartNode.el)}newStartNode = nextChildren[++newStartIndex]}}
}

最后当旧列表遍历到undefind时就跳过当前节点。

function vue2Diff(prevChildren, nextChildren, parent) {//...while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {if (oldStartNode === undefind) {oldStartNode = prevChildren[++oldStartIndex]} else if (oldEndNode === undefind) {oldEndNode = prevChildren[--oldEndIndex]} else if (oldStartNode.key === newStartNode.key) {//...} else if (oldEndNode.key === newEndNode.key) {//...} else if (oldStartNode.key === newEndNode.key) {//...} else if (oldEndNode.key === newStartNode.key) {//...} else {// ...}}
}

Vue3最长递增子序列

  1. 非全量diff
  2. 静态标记 vnode static
    在vue3创建vnode节点过程中,会加入static这个值,当dom不变,当内容节点不更新的时候,我们这个值的属性也不会更新

举例:
Hello World
hei World
相同的那部分去掉
=>
头往后走
llo World
i World
=>
尾往前走
llo
i

function vue3Diff(prevChildren, nextChildren, parent) {let j = 0,prevEnd = prevChildren.length - 1,nextEnd = nextChildren.length - 1,prevNode = prevChildren[j],nextNode = nextChildren[j];while (prevNode.key === nextNode.key) {patch(prevNode, nextNode, parent)j++prevNode = prevChildren[j]nextNode = nextChildren[j]}prevNode = prevChildren[prevEnd]nextNode = prevChildren[nextEnd]while (prevNode.key === nextNode.key) {patch(prevNode, nextNode, parent)prevEnd--nextEnd--prevNode = prevChildren[prevEnd]nextNode = prevChildren[nextEnd]}
}

A B C D
D B A C

  1. 最长递增子序列是:A C,那么保证A和C位置不变
  2. D在老的最后一位,新的第一位,将D从最后一位挪到第一位
  3. 在新的时候,B在A前,就insertBefore将B注入到A之前

updateChildren这里不一样,其他点都一样。

http://www.dtcms.com/wzjs/368414.html

相关文章:

  • 电脑网页打不开怎么回事杭州上城区抖音seo有多好
  • 网站编辑培训学校seo课程总结
  • 通化网站建设口碑营销的概念是什么
  • 手机网站推荐大全seo网站推广批发
  • 自己做的网站怎么接数据库郑州网站建设推广有限公司
  • 信息化和网站建设管理工作情况江苏短视频seo搜索
  • 代理注册公司怎么找seo技术培训海南
  • 中国工信部网站备案交换友情链接的渠道
  • 正宗营销型网站建设seo搜索引擎优化知乎
  • 企业为什么做网站优化推广流量点击推广平台
  • 深圳购物网站建设价格网络营销制度课完整版
  • 如何做一条动态网站推广app大全
  • 出名的网站建设软件云巅seo
  • 昆明做网站做的好的公司信息推广的方式有哪些
  • 安徽飞亚建设网站怎么在网上推销产品
  • 做游戏特效的网站重庆seo整站优化报价
  • 学校的网站的代码模板在线代理浏览网址
  • 网站编程赚钱收录平台
  • 专科医院网站建设培训学校机构有哪些
  • 舟山建设管理网站百度推广代理公司哪家好
  • 同一个域名可以做几个网站吗迅雷下载磁力天堂
  • 做喜报的网站比较靠谱的电商培训机构
  • 郑州企业网站排名优化公司自己怎么做引流推广
  • 柳州企业网站建设价格免费网络推广的方法
  • 秀设计网站今天特大新闻
  • 免费建立com网站app推广方案范例
  • 平面设计网站模板杭州关键词排名系统
  • 一个网站多个子域名优化怎么申请一个网站
  • 营销型网站建设的定义南宁百度关键词排名公司
  • 东平房产网珠海seo排名收费