Vue實(shí)例從初始化到掛載的完整流程
一、整體流程概覽
當(dāng)我們執(zhí)行 new Vue({ ... }) 時(shí),Vue 會經(jīng)歷 初始化 → 編譯模板 → 掛載 DOM 三個(gè)階段。整個(gè)過程由 _init 方法驅(qū)動,最終通過 $mount 完成視圖渲染。
核心路徑:new Vue() → _init() → initState() → $mount() → mountComponent() → _render() → _update() → 真實(shí) DOM
二、詳細(xì)步驟解析
1. 構(gòu)造函數(shù)與_init初始化
源碼位置:src/core/instance/index.js
function Vue(options) {
if (!(this instanceof Vue)) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
- 調(diào)用
_init是整個(gè)實(shí)例化的起點(diǎn)。 - 在此之前,Vue 原型上已通過 mixin 注入了各類方法:
initMixin→ 定義_initstateMixin→$set,$delete,$watcheventsMixin→$on,$emitlifecycleMixin→_update,$destroyrenderMixin→_render
2._init中的關(guān)鍵操作
源碼位置:src/core/instance/init.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
);
}
// 初始化代理(開發(fā)環(huán)境)
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);
}
}
關(guān)鍵結(jié)論:
beforeCreate時(shí):data / props 尚未初始化,無法訪問;created時(shí):數(shù)據(jù)已響應(yīng)式化,但 DOM 還未生成,不能操作$el;- 掛載由
$mount觸發(fā)。
3. 數(shù)據(jù)初始化:initState與initData
源碼位置:src/core/instance/state.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 || {};
// 校驗(yàn) 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]
}
}
// 響應(yīng)式化
observe(data, true /* asRootData */);
}
重點(diǎn):
- 組件中
data必須是函數(shù)(避免多實(shí)例共享對象); - 通過
proxy實(shí)現(xiàn)this.message→this._data.message; - 最終調(diào)用
observe將 data 轉(zhuǎn)為響應(yīng)式(基于Object.defineProperty)。
4. 掛載階段:$mount與模板編譯
源碼位置:src/platforms/web/entry-runtime-with-compiler.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 函數(shù)
const { render, staticRenderFns } = compileToFunctions(template, {}, this);
options.render = render;
options.staticRenderFns = staticRenderFns;
}
}
// 調(diào)用真正的 mount
return mount.call(this, el, hydrating);
}
關(guān)鍵點(diǎn):
- 若無
render函數(shù),則嘗試從template或el提取模板; - 通過
compileToFunctions將模板編譯為render函數(shù); - 編譯三步:HTML → AST → render 字符串 → render 函數(shù)。
5. 渲染組件:mountComponent
源碼位置:src/core/instance/lifecycle.js
export function mountComponent(vm, el, hydrating) {
vm.$el = el;
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode;
// 警告:運(yùn)行時(shí)版本缺少編譯器
}
callHook(vm, 'beforeMount');
// 定義更新函數(shù)
let updateComponent = () => {
vm._update(vm._render(), hydrating);
};
// 創(chuàng)建渲染 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;
}
? 核心機(jī)制:
- 創(chuàng)建一個(gè) 渲染 Watcher,監(jiān)聽響應(yīng)式數(shù)據(jù)變化;
- 初次執(zhí)行
updateComponent→ 觸發(fā)首次渲染; - 數(shù)據(jù)變更時(shí),自動觸發(fā)
beforeUpdate→ 重新_render→_update。
6. 生成 VNode 與更新 DOM
_render:生成虛擬 DOM
Vue.prototype._render = function () {
const { render } = this.$options;
let vnode;
try {
vnode = render.call(this._renderProxy, this.$createElement);
} catch (e) { /* error handling */ }
// 校驗(yàn) vnode 合法性
return vnode;
}
_update:將 VNode 轉(zhuǎn)為真實(shí) DOM
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__是平臺相關(guān)方法(Web 端為patch函數(shù)),負(fù)責(zé) VNode diff + DOM 操作。
三、總結(jié):掛載全過程
| 階段 | 關(guān)鍵動作 | 生命周期鉤子 |
|---|---|---|
| 初始化 | 合并配置、初始化 props/data/methods/watch | beforeCreate → created |
| 編譯 | template → AST → render 函數(shù) | — |
| 掛載 | 創(chuàng)建渲染 Watcher,首次執(zhí)行 _render + _update | beforeMount → mounted |
| 更新 | 數(shù)據(jù)變化 → 觸發(fā) Watcher → 重新渲染 | beforeUpdate → updated |
一句話概括:
Vue 實(shí)例掛載的本質(zhì)是——將響應(yīng)式數(shù)據(jù)通過 render 函數(shù)生成 VNode,再通過 patch 算法高效更新到真實(shí) DOM 上,整個(gè)過程由一個(gè) 渲染 Watcher 驅(qū)動。
以上就是Vue實(shí)例從初始化到掛載的完整流程的詳細(xì)內(nèi)容,更多關(guān)于Vue實(shí)例掛載流程的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue2?響應(yīng)式系統(tǒng)之深度響應(yīng)
這篇文章主要介紹了Vue2?響應(yīng)式系統(tǒng)之深度響應(yīng),文章基于Vue2?響應(yīng)式系統(tǒng)的相關(guān)資料展開對Vue2?深度響應(yīng)的介紹,需要的小伙伴可以參考一下2022-04-04
Ant Design Vue table中列超長顯示...并加提示語的實(shí)例
這篇文章主要介紹了Ant Design Vue table中列超長顯示...并加提示語的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10
vue中LocalStorage與SessionStorage的區(qū)別與用法
本文主要介紹了LocalStorage和SessionStorage。LocalStorage和SessionStorage是兩種存儲方式,本文詳細(xì)的介紹一下區(qū)別以及用法,感興趣的可以了解一下2021-09-09
使用el-upload實(shí)現(xiàn)文件上傳方式(包括預(yù)覽,下載)
該文章介紹了兩種文件上傳、預(yù)覽和下載的方法,分別是通過卡片形式和按鈕形式,卡片形式中的下載和預(yù)覽功能尚未完善,父組件使用了el-upload組件,可以實(shí)現(xiàn)新增、編輯和查看文件的功能2025-11-11
基于vue2.0的活動倒計(jì)時(shí)組件countdown(附源碼下載)
這是一款基于vue2.0的活動倒計(jì)時(shí)組件,可以使用服務(wù)端時(shí)間作為當(dāng)前時(shí)間,在倒計(jì)時(shí)開始和結(jié)束時(shí)可以自定義回調(diào)函數(shù)。這篇文章主要介紹了基于vue2.0的活動倒計(jì)時(shí)組件countdown,需要的朋友可以參考下2018-10-10

