You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
20 KiB
20 KiB
30|虚拟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执行我们写的