近來沒什么事,準備把Vue的源碼通讀一遍,2.0中加入了Serve-Side-Render、Virtual-Dom等新特性,看一看實現(xiàn)原理,順便記錄一下學(xué)習(xí)的過程,畢竟好記性不如爛筆頭。
作為源碼解讀系列第一篇文章,先從整個框架的入口開始看吧。Vue使用了部分ES6語法,比如module機制。
import config from './config'
//加載初始全局API
import { initGlobalAPI } from './global-api/index'
//類的初始化入口
import Vue from './instance/index'
initGlobalAPI(Vue)
//在Vue類的原型上定義$isServer屬性,用于判斷是否支持服務(wù)端渲染
//這樣在Vue實例上調(diào)用vue.$isServer就可以知道是否開啟了SSR
//關(guān)于Object.defineProperty,這是一個ES5的方法,可以直接在對象上定義屬性
//Vue的數(shù)據(jù)綁定機制就是用的Object.defineProperty,這也是Vue不支持IE8及以下版本的原因
//將data對象的屬性轉(zhuǎn)成getter和setter,具體原理見http://cn.vuejs.org/guide/reactivity.html
Object.defineProperty(Vue.prototype, '$isServer', {
get: () => config._isServer
})
//定義Vue版本號
Vue.version = '2.0.0-rc.4'
export default Vue
以下就是config的源碼
//Vue使用了Flow做靜態(tài)類型檢查
//Flow可以在代碼運行前檢查出參數(shù)、返回值、變量等類型異常
//方便我們在代碼運行前進行部分debug
/* @flow */
import { no, noop } from 'shared/util'
export type Config = {
optionMergeStrategies: { [key: string]: Function };
silent: boolean;
devtools: boolean;
errorHandler: ?Function;
ignoredElements: ?Array<string>;
keyCodes: { [key: string]: number };
// platform
isReservedTag: (x?: string) => boolean;
isUnknownElement: (x?: string) => boolean;
getTagNamespace: (x?: string) => string | void;
mustUseProp: (x?: string) => boolean;
// internal
_assetTypes: Array<string>;
_lifecycleHooks: Array<string>;
_maxUpdateCount: number;
_isServer: boolean;
}
const config: Config = {
optionMergeStrategies: Object.create(null),
//是否顯示warning,默認顯示
silent: false,
//是否啟動devtool
devtools: process.env.NODE_ENV !== 'production',
errorHandler: null,
ignoredElements: null,
keyCodes: Object.create(null),
isReservedTag: no,
isUnknownElement: no,
getTagNamespace: noop,
mustUseProp: no,
//組件的默認類型
_assetTypes: [
'component',
'directive',
'filter'
],
//定義組件的各種生命周期
_lifecycleHooks: [
'beforeCreate',
'created',
'beforeMount',
'mounted',
'beforeUpdate',
'updated',
'beforeDestroy',
'destroyed',
'activated',
'deactivated'
],
//規(guī)定組件在一次刷新中的最大循環(huán)更新次數(shù)
_maxUpdateCount: 100,
//判斷全局環(huán)境是否為server
_isServer: process.env.VUE_ENV === 'server'
}
export default config
再來看Vue實例由哪些部分構(gòu)成,這部分代碼在src/instance,這個文件夾下有events.js,init.js,lifecycle.js,proxy.js,render.js,state.js`等文件,可以看到,一個Vue instance由這幾部分構(gòu)成。為了將這些模塊組織在一起,Vue使用了Mixin模式。以下是index.js的代碼:
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
function Vue (options) {
this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
這里的Mixin利用了每個JavaScript對象都有一個原型,通過原型可以繼承更多的屬性,實現(xiàn)Vue類上方法的重用。在每個mixin方法中,將Vue類作為參數(shù)傳遞進去,并在其原型上定義各種內(nèi)置方法。以eventsMixin方法為例:
//為了說明,這里是其簡化版
export function eventsMixin (Vue: Class<Component>) {
//實現(xiàn)事件綁定的方法,將其添加到原型上
Vue.prototype.$on = function (event: string, fn: Function): Component {
const vm: Component = this
//支持綁定多個事件
;(vm._events[event] || (vm._events[event] = [])).push(fn)
return vm
}
總結(jié):
- Vue中大量使用Object.property,包括利用它生成訪問器,實現(xiàn)數(shù)據(jù)綁定。
- Vue使用了Flow做靜態(tài)類型檢查,有助于提前發(fā)現(xiàn)bug
- 實現(xiàn)Vue類的過程中使用了Mixin這種設(shè)計模式,基于JavaScript原型繼承的特性,可以方便的擴展Vue實例的功能。
參考鏈接:
Object.defineProperty()
Flow | A Static Type Checker For JavaScript