• 深度剖析 Vue3 如何通过虚拟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:

    hello world

    • 1
    • 2

    上面的 template 会解析成下面的函数,最终返回一个 JavaScript 的对象能够描述这段HTML:

    function render(){return h('div',{id:"app"},children:[h('p',{},'hello world'),h(Rate,{value:4}),])
    } 
    
    • 1
    • 2

    知道虚拟 DOM 是什么之后,那么它是怎么创建的呢?


    DOM 的创建

    我们简单回忆上一讲介绍的 mount 函数,在代码中,我们使用 createVNode 函数创建项目的虚拟 DOM,可以看到 Vue 内部的虚拟 DOM,也就是 vnode,就是一个对象,通过 type、props、children 等属性描述整个节点

    const vnode = createVNode( (rootComponent as ConcreteComponent,rootProps
    )
    function _createVNode() {// 处理属性和 classif (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 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    createVNode 负责创建 Vue 中的虚拟 DOM,而上一讲中我们讲过 mount 函数的核心逻辑就是使用 setupComponent 执行我们写的