先決條件
- 需要能夠熟悉使用 vue ,了解vue屬性和方法臭脓。
- 帶著問題去看源碼
- 熟悉ES6或者Typescript語法
- 確定源碼的版本僚楞,我看的是 2.6.12
問題
- vue在初始化的時候都做了什么事情别凤?
vue的初始化
上個章節(jié)我們知道了vue源代碼的入口文件src/platforms/web/entry-runtime.js
。
import Vue from 'core/index'
import config from 'core/config'
import { extend, noop } from 'shared/util'
import { mountComponent } from 'core/instance/lifecycle'
import { devtools, inBrowser } from 'core/util/index'
import {
query,
mustUseProp,
isReservedTag,
isReservedAttr,
getTagNamespace,
isUnknownElement
} from 'web/util/index'
import { patch } from './patch'
import platformDirectives from './directives/index'
import platformComponents from './components/index'
// install platform specific utils
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement
// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)
// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop
// public mount method
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
第一行import Vue from 'core/index'
川背,就引入了vue的主體贰拿,并在這個主體上進行擴展。比如熄云,添加一些平臺的特定方法 vue.config.mustUseProp
膨更,不過,我們平常并不會用到缴允。
要注意的是荚守,在入口文件給vue綁定了原型方法 $mount
珍德,這個在后面會用到。
核心文件介紹
上面的代碼矗漾,很多都引入了core文件锈候,我們了解下 core
這個文件夾的每個文件的作用。
core
翻譯過來就是 核心 的意思敞贡,vue的核心代碼都在這個文件夾內(nèi)泵琳。
-
components
定義組件代碼 -
global-api
給vue設(shè)置全局配置項,全局API方法等誊役,比如Vue.set Vue.nextTick
等 -
instance
創(chuàng)建vue初始化函數(shù)获列,實例方法,實例屬性和構(gòu)建vue的生命周期蛔垢,這個就很重要了击孩。 -
observer
創(chuàng)建vue中常用的觀察者模型,我們的雙向數(shù)據(jù)綁定和一些watch監(jiān)聽都依賴于它 -
util
創(chuàng)建一些工具函數(shù)提供給源碼使用 -
vdom
創(chuàng)建vue的虛擬DOM -
config.js
常用的配置項給源碼使用 -
index.js
core 的入口代碼文件
我們看下 core 的入口代碼啦桌,第一行 import Vue from './instance/index'
還是在引用vue主體溯壶,目前還是沒有找到Vue主體。
core/index.js
的作用甫男,第一是通過initGlobalAPI
來初始化Vue全局的配置項和全局API(具體請看vue的文檔);第二是定義Vue的原型方法 $isServer 验烧,$ssrContext板驳, FunctionalRenderContext
。
Vue主體函數(shù)
通過入口文件碍拆,我們繼續(xù)向里面深挖 若治,看到這里老鐵們,我們找到了Vue的主體了感混。
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
// 執(zhí)行 initMixin中定義的原型方法
this._init(options)
}
// 定義初始化函數(shù)端幼,實例方法,觸發(fā)生命周期鉤子函數(shù)
initMixin(Vue)
// 定義實例屬性弧满,$data, $props 和 實例方法 $set(), $delete(), $watch()
stateMixin(Vue)
// 定義實例方法/屬性 $on(), $once(), $off() 和 $emit()
eventsMixin(Vue)
// 定義實例方法/生命周期 $forceUpdate(), $destory()婆跑,_update()
lifecycleMixin(Vue)
// 定義實例方法/生命周期 $nextTick(),
// 并且定義內(nèi)部方法 _render()
renderMixin(Vue)
export default Vue
function Vue() {....}
就是構(gòu)建Vue的主體函數(shù)。
這個文件里面有很多方法比如initMixin(), stateMixin(), eventsMixin()
等 都是圍繞者主體函數(shù)來構(gòu)建Vue實例方法庭呜,內(nèi)部方法或者添加鉤子函數(shù)的滑进,不過這都不重要。
在這里的重點是募谎,當我們使用vue的時候 扶关,它執(zhí)行了 function Vue() {....}
主體函數(shù)里面的 this._init()
方法。
new Vue({
data: {}
})
調(diào)用了 this._init(options)
initMixin()
定義了 this._init()
方法数冬。
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
vm._uid = uid++
// a flag to avoid this being observed
vm._isVue = true
// 定義實例屬性 $options节槐,可以重置一些自定義property屬性和方法,比如自定義 created(),methods() 和 data
// vm.constructor 是在 initGlobalAPI 定義的
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
// expose real self
vm._self = vm
// 定義實例屬性 $root, $parent, $children, $refs
// 并初始化實例的一些內(nèi)部屬性 _watcher铜异,_inactive地来, _isMounted 等
initLifecycle(vm)
// 初始化事件
initEvents(vm)
// 定義實例屬性 $slots, $scopedSlots, $createElement, $attrs, '$listeners
initRender(vm)
// 觸發(fā)鉤子函數(shù) beforeCreate
callHook(vm, 'beforeCreate')
// provide 和 inject 主要在開發(fā)高階插件/組件庫時使用。并不推薦用于普通應(yīng)用程序代碼中熙掺。
initInjections(vm) // resolve injections before data/props
// 初始化狀態(tài)未斑,比如 data, props, methods, computed和watch
initState(vm)
// provide 和 inject 主要在開發(fā)高階插件/組件庫時使用。并不推薦用于普通應(yīng)用程序代碼中币绩。
initProvide(vm) // resolve provide after data/props
// 當數(shù)據(jù)蜡秽,方法等必要屬性都初始化后,觸發(fā)created鉤子函數(shù)
callHook(vm, 'created')
// 將實例掛載到Dome上
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
_init() 函數(shù)做了很多事了缆镣,初始化事件芽突,添加了很多實例屬性,觸發(fā)鉤子函數(shù)董瞻,初始化數(shù)據(jù)寞蚌,對數(shù)據(jù)做監(jiān)聽,最后把實例通過$mount()
掛載到dome上钠糊。$mount()
函數(shù)在一開始src/platforms/web/entry-runtime.js
代碼文件被定義挟秤。
總結(jié):
從找到編譯的源代碼入口src/platforms/web/entry-runtime.js
,到最后找到 Vue的主體函數(shù)抄伍。vue初始化的主線都比較清晰艘刚,感覺就像撥洋蔥,在核心里創(chuàng)建函數(shù)主體截珍,然后給函數(shù)主體一層一層添加需要的方法和屬性攀甚。下一節(jié)看vue的生命周期是如何形成的。