|虚拟DOM(上):如何通过虚拟DOM更新页面?

你好,我是大圣。
上一讲我们主要介绍了Vue项目的首次渲染流程,在mountComponent中注册了effect函数,这样,在组件数据有更新的时候,就会通知到组件的update方法进行更新。
Vue中组件更新的方式也是使用了响应式+虚拟DOM的方式,这个我们在第一讲中有介绍过Vue 1、Vue 2和Vue 3中更新方式的变化,今天我们就来详细剖析一下Vue组件内部如何通过虚拟DOM更新页面的代码细节。
Vue虚拟DOM执行流程
我们从虚拟DOM在Vue的执行流程开始讲起。在Vue中,我们使用虚拟DOM来描述页面的组件,比如下面的template虽然格式和HTML很像,但是在Vue的内部会解析成JavaScript函数,这个函数就是用来返回虚拟DOM:
< div id = "app" >
<
p > hello world < /p> <
Rate: value = "4" > < /Rate> <
/div>
上面的template会解析成下面的函数,最终返回一个JavaScript的对象能够描述这段HTML:
function render() {
return h('div', {
id: "app"
}, children: [
h('p', {}, 'hello world'),
h(Rate, {
value: 4
}),
])
}
知道虚拟DOM是什么之后,那么它是怎么创建的呢?
DOM的创建
我们简单回忆上一讲介绍的mount函数,在代码中,我们使用createVNode函数创建项目的虚拟DOM,可以看到Vue内部的虚拟DOM,也就是vnode,就是一个对象,通过type、props、children等属性描述整个节点:
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
function\ _createVNode() {
// 处理属性和class
if (props) {
...
}
// 标记vnode信息
const shapeFlag = isString(type) ?
ShapeFlags.ELEMENT :
\_\ _FEATURE\ _SUSPENSE\ _\ _ && isSuspense(type) ?
ShapeFlags.SUSPENSE :
isTeleport(type) ?
ShapeFlags.TELEPORT :
isObject(type) ?
ShapeFlags.STATEFUL\ _COMPONENT :
isFunction(type) ?
ShapeFlags.FUNCTIONAL\ _COMPONENT :
0
return createBaseVNode(
type,
props,
children,
patchFlag,
dynamicProps,
shapeFlag,
isBlockNode,
true
)
}
function createBaseVNode(type, props, children, ...) {
const vnode = {
type,
props,
key: props && normalizeKey(props),
ref: props && normalizeRef(props),
children,
shapeFlag,
patchFlag,
dynamicProps,
...
}
as VNode
// 标准化子节点
if (needFullChildrenNormalization) {
normalizeChildren(vnode, children)
} else if (children) {
vnode.shapeFlag |= isString(children) ?
ShapeFlags.TEXT\ _CHILDREN :
ShapeFlags.ARRAY\ _CHILDREN
}
return vnode
}
componentUpdateFn
createVNode负责创建Vue中的虚拟DOM,而上一讲中我们讲过mount函数的核心逻辑就是使用setupComponent执行我们写的