In the previous template compilation study notes, we know that the template $mount
will be compiled to generate render
a function, and then called mount.call(this, el, hydrating)
;
// src/platforms/web/entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount
// 重新定义 $mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el)
// ... 其他代码
// 调用原先 Vue 原型上 $mount 方法
return mount.call(this, el, hydrating)
}
The above mount
is $mount
the method on the Vue prototype;
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
mountComponent
It is the entry function of component instance mounting, because it is related to the life cycle, so the specific code of this method is located in the source code lifecycle
file ;
// src/core/instance/lifecycle.js
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
// 将真实的 el 赋值给实例,为后面虚拟 dom 做铺垫
vm.$el = el
// render 函数的判断
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
if (process.env.NODE_ENV !== 'production') {
if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
vm.$options.el || el) {
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.',
vm
)
} else {
warn(
'Failed to mount component: template or render function not defined.',
vm
)
}
}
}
callHook(vm, 'beforeMount')
let updateComponent
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
updateComponent = () => {
const name = vm._name
const id = vm._uid
const startTag = `vue-perf-start:${
id}`
const endTag = `vue-perf-end:${
id}`
mark(startTag)
const vnode = vm._render()
mark(endTag)
measure(`vue ${
name} render`, startTag, endTag)
mark(startTag)
vm._update(vnode, hydrating)
mark(endTag)
measure(`vue ${
name} patch`, startTag, endTag)
}
} else {
updateComponent = () => {
// 实例挂载
vm._update(vm._render(), hydrating)
}
}
// 创建一个渲染 watcher 实例,主要执行 updateComponent 的方法
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
hydrating = false
// mounted 钩子函数只执行一次
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}
From the above code, we can see that mountComponent
the method mainly el
assigns to the instance, and then vm._update(vm._render(), hydrating)
mounts the instance by calling , and we then look at _update
and _render
;
// src/core/instance/render.js
export function initRender (vm: Component) {
// ... 其他代码
// 主要参数: tag, data, children
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
}
export function renderMixin (Vue: Class<Component>) {
installRenderHelpers(Vue.prototype)
Vue.prototype.$nextTick = function (fn: Function) {
return nextTick(fn, this)
}
Vue.prototype._render = function (): VNode {
const vm: Component = this
// 获取 render 函数
const {
render, _parentVnode } = vm.$options
if (_parentVnode) {
vm.$scopedSlots = normalizeScopedSlots(
_parentVnode.data.scopedSlots,
vm.$slots,
vm.$scopedSlots
)
}
vm.$vnode = _parentVnode
let vnode
try {
currentRenderingInstance = vm
// 执行 render 函数生成 vnode
vnode = render.call(vm._renderProxy, vm.$createElement)
} catch (e) {
handleError(e, vm, `render`)
if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {
try {
vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
} catch (e) {
handleError(e, vm, `renderError`)
vnode = vm._vnode
}
} else {
vnode = vm._vnode
}
} finally {
currentRenderingInstance = null
}
// ... 其他代码
return vnode
}
}
_render
The function is render
actually vnode
the core of converting the function to , which is generated by executing render
the function vnode
;
// src/core/instance/lifecycle.js
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) {
// 初始化渲染调用
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
// 更新时调用
vm.$el = vm.__patch__(prevVnode, vnode)
}
restoreActiveInstance()
if (prevEl) {
prevEl.__vue__ = null
}
if (vm.$el) {
vm.$el.__vue__ = vm
}
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
vm.$parent.$el = vm.$el
}
}
_update
It is the core method to transform virtual DOM into real DOM. It mainly uses patch
the method to create real DOM. Only the parameters passed in are different when initializing rendering and updating. patch
The method can be moved to another study note ;
Summarize:
The process of initial rendering is executed after the template is compiled mountComponent
, mountComponent
mainly through vm._update(vm._render(), hydrating)
the method to mount the instance, and _render
the method is to execute render
the function to generate vnode
, _update
the method is to convert the generated virtual DOM into a real DOM, the main core is patch
the method ;