主要大綱:vue
先來一段最多見的vue代碼demoweb
<div id="app"> {{ message }} </div> // js var vm = new Vue({ el: '#app', data: { message: ‘hello vue' } })
上面已經建立了一個vue應用程序;從上面很容易就看出來 Vue是一個構造器,vm是用這個構造器構造出來的實例化對象,實例化的時候傳入了參數,參數中包括el和data
上述延伸了3個問題:api
這些問題是咱們解鎖vue源碼的最開始的步驟,因此咱們不妨經過vue源碼的入口開始尋找這些源碼的實現app
在源碼的src/platforms/web下面放着不一樣版本的構建entry文件,這些文件中導出export的Vue,都是從src/core/instance/index
這個文件import過來的,
咱們先看下入口文件能帶給咱們什麼答案:dom
// src/core/instance/index 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)
這個入口文件作了3件事:ide
這個入口文件的意義,是在暴露vue以前,給vue經過initGlobalAPI方法給vue拓展了全局靜態方法,對應Vue的外部API是 Vue.config,包含了Vue的全局配置,函數
Vue.config.silent // 日誌與警告 Vue.config.errorHandler // 這個處理函數被調用的時候,能夠獲取錯誤信息和Vue實例 Vue.config.devtools // 配置是否容許 vue-devtools 檢查代碼 …..
initGlobalAPI方法定義了configDef對象,它的getter方法會的屬性值是config,setter方法給出警告不容許修改。最後在vue上添加了config屬性,屬性描述返回configDef對象ui
export function initGlobalAPI (Vue: GlobalAPI) { // config const configDef = {} configDef.get = () => config if (process.env.NODE_ENV !== 'production') { configDef.set = () => { warn( 'Do not replace the Vue.config object, set individual fields instead.' ) } } Object.defineProperty(Vue, 'config', configDef) // 添加config屬性
除此以外,還定義了util屬性,可是並無暴露到外面,也並不建議外部去使用this
瞭解了構造函數,也就知道了new vue()的時候發生了什麼
下面這段代碼就是Vue的構造方法,咱們能夠直觀的看出vue構造器是使用ES5的Function去實現類,是由於能夠經過prototype往vue原型上拓展不少方法,把這些方法拆分到不一樣的文件/模塊下,這樣更有利於代碼的維護,與協同開發
好比在這個文件中,能夠看到把Vue看成一個參數傳進下面的**Mixin方法中,這些方法都是經過接收vue,在它的prototype上面定義一些功能的;prototype
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’) // vue必須是new vue()的實例化對象 } console.log('options', options) this._init(options) // 調用內部_init方法 } initMixin(Vue) // 在created生命週期函數以前的操做 stateMixin(Vue) // 利用 definedProperty 進行靜態數據的訂閱發佈 eventsMixin(Vue) // 實例事件流的注入, 利用的是訂閱發佈模式的事件流構造 lifecycleMixin(Vue) // renderMixin(Vue) // 實現 _render 渲染虛擬dom export default Vue
這個構造函數的最核心點,就是this._init(options)
在此處打斷點,能夠看到參數options傳進來的就是外面咱們實例化時傳入的參數el 和 data
new Vue({ el: '#app', data: { message: ‘hello vue' } })
這個_init方法出自initMixin 函數
看完這個函數,咱們梳理出整個初始化階段源碼的幾個重要的節點
export function initMixin (Vue: Class<Component>) {
console.log('Vue', Vue) Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid 實例化的uid遞增1 vm._uid = uid++ let startTag, endTag /* istanbul ignore if */
...
// 用_isVue來標識當前的實例是個Vue實例,這樣作是爲了後續被observed vm._isVue = true // 合併配置options,並判斷是不是內部Component的options的初始化 if (options && options._isComponent) { // 內部 initInternalComponent(vm, options) } else { // 非內部 vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } // 在render中將this指向vm._renderProxy 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 // 初始化vm的狀態,包括data、props、computed、watcher等 initState(vm) initProvide(vm) // resolve provide after data/props // vm已經建立好來,回掉created鉤子函數 callHook(vm, 'created’) /* istanbul ignore if */ … // 將實例進行掛載 if (vm.$options.el) { vm.$mount(vm.$options.el) } }
}