小程序三方平台开发: 解析小程序开发的未来趋势和机遇
284
2024-06-11
这次给大家带来diff的对象是虚拟dom,使用diff虚拟dom对象的注意事项有哪些,下面就是实战案例,一起来看一下。
虚拟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 <blockquote style="text-align: left;"><p style="text-align: left;">const el = vnode.el = oldVnode.el 这是很重要的一步,让vnode.el引用到现在的真实dom,当el修改时,vnode.el会同步变化。</p></blockquote><ol class=" list-paddingleft-2">
<li><p style="text-align: left;">比较二者引用是否一致</p></li>
<li><p style="text-align: left;">之后asyncFactory不知道是做什么的,所以这个比较看不懂</p></li>
<li><p style="text-align: left;"><a href="http://www.php-/wiki/188.html" target="_blank">静态</a>节点比较key,相同后也不做重新渲染,直接拷贝componentInstance(once命令在此生效)</p></li>
<li><p style="text-align: left;">如果vnode是文本节点或注释节点,但是vnode.text != oldVnode.text时,只需要更新vnode.elm的文本内容就可以</p></li>
<li><p style="text-align: left;">children的比较</p></li>
</ol>
如果只有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) {
refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
} else if (newStartIdx >&nb
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~