上一篇文章我們寫到從入口檔案一步步找到Vue的建構函式,現在我們要去看看Vue例項化經歷的過程
Vue的建構函式
我們知道Vue的建構函式在src/core/instance/index.js
中,不明白的可以去看上一篇文章 Vue原始碼學習筆記一。那我們關注一下Vue的建構函式的內容:
// src/core/instance/index.js
import { initMixin } from `./init`
// Vue的建構函式
function Vue (options) {
//... 驗證環境
this._init(options)
}
// 在Vue原型上繫結例項方法
initMixin(Vue) // init
stateMixin(Vue) // $set $delete $watch
eventsMixin(Vue) // $on $once $off $emit
lifecycleMixin(Vue) // _update $forceUpdate $destroy
renderMixin(Vue) // $nextTick _render
新增Vue屬性和方法
這邊我們可以看到Vue的建構函式中執行了init
方法,從下方得知init是在srccoreinstanceinit.js
中匯出的initMixin
函式中定義的
initMixin
-
vm
即this
,同時為例項新增一個唯一的uid
,vm._isVue = true
監聽物件變化時用於過濾vm,因為Vue的例項是不需要監聽變化的。
// src/core/instance/init.js
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// 當前例項新增了一個唯一的uid
vm._uid = uid++
// ...
// 監聽物件變化時用於過濾vm
vm._isVue = true
//...
}
- 引數處理,根據我們的小栗子,我們的options處理直接進入了else,然後對引數進行合併,這裡是對vue extend的引數需要進行合併處理,我們這裡
resolveConstructorOptions
返回的即是constructor.options
本身 - 生命週期相關變數初始化
initLifecycle(vm)
// srccoreinstancelifecycle.js
// 為元件掛載相應屬性,並初始化
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
vm.$children = []
vm.$refs = {}
vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
4.vm 事件監聽初始化 initEvents()
// src/core/instance/events.js
export function initEvents (vm: Component) {
// 建立事件物件,用於儲存事件
vm._events = Object.create(null)
// 系統事件標識位
vm._hasHookEvent = false
// init parent attached events npm
// 將父元件模板中註冊的事件放到當前元件例項的listeners
const listeners = vm.$options._parentListeners
if (listeners) {
updateComponentListeners(vm, listeners)
}
}
initEvents(vm)
initRender(vm)
callHook(vm, `beforeCreate`)
initInjections(vm)
// vm狀態初始化,prop/data/computed/method/watch都在這裡初始化完成,vue例項create的關鍵
initState(vm)
initProvide(vm)
callHook(vm, `created`)
stateMixin
Vue例項方法–資料,該檔案對應的是Vue的資料的處理,首先對$data進行掛載,然後設定資料
$set
、刪除資料$delete
、觀測資料$watch
方法掛載
// stateMixin(Vue) src/core/instance/state.js
export function stateMixin (Vue: Class<Component>) {
// data
const dataDef = {}
dataDef.get = function () { return this._data }
// prop
const propsDef = {}
propsDef.get = function () { return this._props }
// ...
// 定義$data & prop屬性
Object.defineProperty(Vue.prototype, `$data`, dataDef)
Object.defineProperty(Vue.prototype, `$props`, propsDef)
// 原型鏈新增函式set 和 delete
Vue.prototype.$set = set
Vue.prototype.$delete = del
// 原型鏈新增函式$watch
Vue.prototype.$watch = function (){
// ...
}
}
eventsMixin
Vue例項方法–事件,該檔案主要掛載Vue例項方法的事件,監聽事件
on once
、移除事件off
、觸發事件emit
的掛載
// eventsMixin(Vue) src/core/instance/events.js
export function eventsMixin (Vue: Class<Component>) {
Vue.prototype.$on = function (event: string, fn: Function): Component {
// ...
}
Vue.prototype.$once = function (event: string, fn: Function): Component {
// ...
}
Vue.prototype.$off = function (event?: string, fn?: Function): Component {
// ...
}
Vue.prototype.$emit = function (event: string): Component {
// ...
}
}
lifecycleMixin
Vue例項方法–生命週期,,該檔案主要掛載Vue例項方法中的生命週期方法,重新渲染
$forceUpdate()
、銷燬例項$destroy()
// lifecycleMixin(Vue) src/core/instance/lifecycle.js
Vue.prototype._mount = function(){}
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
Vue.prototype._updateFromParent = function(){}
Vue.prototype.$forceUpdate = function () {}
Vue.prototype.$destroy = function () {}
renderMixin
檔案主要掛載Vue例項方法中的dom更新回撥
$nextTick
及一些其他的render函式,後續我們再的深挖一下
// renderMixin(Vue) src/core/instance/render.js
Vue.prototype.$nextTick = function (fn: Function) {}
Vue.prototype._render = function (): VNode {}
Vue.prototype._s = _toString
Vue.prototype._v = createTextVNode
Vue.prototype._n = toNumber
Vue.prototype._e = createEmptyVNode
Vue.prototype._q = looseEqual
Vue.prototype._i = looseIndexOf
Vue.prototype._m = function(){}
Vue.prototype._o = function(){}
Vue.prototype._f = function resolveFilter (id) {}
Vue.prototype._l = function(){}
Vue.prototype._t = function(){}
Vue.prototype._b = function(){}
Vue.prototype._k = function(){}
全域性API
上面部分,我們對Vue的建構函式,在
src/core/instance/index.js
檔案中的作用進行了大體的瞭解,當然這並沒有結束,依據我們Vue原始碼學習筆記一中提到的,我們追溯到上一級src/core/index.js
// src/core/index.js
import Vue from `./instance/index`
import { initGlobalAPI } from `./global-api/index`
import { isServerRendering } from `core/util/env`
import { FunctionalRenderContext } from `core/vdom/create-functional-component`
// 初始化全域性變數
initGlobalAPI(Vue)
// 為vue原型定義屬性 isServer 判斷是否為服務端渲染
Object.defineProperty(Vue.prototype, `$isServer`, {
get: isServerRendering
})
// 為vue原型定義屬性 ssrContext
Object.defineProperty(Vue.prototype, `$ssrContext`, {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
Object.defineProperty(Vue, `FunctionalRenderContext`, {
value: FunctionalRenderContext
})
Vue.version = `__VERSION__`
export default Vue
initGlobalAPI(Vue)
在Vue 建構函式上掛載靜態屬性和方法即全域性API
// src/core/global-api/index.js
export function initGlobalAPI(Vue: GlobalAPI) {
const configDef = {}
configDef.get = () => config
// ...
Object.defineProperty(Vue, `config`, configDef)
Vue.util = { // Vue.util
warn,
extend,
mergeOptions,
defineReactive
}
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + `s`] = Object.create(null)
})
Vue.options._base = Vue
extend(Vue.options.components, builtInComponents)
initUse(Vue) // Vue.use
initMixin(Vue) // Vue.mixin
initExtend(Vue) // Vue.extend
initAssetRegisters(Vue) // Vue.component Vue.directive Vue.filter
}
內建元件&命令
在追溯到上一級,在檔案
src/platforms/web/runtime/index.js
,該檔案註冊了一些 Vue內建的元件:包裹動態元件KeepAlive
、元素過渡效果Transition
、多個元素過渡TransitionGroup
// src/platforms/web/runtime/index.js 執行後
// 安裝平臺特定的utils
Vue.config.isUnknownElement = isUnknownElement
Vue.config.isReservedTag = isReservedTag
Vue.config.getTagNamespace = getTagNamespace
Vue.config.mustUseProp = mustUseProp
// 安裝平臺特定的 指令 和 元件
Vue.options = {
components: {
KeepAlive,
Transition,
TransitionGroup
},
directives: {
model,
show
},
filters: {},
_base: Vue
}
Vue.prototype.__patch__
Vue.prototype.$mount
compiler編譯器新增
再上一級為
src/platforms/web/entry-runtime-with-compiler.js
,該檔案對原來的Vue.prototype.$mount
進行覆蓋定義,並且在Vue上掛載了compile
。給Vue的 $mount 方法新增 compiler 編譯器,支援 template。
// src/platforms/web/entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount
// ...
Vue.prototype.$mount = function (){
//... 覆蓋 Vue.prototype.$mount
}
// ...
//在 Vue 上掛載 compile
//compileToFunctions 函式的作用,就是將模板 template 編譯為render函式。
Vue.compile = compileToFunctions
總結
至此的話我們從巨集觀上過了一下從我們一層層找到vue到一層層往外看到對Vue的新增屬性方法等,我們有了一個整體的概念
-
src/core/instance/index.js
vue的建構函式,新增Vue屬性和方法 -
src/core/index.js
全域性API的掛載 -
src/platforms/web/runtime/index.js
主要是新增web平臺特有的配置、元件和指令 -
web/entry-runtime-with-compiler.js
給Vue的 $mount 方法新增 compiler 編譯器,支援 template -
scripts/config.js
編譯入口檔案