本篇源代碼所在路徑vue/src/core/instance/vue
幾乎全部JS框架或插件的編寫都有一個相似的模式,即向全局輸出一個類或者說構造函數,經過建立實例來使用這個類的公開方法,或者使用類的靜態全局方法輔助實現功能。相信精通Jquery或編寫過Jquery插件的開發者會對這個模式很是熟悉。Vue.js也一模一樣,只是一開始接觸這個框架的時候對它所能實現的功能的感嘆蓋過了它也不過是一個內容較爲豐富和精緻的大型類的本質。git
Vue的核心類的構建文件,代碼很是簡單,就是一串定義構造函數的基礎代碼:github
// 定義Vue構造函數,形參options
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)
}
複製代碼
可是Vue全部功能的實現,這只是一個開始:安全
// 引入初始化混合函數
import { initMixin } from './init'
// 引入狀態混合函數
import { stateMixin } from './state'
// 引入視圖渲染混合函數
import { renderMixin } from './render'
// 引入事件混合函數
import { eventsMixin } from './events'
// 引入生命週期混合函數
import { lifecycleMixin } from './lifecycle'
// 引入warn控制檯錯誤提示函數
import { warn } from '../util/index'
...
// 掛載初始化方法
initMixin(Vue)
// 掛載狀態處理相關方法
stateMixin(Vue)
// 掛載事件響應相關方法
eventsMixin(Vue)
// 掛載生命週期相關方法
lifecycleMixin(Vue)
// 掛載視圖渲染方法
renderMixin(Vue)
複製代碼
在類構造文件的頭部引入了同目錄下5個文件中的混合函數(我認爲這裏只是爲了要表示把一些方法混入到初始類中才統一用了Mixin的後綴,因此不要深究覺得這是什麼特殊的函數),分別是初始化 initMixin
、狀態 stateMixin
、渲染 renderMixin
、事件 eventsMixin
、生命週期 lifecycleMixin
。在文件尾部將這幾個函數裏包含的具體方法掛載到Vue原始類上。架構
從各個細化模塊,能夠看出做者是如何進行邏輯架構分類的。這裏又學到了一種模塊開發的好方法,將類繼承方法按模塊獨立編寫,單獨進行掛載實現了可插拔的便利性。框架
export default Vue
複製代碼
文件最後的經典代碼。到此Vue的類構造完成!ide
就這樣完成了麼!且慢,來稍微看一下初始化混合函數初步作了些啥:函數
下面代碼位於vue/src/core/instance/init.js工具
最早爲基礎類掛載的方法就是_init()
,這是惟一在類實例化的過程當中執行的函數,位於整個函數棧的最底層,其餘的功能將在此方法裏初步分化。post
// 導出ininMixin函數,接收形參Vue,
// 使用Flow進行靜態類型檢查指定爲Component類
export function initMixin (Vue: Class<Component>) {
// 在Vue類的原型上掛載_init()方法
// 接收類型爲原始對象的options形參,此參數爲非必選參數
Vue.prototype._init = function (options?: Object) {
// 將實例對象賦值給vm變量
// 這裏會再次進行Component類型檢查確保vm接收到的是Vue類的實例
const vm: Component = this
// 給實例對象vm定義_uid屬性,做爲vue實例的惟一標識ID
// uid是在函數外定義的變量,從0開始增量賦值
// a uid
vm._uid = uid++
// 定義startTag、endTag變量
let startTag, endTag
// 註釋的意思是代碼覆蓋率檢測工具istanbul會忽略if分支
// 由於下面代碼是專爲性能分析使用的,之後都不作分析
/* istanbul ignore if */
// 非生產環境且進行性能分析的時候執行如下代碼
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
// mark是進行性能分析的工具函數,目前可忽略
mark(startTag)
}
// 給vm設置一個_isVue屬性做爲標記,避免被觀察
// 猜測多是以後觀察者進行監視的時候會忽略掉有這個標記的對象
// 具體緣由待之後分析
// a flag to avoid this being observed
vm._isVue = true
// 合併options對象
// merge options
// 若是是內部組件則執行初始化內部組件函數
// 這裏特地區分出內部定義的組件,是爲了進行特別處理提高優化
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
// 不然執行合併options函數,並賦值給vm的公共屬性
// 在這裏的合併函數主要是解決與繼承自父類的配置對象的合併
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
// 忽略代碼覆蓋,在非生產環境初始化代理
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
// 暴露實例對象
vm._self = vm
// 初始化實例的生命週期相關屬性
initLifecycle(vm)
// 初始化事件相關屬性和監聽功能
initEvents(vm)
// 初始化渲染相關屬性和功能
initRender(vm)
// 調用生命週期鉤子函數beforeCreate
callHook(vm, 'beforeCreate')
// 初始化父組件注入屬性
initInjections(vm) // resolve injections before data/props
// 初始化狀態相關屬性和功能
initState(vm)
// 初始化子組件屬性提供器
initProvide(vm) // resolve provide after data/props
// 調用生命週期鉤子函數created
callHook(vm, 'created')
// 性能檢測代碼
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
// 執行DOM元素掛載函數
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
複製代碼
還記得在文件組織裏分析的,Component類的的具體定義可參照這個文件。
初始化函數內容很少,主要作了這麼幾件事:
這部分代碼已經完整地展現出了將Vue實例對象掛載到DOM元素上並執行渲染的大半程生命週期的進程,在此以後就是視圖的交互過程,直到實例對象被銷燬。後半段代碼清晰地呈現了生命週期中各個功能的初始化順序,也就是那張著名的生命週期圖示的對應代碼。
各個生命週期的初始化函數內容比較豐富,決定在另外一個文檔中作一個單獨討論類初始化函數詳情
雖然核心類的定義代碼寥寥數行,可是在類初始化的過程當中執行了很是多的其餘功能的初始化,從這個基礎的類的實現去一步步解開每個更復雜的功能的實現可能會讓學習者能逐步深刻了解Vue的豐富內容,基於源代碼一句句的解釋雖然很是冗餘,可是但願即使是基礎不是特別紮實的同窗也能看懂,認識到源碼學習再也不是大難題。