当你调用 new Vue()
时,Vue.js 会执行一系列复杂的初始化过程。让我们深入剖析这个看似简单的操作背后发生的事情:
1. 初始化阶段
(1) 内部初始化
function Vue(options) {
if (!(this instanceof Vue)) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options); // 关键初始化入口
}
(2) 合并选项
-
合并构造函数选项 (
Vue.options
) 和实例选项 (new Vue
传入的选项) -
处理
components
,directives
,filters
等 -
生命周期钩子:合并为数组形式(混入时多个钩子都会执行)
-
data:必须为函数,合并后调用函数返回新对象
-
自定义合并:可通过
Vue.config.optionMergeStrategies
定制
// 示例:自定义选项合并策略
Vue.config.optionMergeStrategies.customOption = function (parentVal, childVal) {
return childVal === undefined ? parentVal : childVal;
};
2. 核心初始化流程
(1) 生命周期开始
callHook(vm, 'beforeCreate')
(2) 响应式系统建立
initInjections(vm) // 处理注入
initState(vm) // 核心响应式处理
initProvide(vm) // 处理提供
其中 initState
包含:
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)
(3) 挂载准备
callHook(vm, 'created')
if (vm.$options.el) {
vm.$mount(vm.$options.el) // 触发挂载
}
3. 响应式系统深度解析
数据观测实现
function initData(vm) {
let data = vm.$options.data;
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {};
// 数据代理:vm.xxx -> vm._data.xxx
proxy(vm, '_data', key);
// 核心响应式处理
observe(data);
}
依赖收集原理
class Observer {
constructor(value) {
this.value = value;
this.dep = new Dep();
if (Array.isArray(value)) {
// 数组的特殊处理
protoAugment(value, arrayMethods);
this.observeArray(value);
} else {
// 对象的处理
this.walk(value);
}
}
walk(obj) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
});
}
}
4. 依赖收集的完整闭环
function defineReactive(obj, key, val) {
const dep = new Dep();
let childOb = observe(val); // 递归观察子属性
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.depend(); // 收集依赖
if (childOb) {
childOb.dep.depend(); // 子对象依赖收集
if (Array.isArray(val)) {
dependArray(val); // 数组元素依赖收集
}
}
}
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
childOb = observe(newVal); // 新值观察
dep.notify(); // 触发更新
}
});
}
5. 虚拟DOM与渲染系统
在 $mount
阶段:
Vue.prototype.$mount = function(el) {
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;
// 解析模板/el到render函数
if (!options.render) {
let template = options.template;
if (template) {
// 处理各种模板来源
} else if (el) {
template = getOuterHTML(el);
}
if (template) {
// 编译核心流程
const { render, staticRenderFns } = compileToFunctions(
template,
{ ... },
this
);
options.render = render;
options.staticRenderFns = staticRenderFns;
}
}
// 调用真正的挂载方法
return mount.call(this, el);
};
5. 完整生命周期图示
new Vue()
│
├─ beforeCreate
│ ├─ 初始化事件/生命周期
│ └─ 未初始化响应式数据
│
├─ created
│ ├─ 已完成响应式观测
│ ├─ 计算属性/methods/watch已配置
│ └─ 未挂载DOM
│
├─ beforeMount
│ ├─ 编译模板为render函数
│ └─ 未执行DOM挂载
│
├─ mounted
│ ├─ 已完成DOM挂载
│ └─ $el可用
│
├─ beforeUpdate (数据变化时)
│
├─ updated
│
├─ beforeDestroy
│
└─ destroyed
6. 性能优化相关设计
-
异步更新队列:
// 变更检测是异步的 Vue.nextTick(() => { // DOM更新完成 })
-
组件级更新:
-
每个组件对应一个渲染 Watcher
-
通过
shouldUpdate
钩子优化
-
-
静态树提升:
-
编译时标记静态子树
-
跳过不必要的 diff 比对
-
7. 与其他设计模式的对比
特性 | Vue | React | Angular |
---|---|---|---|
初始化方式 | 选项式/组合式API | 函数式组件 | 装饰器类 |
响应式原理 | getter/setter | 手动setState | Zone.js脏检查 |
变更检测 | 自动依赖追踪 | 手动/自动混合 | 脏检查循环 |
机制 | Vue 2.x | React 16+ | Angular |
---|---|---|---|
变更检测 | 细粒度依赖追踪 | 虚拟DOM diff | 脏检查循环 |
更新粒度 | 组件级 | 纤维节点级 | 组件级 |
数据绑定 | 双向绑定 | 单向数据流 | 双向绑定 |
异步更新 | nextTick 队列 | 优先级调度 | Zone.js 控制 |
模板处理 | 编译时优化 | JSX 运行时处理 | 编译时优化 |
理解 new Vue()
的完整初始化过程,有助于:
-
更高效地使用Vue框架
-
更好地调试应用问题
-
编写更合理的组件代码
-
实现高级定制功能