源码看vue中对标签和组件渲染区别

346 阅读2分钟

最近一有时间还是会研究研究vue源码,虽然现在还有很多地方不懂,但是还是要把搞懂的记录下,今天主要写写vue中对标签和对组件渲染的区别

runtime-only原理

区别于runtime-compiler模式(见我上一篇文档),runtime-only模式基于webpack中vue-loader生成的render函数,直接走 render => vdome => patch流程

plantform/runtime/index

    Vue.prototype.__patch__ = inBrowser ? patch : noop  // 定义在浏览器环境下patch

    Vue.prototype.$mount = function ( // 在runtimeonly版本中并没有用到
        el?: string | Element,
        hydrating?: boolean
   ): Component {
        el = el && inBrowser ? query(el) : undefined
        return mountComponent(this, el, hydrating)  //  
}
    

我们一般在main.js中写的$mount方法即是上面的方法

new Vue({
render: h => h(App)
}).$mount('#app')

继续看下mountComponent,其在core/instance/lifecycle.js,在这里先基于render生成vnode, 然后在update方法中进行patch

    export function mountComponent (
        vm: Component,
        el: ?Element,
        hydrating?: boolean
    ): Component {
        vm.$el = el
         updateComponent = () => {
        const vnode = vm._render() // 这里调用createElement方法,生产vdom
        vm._update(vnode, hydrating) // 这里将vdom对象渲染到页面

}

render函数

core/instance/render.js中,render方法实质上是是调用了createElement方法,在该方法中,假如识别是组件,则调用createComponent实现转vnode

对于createComponent方法

export function createComponent (
    Ctor: Class<Component> | Function | Object | void,
    data: ?VNodeData,
    context: Component,
    children: ?Array<VNode>,
    tag?: string
): VNode | Array<VNode> | void {
    if (isUndef(Ctor)) {
    return
}
const baseCtor = context.$options._base
// 基于Vue创建子组件构造器,baseCtor即是Vue
if (isObject(Ctor)) {
    Ctor = baseCtor.extend(Ctor)
}
// 组装组件钩子
installComponentHooks(data)
// 生成组件对应的vnode
const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
    data, undefined, undefined, undefined, context,
    { Ctor, propsData, listeners, tag, children },
    asyncFactory
)

return vnode

createComponent做了三件事,生成构造器;组装组件钩子;生产对应的vnode

_update方法

    Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
        const vm: Component = this
        const prevEl = vm.$el
        const prevVnode = vm._vnode
        const restoreActiveInstance = setActiveInstance(vm)
        vm._vnode = vnode
        if (!prevVnode) {
                // 基于patch实现
              vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
        vm.$el = vm.__patch__(prevVnode, vnode)
}

patch方法在core/vdom/patch.js中,基于createPatchFunction方法返回patch方法

其中patch方法,主要由createElm实现

    if (isDef(vnode.elm) && isDef(ownerArray)) {
    vnode = ownerArray[index] = cloneVNode(vnode)
}
    vnode.isRootInsert = !nested // for transition enter check
    // 假如是组件,则返回true,进入createComponent中
    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
        return
    }
    // 后续对非组件元素进行patch
    ...
 }

对于在patch中的createComponent

    function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
        let i = vnode.data
        if (isDef(i)) {
            const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
            if (isDef(i = i.hook) && isDef(i = i.init)) {
                // 执行组件init钩子,
                i(vnode, false /* hydrating */)}
            if (isDef(vnode.componentInstance)) {
                // 初始化子组件
                initComponent(vnode, insertedVnodeQueue)
                // 将自组件插入父组件中
                insert(parentElm, vnode.elm, refElm)
                if (isTrue(isReactivated)) {
                        reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
                   }

        return true
}
}
}
    

最后,在网上找到一张关于父子组件渲染关系图

image.png