Vue 源码阅读四:虚拟 DOM 是如何渲染成真实的 DOM 的?(上)
2023-12-20 10:59:49
前面用了两篇文章,讲虚拟 DOM 是如何生成的。终于到了如何将虚拟 DOM 渲染成真实 DOM 的部分了。
vm._render()
,我们已经知道是如何生成虚拟 DOM 的了。接下来,我们看看vm._update
是如何将虚拟 DOM 渲染成真实的 DOM的。
export function updateComponent(vm) {
vm._update(vm._render())
}
export function setComponentProps (vm, propsData, children, slots) {
vm.$options._renderChildren = children
vm.$slots = slots
vm._attrs = propsData.attrs
vm._props = propsData.props
vm._update(vm._render(), true)
}
我们可以看到,vm._update
方法有两个参数,第一个参数是虚拟 DOM,第二个参数是一个布尔值,表示是否需要强制更新。
如果第二个参数为 true
,则无论虚拟 DOM 是否发生变化,都会强制更新真实 DOM。
export function patch(oldVnode, vnode, hydrating, removeOnly) {
if (isUndef(vnode)) {
if (isDef(oldVnode)) { invokeDestroyHook(oldVnode) }
return
}
let isInitialPatch = false
const insertedVnodeQueue = []
const removedVnodeQueue = []
if (isUndef(oldVnode)) {
// empty mount (likely as component), create new root element
isInitialPatch = true
createElm(vnode, insertedVnodeQueue)
} else {
const isRealElement = isDef(oldVnode.nodeType)
if (!isRealElement && sameVnode(oldVnode, vnode)) {
// patch existing root node
patchVnode(oldVnode, vnode, insertedVnodeQueue, removedVnodeQueue, hydrating, false, false)
} else {
if (isRealElement) {
// mounting to a real element
// check if this is server-rendered content and if we can perform
// a fast update by simply replacing the client-mounted DOM
// with the server-rendered DOM.
if (oldVnode.nodeType === 1 && oldVnode.hasAttribute('server-rendered')) {
oldVnode.removeAttribute('server-rendered')
hydrate(oldVnode, vnode, insertedVnodeQueue)
return
} else {
// replace old root element with new root element
oldVnode = emptyNodeAt(oldVnode)
}
}
// create new root element with vnode
createElm(vnode, insertedVnodeQueue)
}
}
removeVnodes(removedVnodeQueue, false)
if (isInitialPatch) {
fireAttachedHook(insertedVnodeQueue)
}
}
patch
函数是 Vue 中用来将虚拟 DOM 渲染成真实 DOM 的核心函数。
它有三个参数,第一个参数是旧的虚拟 DOM,第二个参数是新的虚拟 DOM,第三个参数是一个布尔值,表示是否正在进行服务端渲染。
如果第三个参数为 true
,则会使用服务端渲染的 HTML 来生成真实 DOM,否则会使用客户端渲染的 HTML 来生成真实 DOM。
patch
函数首先会检查旧的虚拟 DOM 和新的虚拟 DOM 是否相同。
如果相同,则不会更新真实 DOM。
如果不同,则会调用 patchVnode
函数来更新真实 DOM。
patchVnode
函数有七个参数,第一个参数是旧的虚拟 DOM,第二个参数是新的虚拟 DOM,第三个参数是一个数组,用来存储新插入的真实 DOM 元素,第四个参数是一个数组,用来存储被删除的真实 DOM 元素,第五个参数是一个布尔值,表示是否正在进行服务端渲染,第六个参数是一个布尔值,表示是否正在强制更新,第七个参数是一个布尔值,表示是否正在更新组件。
patchVnode
函数首先会检查旧的虚拟 DOM 和新的虚拟 DOM 的类型是否相同。
如果相同,则会更新旧的真实 DOM 元素的属性和子元素。
如果不同,则会删除旧的真实 DOM 元素,并创建一个新的真实 DOM 元素。
当 patchVnode
函数更新完所有子元素后,它会调用 removeVnodes
函数来删除被删除的真实 DOM 元素。
removeVnodes
函数有一个参数,是一个数组,用来存储被删除的真实 DOM 元素。
它会遍历数组中的每个真实 DOM 元素,并将其从 DOM 树中删除。
最后,patch
函数会调用 fireAttachedHook
函数来触发所有新插入的真实 DOM 元素的 attached
钩子函数。
attached
钩子函数会在真实 DOM 元素被插入到 DOM 树中时触发。
它可以用来执行一些操作,例如初始化组件或绑定事件监听器。