您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

怎样使用vue中diff算法

2024/3/28 7:14:05发布26次查看
这次给大家带来怎样使用vue中diff算法,使用vue中diff算法的注意事项有哪些,下面就是实战案例,一起来看一下。
虚拟dom
diff算法首先要明确一个概念就是diff的对象是虚拟dom,更新真实dom则是diff算法的结果
vnode基类
constructor (  。。。 ) {  this.tag = tag  this.data = data  this.children = children  this.text = text  this.elm = elm  this.ns = undefined  this.context = context  this.fncontext = undefined  this.fnoptions = undefined  this.fnscopeid = undefined  this.key = data && data.key  this.componentoptions = componentoptions  this.componentinstance = undefined  this.parent = undefined  this.raw = false  this.isstatic = false  this.isrootinsert = true  this.iscomment = false  this.iscloned = false  this.isonce = false  this.asyncfactory = asyncfactory  this.asyncmeta = undefined  this.isasyncplaceholder = false }
这个部分的代码 主要是为了更好地知道在diff算法中具体diff的属性的含义,当然也可以更好地了解vnode实例
整体过程
核心函数是patch函数
isundef判断(是不是undefined或者null)
// empty mount (likely as component), create new root elementcreateelm(vnode, insertedvnodequeue) 这里可以发现创建节点不是一个一个插入,而是放入一个队列中统一批处理
核心函数samevnode
function samevnode (a, b) { return (  a.key === b.key && (   (    a.tag === b.tag &&    a.iscomment === b.iscomment &&    isdef(a.data) === isdef(b.data) &&    sameinputtype(a, b)   ) || (    istrue(a.isasyncplaceholder) &&    a.asyncfactory === b.asyncfactory &&    isundef(b.asyncfactory.error)   )  ) )}
这里是一个外层的比较函数,直接去比较了两个节点的key,tag(标签),data的比较(注意这里的data指的是vnodedata),input的话直接比较type。
export interface vnodedata { key?: string | number; slot?: string; scopedslots?: { [key: string]: scopedslot }; ref?: string; tag?: string; staticclass?: string; class?: any; staticstyle?: { [key: string]: any }; style?: object[] | object; props?: { [key: string]: any }; attrs?: { [key: string]: any }; domprops?: { [key: string]: any }; hook?: { [key: string]: function }; on?: { [key: string]: function | function[] }; nativeon?: { [key: string]: function | function[] }; transition?: object; show?: boolean; inlinetemplate?: {  render: function;  staticrenderfns: function[]; }; directives?: vnodedirective[]; keepalive?: boolean;}
这会确认两个节点是否有进一步比较的价值,不然直接替换
替换的过程主要是一个createelm函数 另外则是销毁oldvnode
// destroy old node    if (isdef(parentelm)) {     removevnodes(parentelm, [oldvnode], 0, 0)    } else if (isdef(oldvnode.tag)) {     invokedestroyhook(oldvnode)    }
插入过程简化来说就是判断node的type分别调用
createcomponent(会判断是否有children然后递归调用)
createcomment
createtextnode
创建后使用insert函数
之后需要用hydrate函数将虚拟dom和真是dom进行映射
function insert (parent, elm, ref) {  if (isdef(parent)) {   if (isdef(ref)) {    if (ref.parentnode === parent) {     nodeops.insertbefore(parent, elm, ref)    }   } else {    nodeops.appendchild(parent, elm)   }  } }
核心函数
function patchvnode (oldvnode, vnode, insertedvnodequeue, removeonly) {  if (oldvnode === vnode) {   return  }  const elm = vnode.elm = oldvnode.elm  if (istrue(oldvnode.isasyncplaceholder)) {   if (isdef(vnode.asyncfactory.resolved)) {    hydrate(oldvnode.elm, vnode, insertedvnodequeue)   } else {    vnode.isasyncplaceholder = true   }   return  }  if (istrue(vnode.isstatic) &&   istrue(oldvnode.isstatic) &&   vnode.key === oldvnode.key &&   (istrue(vnode.iscloned) || istrue(vnode.isonce))  ) {   vnode.componentinstance = oldvnode.componentinstance   return  }  let i  const data = vnode.data  if (isdef(data) && isdef(i = data.hook) && isdef(i = i.prepatch)) {   i(oldvnode, vnode)  }  const oldch = oldvnode.children  const ch = vnode.children  if (isdef(data) && ispatchable(vnode)) {   for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldvnode, vnode) if (isdef(i = data.hook) && isdef(i = i.update)) i(oldvnode, vnode) } if (isundef(vnode.text)) { if (isdef(oldch) && isdef(ch)) { 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(elm, 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) } }
const el = vnode.el = oldvnode.el 这是很重要的一步,让vnode.el引用到现在的真实dom,当el修改时,vnode.el会同步变化。
比较二者引用是否一致
之后asyncfactory不知道是做什么的,所以这个比较看不懂
静态节点比较key,相同后也不做重新渲染,直接拷贝componentinstance(once命令在此生效)
如果vnode是文本节点或注释节点,但是vnode.text != oldvnode.text时,只需要更新vnode.elm的文本内容就可以
children的比较
如果只有oldvnode有子节点,那就把这些节点都删除
如果只有vnode有子节点,那就创建这些子节点,这里如果oldvnode是个文本节点就把vnode.elm的文本设置为空字符串
都有则updatechildren,这个之后详述
如果oldvnode和vnode都没有子节点,但是oldvnode是文本节点或注释节点,就把vnode.elm的文本设置为空字符串
updatechildren
这部分重点还是关注整个算法
首先四个指针,oldstart,oldend,newstart,newend,两个数组,oldvnode,vnode。
function updatechildren (parentelm, oldch, newch, insertedvnodequeue, removeonly) { let oldstartidx = 0 let newstartidx = 0 let oldendidx = oldch.length - 1 let oldstartvnode = oldch[0] let oldendvnode = oldch[oldendidx] let newendidx = newch.length - 1 let newstartvnode = newch[0] let newendvnode = newch[newendidx] let oldkeytoidx, idxinold, vnodetomove, refelm while (oldstartidx <= oldendidx && newstartidx <= newendidx) { if (isundef(oldstartvnode)) { oldstartvnode = oldch[++oldstartidx] // vnode has been moved left } else if (isundef(oldendvnode)) { oldendvnode = oldch[--oldendidx] } else if (samevnode(oldstartvnode, newstartvnode)) { patchvnode(oldstartvnode, newstartvnode, insertedvnodequeue) oldstartvnode = oldch[++oldstartidx] newstartvnode = newch[++newstartidx] } else if (samevnode(oldendvnode, newendvnode)) { patchvnode(oldendvnode, newendvnode, insertedvnodequeue) oldendvnode = oldch[--oldendidx] newendvnode = newch[--newendidx] } else if (samevnode(oldstartvnode, newendvnode)) { // vnode moved right patchvnode(oldstartvnode, newendvnode, insertedvnodequeue) canmove && nodeops.insertbefore(parentelm, oldstartvnode.elm, nodeops.nextsibling(oldendvnode.elm)) oldstartvnode = oldch[++oldstartidx] newendvnode = newch[--newendidx] } else if (samevnode(oldendvnode, newstartvnode)) { // vnode moved left patchvnode(oldendvnode, newstartvnode, insertedvnodequeue) canmove && nodeops.insertbefore(parentelm, oldendvnode.elm, oldstartvnode.elm) oldendvnode = oldch[--oldendidx] newstartvnode = newch[++newstartidx] } else { if (isundef(oldkeytoidx)) oldkeytoidx = createkeytooldidx(oldch, oldstartidx, oldendidx) idxinold = isdef(newstartvnode.key) ? oldkeytoidx[newstartvnode.key] : findidxinold(newstartvnode, oldch, oldstartidx, oldendidx) if (isundef(idxinold)) { // new element createelm(newstartvnode, insertedvnodequeue, parentelm, oldstartvnode.elm, false, newch, newstartidx) } else { vnodetomove = oldch[idxinold] if (samevnode(vnodetomove, newstartvnode)) { patchvnode(vnodetomove, newstartvnode, insertedvnodequeue) oldch[idxinold] = undefined canmove && nodeops.insertbefore(parentelm, vnodetomove.elm, oldstartvnode.elm) } else { // same key but different element. treat as new element createelm(newstartvnode, insertedvnodequeue, parentelm, oldstartvnode.elm, false, newch, newstartidx) } } newstartvnode = newch[++newstartidx] } } if (oldstartidx > oldendidx) {   refelm = isundef(newch[newendidx + 1]) ? null : newch[newendidx + 1].elm   addvnodes(parentelm, refelm, newch, newstartidx, newendidx, insertedvnodequeue)  } else if (newstartidx > newendidx) {   removevnodes(parentelm, oldch, oldstartidx, oldendidx)  } }
一个循环比较的几种情况和处理(以下的++ --均指index的++ --)比较则是比较的node节点,简略写法 不严谨 比较用的是samevnode函数也不是真的全等
整体循环不结束的条件oldstartidx <= oldendidx && newstartidx <= newendidx
oldstart === newstart,oldstart++ newstart++
oldend === newend,oldend-- newend--
oldstart === newend, oldstart插到队伍末尾 oldstart++ newend--
oldend === newstart, oldend插到队伍开头 oldend-- newstart++
剩下的所有情况都走这个处理简单的说也就两种处理,处理后newstart++
newstart在old中发现一样的那么将这个移动到oldstart前
没有发现一样的那么创建一个放到oldstart之前
循环结束后并没有完成
还有一段判断才算完
if (oldstartidx > oldendidx) {   refelm = isundef(newch[newendidx + 1]) ? null : newch[newendidx + 1].elm   addvnodes(parentelm, refelm, newch, newstartidx, newendidx, insertedvnodequeue)  } else if (newstartidx > newendidx) {   removevnodes(parentelm, oldch, oldstartidx, oldendidx)  }
简单的说就是循环结束后,看四个指针中间的内容,old数组中和new数组中,多退少补而已
相信看了本文案例你已经掌握了方法,更多精彩请关注其它相关文章!
推荐阅读:
微信小程序开发中怎样实现搜索内容高亮功能
如何在项目中使用js中存储键值
以上就是怎样使用vue中diff算法的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product