初始化過程
初始化過程:init -> $mount -> compile -> new Watcher -> render -> update
- src/platforms/web/runtime/index.js:實現$mount
- src/core/index:全局api
- src/core/instance/index:聲明vue構造函數
- src/platforms/web/entry-runtime-with-compiler:覆蓋了$mount
- src/core/instance/lifecycle.js mountComponent:執行渲染和更新,虛擬dom -》真實dom
1、定義$mount,patch
src/platforms/web/runtime/index.js
執行掛載方法
Vue.prototype.__patch__ = inBrowser ? patch : noop // 定義一個補丁函數,執行patching算法進行更新
Vue.prototype.$mount = function ( // 定義一個$mount 方法 (掛載根組件到指定宿主元素)
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating) // 執行掛載
}
2、定義全局api
src/core/index.js
initGlobalAPI(Vue)
// src/core/global-api/index.js
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
initUse(Vue) // 實現Vue.use函數
initMixin(Vue) // 實現Vue.mixin函數
initExtend(Vue) // 實現Vue.extend函數
initAssetRegisters(Vue) // 註冊並實現指令,組件,過濾器
3、定義Vue構造函數
src/core/instance/index.js
function Vue (options) { // vue構造函數
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options) // 初始化
}
initMixin(Vue) // 實現_init函數
stateMixin(Vue) // 狀態相關api,$data,$props,$set,$delete,$watch
eventsMixin(Vue) // 事件相關api,$on,$once,$off,$emit
lifecycleMixin(Vue) // 生命週期,_update,$forceUpdate,$destory
renderMixin(Vue) // 渲染api, _render,$nextTick
src/core/instance/init.js
實現initMixin()
方法,創建組件實例,初始化數據、屬性以及事件等等。
export function initMixin (Vue: Class<Component>) { // 創建組件實例,初始化其數據、屬性和事件等等
Vue.prototype._init = function (options?: Object) { // 在Vue原型中添加_init方法
...
initLifecycle(vm) // 定義了$parent,$root,$children,$refs,
initEvents(vm) // 處理父組件傳遞的監聽器($on,$emit)
initRender(vm) // $slots,$scopedSlots,_c,$createElement
callHook(vm, 'beforeCreate') // 在beforeCreate生命週期之前執行了以上三個方法
initInjections(vm) // resolve injections before data/props 獲取注入數據
initState(vm) // 初始化props,methods,data,computed,watch
initProvide(vm) // resolve provide after data/props 注入數據
callHook(vm, 'created')
...
}
}
4、入口
src/platforms/web/entry-runtime-with-compiler.js
這個文件主要是擴展了$mount的方法,用於處理template和el選項,同時將獲取的模板進行編譯。
const mount = Vue.prototype.$mount // 獲取Vue原型中的 $mount 方法
Vue.prototype.$mount = function ( // 添加一個處理template或者el選項的功能
el?: string | Element, // el 屬性其實就是掛載點,所有的掛載元素會被Vue生成的DOM替換掉,如果render跟template都不存在,那麼掛載元素的html就會被拿出來當作模板使用。
hydrating?: boolean
): Component {
el = el && query(el) // 查找對應的dom,沒有就自己創建一個div
const options = this.$options // 獲取Vue實例中的參數
if (!options.render) { // 如果不存在render函數,
//因此可以看到render的優先級高於template高於el
let template = options.template // 纔去找template
...
...
// 獲取模板後執行編譯
//compileToFunctions() 將template字符串轉換成render函數
const { render, staticRenderFns } = compileToFunctions(template, { // 編譯模板
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render // 將render函數保存的options中
options.staticRenderFns = staticRenderFns
...
}
return mount.call(this,el,hydrating)
}
無論通過template還是el的方式最終生成的都是render函數。
5、mountComponent
src/core/instance/lifecycle.js
export function mountComponent ( // 執行掛載
...
}