在之前的分析中,我们知道在完全版本的 Vue 中,最终会通过编译生成代码字符串,并且将字符串生成了函数。
...
res.render = createFunction(compiled.render, fnGenErrors)
....
function createFunction (code, errors) {
try {
return new Function(code)
} catch (err) {
errors.push({ err, code })
return noop
}
}
那为什么生成了代码字符串就能有真实 DOM 出呢?可能会想到因为调用了 mounted 方法,那么 mounted 方法内部又是如何生成 DOM 的呢?
找到 monte的实现:
/* @flow */
import Vue from 'core/index'
import { mountComponent } from 'core/instance/lifecycle'
import { inBrowser } from 'core/util/index'
import { patch } from './patch'
// 给全局挂在 patch 方法
// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop
// public mount method
// 挂在 mount 方法
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
export default Vue
- 给Vue 实例挂载了__patch__方法
- 挂载了$mount,并且初始化了组件
mountComponent 方法
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
}
callHook(vm, 'beforeMount')
let updateComponent
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
updateComponent = () => {
...
const vnode = vm._render()
mark(endTag)
measure(`vue ${name} render`, startTag, endTag)
mark(startTag)
vm._update(vnode, hydrating)
}
} else {
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
}
...
return vm
}
都调用了 update方法,而 update 方法,便是__patch__的过程。
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
vm._vnode = vnode
...
if (!prevVnode) {
// initial render
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
// updates
vm.$el = vm.__patch__(prevVnode, vnode)
}
...
}
patch 的过程会创建元素,底层会调用浏览器的creatElement,creatText 等方法去生成标签。
应该注意,这是一个初始化的过程,并不涉及到 old Vnode,所以是以现有的Vnode 去生成真实 Dom