Vue3.0 - 02 - 01

93 阅读1分钟

render 渲染

先从首次渲染来初探 render 函数。我们在执行 createApp(App).mount('#app') 时,实在上是执行了 createApp 返回的 app 对象中的 mount 函数。同时,我们也知道 mount 函数,挂载组件的时候执行了 render(vnode, rootContainer, isSVG) 方法。


export function createAppAPI<HostElement>(

    render: RootRenderFunction,
    
    hydrate?: RootHydrateFunction
    
): CreateAppFunction<HostElement> {

    return function createApp(rootComponent, rootProps = null) {
    
        // ...
        
        const app: App = (context.app = {
        
        // ...
        
        mount(
        
            rootContainer: HostElement,
            
            isHydrate?: boolean,
            
            isSVG?: boolean
            
        ): any {
        
            if (!isMounted) {
            
                // ...
                
                if (isHydrate && hydrate) {
                
                    // ssr 渲染
                    
                } else {
                
                    // 普通渲染
                    
                    render(vnode, rootContainer, isSVG)
                    
                }
                
            // ...
            
            } else if (__DEV__) {
            
                // ...
                
            }
            
        },
        
        // ...
        
        )
        
        // ...
        
        // app 对象本体
        
        return app
        
    }

}

render


const render: RootRenderFunction = (vnode, container, isSVG) => {

    if (vnode == null) {
    
        // ...
        
    } else {
    
        // 由于是首次渲染,执行 patch 函数
        
        patch(container._vnode || null, vnode, container, null, null, null, isSVG)
        
    }
    
    // ...
    
}

patch


const patch: PatchFn = (

    n1,

    n2,

    container,

    anchor = null,

    parentComponent = null,

    parentSuspense = null,

    isSVG = false,

    slotScopeIds = null,

    optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren

) => {

    // ...

    const { type, ref, shapeFlag } = n2

    switch (type) {

        // ...

        default:
            if (shapeFlag & ShapeFlags.ELEMENT) {
                /* ... */
            } else if (shapeFlag & ShapeFlags.COMPONENT) {
                processComponent(
                    n1,
                    n2,
                    container,
                    anchor,
                    parentComponent,
                    parentSuspense,
                    isSVG,
                    slotScopeIds,
                    optimized

                )
            } else if (shapeFlag & ShapeFlags.TELEPORT) {
                /* ... */
            } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
                /* ... */
            } else if (__DEV__) {/* ... */}

}

patch 函数会对 vnode 节点(新旧)进行判断,并根据 shapeFlag 调用不同的函数来处理。

Vue3.0 中在 vnode 中新增了 shapeFlag 属性,主要用来定义描述组件的分类


export const enum ShapeFlags {

    ELEMENT = 1, // HTML 或 SVG 标签 普通 DOM 元素

    FUNCTIONAL_COMPONENT = 1 << 1, // 函数式组件

    STATEFUL_COMPONENT = 1 << 2, // 普通有状态组件

    TEXT_CHILDREN = 1 << 3, // 子节点为纯文本

    ARRAY_CHILDREN = 1 << 4, // 子节点是数组

    SLOTS_CHILDREN = 1 << 5, // 子节点是插槽

    TELEPORT = 1 << 6, // Teleport

    SUSPENSE = 1 << 7, // Supspense

    COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, // 需要被keep-live的有状态组件

    COMPONENT_KEPT_ALIVE = 1 << 9, //已经被keep-live的有状态组件

    COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT // 有状态组件和函数组件都是组件,用component表示

}

processComponent


const processComponent = (

    n1: VNode | null,

    n2: VNode,

    container: RendererElement,

    anchor: RendererNode | null,

    parentComponent: ComponentInternalInstance | null,

    parentSuspense: SuspenseBoundary | null,

    isSVG: boolean,

    slotScopeIds: string[] | null,

    optimized: boolean

) => {

    n2.slotScopeIds = slotScopeIds

    // 判断 n1(旧节点)是否为空,来决定是挂载还是更新

    if (n1 == null) {

        if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {

            // keep-alive 组件处理

        } else {

            // 挂载组件

            mountComponent(

                n2,

                container,

                anchor,

                parentComponent,

                parentSuspense,

                isSVG,

                optimized

            )

        }

    } else {

        // 更新组件

        updateComponent(n1, n2, optimized)

    }

}

mountComponent


const mountComponent: MountComponentFn = (

    initialVNode,

    container,

    anchor,

    parentComponent,

    parentSuspense,

    isSVG,

    optimized

) => {

    // ...

    // 创建组件实例

    const instance: ComponentInternalInstance =

        compatMountInstance || (initialVNode.component = createComponentInstance(

            initialVNode,

            parentComponent,

            parentSuspense
          ))

    // ...

    if (!(__COMPAT__ && compatMountInstance)) {

        // ...

        // 初始化组件

        setupComponent(instance)

        // ...

    }

    // ...

    // 渲染组件

    setupRenderEffect(

        instance,

        initialVNode,

        container,

        anchor,

        parentSuspense,

        isSVG,

        optimized

    )

    // ...

}

可以看出 mountComponent 主要做了三件事:

  1. 创建组件实例 - createComponentInstance;

  2. 初始化组件 - setupComponent;

  3. 渲染组件 - setupRenderEffect。