

一、整体流程概览
当我们执行 new Vue({ ... }) 时,Vue 会经历 初始化 → 编译模板 → 挂载 DOM 三个阶段。整个过程由 _init 方法驱动,最终通过 $mount 完成视图渲染。
核心路径:
new Vue()→_init()→initState()→$mount()→mountComponent()→_render()→_update()→ 真实 DOM
二、详细步骤解析
1. 构造函数与 _init 初始化
源码位置:src/core/instance/index.js
js
function Vue(options) {
if (!(this instanceof Vue)) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
- 调用
_init是整个实例化的起点。 - 在此之前,Vue 原型上已通过 mixin 注入了各类方法:
initMixin→ 定义_initstateMixin→$set,$delete,$watcheventsMixin→$on,$emitlifecycleMixin→_update,$destroyrenderMixin→_render
2. _init 中的关键操作
源码位置:src/core/instance/init.js
js
Vue.prototype._init = function (options) {
const vm = this;
vm._uid = uid++;
vm._isVue = true;
// 合并配置(处理 mixins / extends)
if (options && options._isComponent) {
initInternalComponent(vm, options);
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
);
}
// 初始化代理(开发环境)
if (process.env.NODE_ENV !== 'production') {
initProxy(vm);
} else {
vm._renderProxy = vm;
}
vm._self = vm;
// 初始化生命周期、事件、渲染
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
// 初始化依赖注入(inject / provide)
initInjections(vm); // 在 data/props 之前
initState(vm); // 初始化 props, methods, data, computed, watch
initProvide(vm); // 在 data/props 之后
callHook(vm, 'created');
// 如果有 el,自动挂载
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
}
✅ 关键结论:
beforeCreate时:data / props 尚未初始化,无法访问;created时:数据已响应式化 ,但 DOM 还未生成 ,不能操作$el;- 挂载由
$mount触发。
3. 数据初始化:initState 与 initData
源码位置:src/core/instance/state.js
js
export function initState(vm) {
vm._watchers = [];
const opts = vm.$options;
if (opts.props) initProps(vm, opts.props);
if (opts.methods) initMethods(vm, opts.methods);
if (opts.data) initData(vm);
if (opts.computed) initComputed(vm, opts.computed);
if (opts.watch) initWatch(vm, opts.watch);
}
function initData(vm) {
let data = vm.$options.data;
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {};
// 校验 data 为纯对象
if (!isPlainObject(data)) { /* warn */ }
const keys = Object.keys(data);
const props = vm.$options.props;
const methods = vm.$options.methods;
// 属性名冲突检查(data vs props/methods)
for (let i = keys.length - 1; i >= 0; i--) {
const key = keys[i];
if (props && hasOwn(props, key)) { /* warn */ }
else if (!isReserved(key)) {
proxy(vm, '_data', key); // 通过 this.key 访问 vm._data[key]
}
}
// 响应式化
observe(data, true /* asRootData */);
}
✅ 重点:
- 组件中
data必须是函数(避免多实例共享对象); - 通过
proxy实现this.message→this._data.message; - 最终调用
observe将 data 转为响应式(基于Object.defineProperty)。
4. 挂载阶段:$mount 与模板编译
源码位置:src/platforms/web/entry-runtime-with-compiler.js
js
Vue.prototype.$mount = function (el, hydrating) {
el = el && query(el);
if (el === document.body || el === document.documentElement) {
warn('Do not mount Vue to <html> or <body>');
return this;
}
const options = this.$options;
if (!options.render) {
let template = options.template;
if (template) {
// 处理 string / element 类型的 template
} else if (el) {
template = getOuterHTML(el); // 从 el 提取 HTML
}
if (template) {
// 编译 template → render 函数
const { render, staticRenderFns } = compileToFunctions(template, {}, this);
options.render = render;
options.staticRenderFns = staticRenderFns;
}
}
// 调用真正的 mount
return mount.call(this, el, hydrating);
}
✅ 关键点:
- 若无
render函数,则尝试从template或el提取模板; - 通过
compileToFunctions将模板编译为render函数; - 编译三步:HTML → AST → render 字符串 → render 函数。
5. 渲染组件:mountComponent
源码位置:src/core/instance/lifecycle.js
js
export function mountComponent(vm, el, hydrating) {
vm.$el = el;
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode;
// 警告:运行时版本缺少编译器
}
callHook(vm, 'beforeMount');
// 定义更新函数
let updateComponent = () => {
vm._update(vm._render(), hydrating);
};
// 创建渲染 Watcher(核心!)
new Watcher(vm, updateComponent, noop, {
before() {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate');
}
}
}, true /* isRenderWatcher */);
hydrating = false;
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, 'mounted');
}
return vm;
}
✅ 核心机制:
- 创建一个 渲染 Watcher,监听响应式数据变化;
- 初次执行
updateComponent→ 触发首次渲染; - 数据变更时,自动触发
beforeUpdate→ 重新_render→_update。
6. 生成 VNode 与更新 DOM
_render:生成虚拟 DOM
js
Vue.prototype._render = function () {
const { render } = this.$options;
let vnode;
try {
vnode = render.call(this._renderProxy, this.$createElement);
} catch (e) { /* error handling */ }
// 校验 vnode 合法性
return vnode;
}
_update:将 VNode 转为真实 DOM
js
Vue.prototype._update = function (vnode, hydrating) {
const vm = this;
const prevVnode = vm._vnode;
vm._vnode = vnode;
if (!prevVnode) {
// 初次挂载
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false);
} else {
// 更新
vm.$el = vm.__patch__(prevVnode, vnode);
}
}
__patch__是平台相关方法(Web 端为patch函数),负责 VNode diff + DOM 操作。
三、总结:挂载全过程
| 阶段 | 关键动作 | 生命周期钩子 |
|---|---|---|
| 初始化 | 合并配置、初始化 props/data/methods/watch | beforeCreate → created |
| 编译 | template → AST → render 函数 | --- |
| 挂载 | 创建渲染 Watcher,首次执行 _render + _update |
beforeMount → mounted |
| 更新 | 数据变化 → 触发 Watcher → 重新渲染 | beforeUpdate → updated |
💡 一句话概括 :
Vue 实例挂载的本质是------将响应式数据通过 render 函数生成 VNode,再通过 patch 算法高效更新到真实 DOM 上 ,整个过程由一个 渲染 Watcher 驱动。
参考文献

