源码看vue中对标签和组件渲染区别
最近一有时间还是会研究研究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 } } } 复制代码
最后,在网上找到一张关于父子组件渲染关系图
作者:湾里晴空
链接:https://juejin.cn/post/7020040347870822408