最近一有时间还是会研究研究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
}
}
}
最后,在网上找到一张关于父子组件渲染关系图