src/core/instance/lifecycle.js 中的 mountComponent
完整源码
js
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
if (process.env.NODE_ENV !== 'production') {
/* istanbul ignore if */
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
/* istanbul ignore if */
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)
}
}
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
hydrating = false
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}1. 函数定义与参数
js
export function mountComponent (
vm: Component, // Vue 组件实例
el: ?Element, // 挂载的 DOM 元素
hydrating?: boolean // 是否服务端渲染(hydration)
): Component {作用 :定义挂载组件的核心函数,处理组件实例的渲染和挂载流程
2. 初始化挂载元素
js
vm.$el = el; // 将 DOM 元素保存到组件实例的 $el 属性关键:记录组件挂载的目标 DOM 节点。
3. 检查渲染函数是否存在
js
if (!vm.$options.render) { // 如果用户未提供 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...', vm);
} else {
// 警告:未定义模板或渲染函数
warn('Failed to mount component...', vm);
}
}
}核心逻辑:
- Vue 的运行时构建(runtime-only)不包含模板编译器,若使用 template 但未预编译,会抛出警告。
- 若用户未提供 render 函数,默认生成一个空节点(createEmptyVNode)。
4. 触发 beforeMount 生命周期钩子
js
callHook(vm, 'beforeMount'); // 调用组件 beforeMount 生命周期钩子作用:通知开发者组件即将挂载。
5. 定义更新组件的函数 updateComponent
js
let updateComponent;
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
// 开发环境 + 启用性能监控时,添加性能标记
updateComponent = () => {
const name = vm._name;
const id = vm._uid;
// 记录渲染和更新的性能
mark(startTag);
const vnode = vm._render(); // 生成虚拟 DOM
mark(endTag);
measure(`vue ${name} render`, startTag, endTag);
mark(startTag);
vm._update(vnode, hydrating); // 将虚拟 DOM 更新到真实 DOM
mark(endTag);
measure(`vue ${name} patch`, startTag, endTag);
};
} else {
// 生产环境或不需要查看性能时,直接执行渲染和更新
updateComponent = () => {
vm._update(vm._render(), hydrating);
};
}关键点:
vm._render():调用渲染函数生成虚拟 DOM(vnode)。vm._update():将虚拟 DOM 转换为真实 DOM 并挂载。- 开发环境下使用 mark 和 measure 进行性能分析(需浏览器支持 Performance API)。
6. 创建渲染 Watcher
js
new Watcher(vm, updateComponent, noop, {
before() {
if (vm._isMounted) {
callHook(vm, 'beforeUpdate'); // 触发 beforeUpdate 钩子
}
}
}, true /* isRenderWatcher */);作用:
- 创建一个
Watcher实例,负责依赖收集和组件更新。 updateComponent作为Watcher的回调函数,当数据变化时触发重新渲染。isRenderWatcher: true表示这是组件的渲染Watcher(每个组件只有一个)。
7. 完成挂载
js
hydrating = false; // 标记服务端渲染(hydration)结束
if (vm.$vnode == null) { // 如果是根组件
vm._isMounted = true; // 标记组件已挂载
callHook(vm, 'mounted'); // 触发 mounted 生命周期钩子
}
return vm; // 返回组件实例逻辑解析:
vm.$vnode == null表示当前组件是根组件(非子组件)。- 只有根组件会立即触发
mounted钩子,子组件的mounted在其 DOM 插入父节点后触发。
总结流程
- 初始化:绑定挂载元素,检查渲染函数。
- 生命周期:触发 beforeMount 钩子。
- 渲染机制:定义更新函数,创建 Watcher 监听变化。
- 挂载完成:标记状态并触发 mounted 钩子。
核心设计思想
- 响应式驱动:通过 Watcher 监听数据变化,自动触发 updateComponent。
- 虚拟 DOM:使用 _render 生成虚拟 DOM,_update 进行 Diff 和 DOM 更新。
- 生命周期管理:在关键节点(如挂载前、挂载后、更新前)触发钩子函数。
