人人都能懂的Vue源碼系列—03—resolveConstructorOptions函數-上

上篇文章介紹了Vue構造函數的部分實現,當前Vue實例不是組件時,會執行mergeOptions方法。vue

vm.$options = mergeOptions(
  resolveConstructorOptions(vm.constructor),
    options || {},
    vm
)

mergeOptions方法,咱們以後的博文再來作詳細介紹。今天主要研究resolveConstructorOptions方法,從字面意思來看,這個方法是來解析constructor上的options屬性的。咱們來看源碼。webpack

export function resolveConstructorOptions (Ctor: Class<Component>) {
  let options = Ctor.options
  // 有super屬性,說明Ctor是Vue.extend構建的子類
  if (Ctor.super) {
    const superOptions = resolveConstructorOptions(Ctor.super)
    const cachedSuperOptions = Ctor.superOptions // Vue構造函數上的options,如directives,filters,....
    if (superOptions !== cachedSuperOptions) {
      // super option changed,
      // need to resolve new options.
      Ctor.superOptions = superOptions
      // check if there are any late-modified/attached options (#4976)
      const modifiedOptions = resolveModifiedOptions(Ctor)
      // update base extend options
      if (modifiedOptions) {
        extend(Ctor.extendOptions, modifiedOptions)
      }
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
      if (options.name) {
        options.components[options.name] = Ctor
      }
    }
  }
  return options
}

這個方法要分紅兩種狀況來講明,第一種是Ctor是基礎Vue構造器的狀況,另外一種是Ctor是經過Vue.extend方法擴展的狀況。web

Ctor是基礎Vue構造器

當Ctor(Ctor其實就是構造函數)是基礎Vue構造器時,好比是經過new關鍵字新建Vue構造函數的實例npm

const vm = new Vue({
  el: '#app',
    data: {
      message: 'Hello Chris'
    }
})

這個時候options就是Vue構造函數上的options。以下圖
global options
那麼這個options是在哪裏定義的呢?在以前的代碼中好像沒有看到options的定義在哪裏?此時咱們應該怎麼去找這個options定義的地方呢?
這裏教你們一個方法,首先找到package.json,在這裏能夠找到咱們平時用到的一些npm腳本。以npm run dev爲例。實際上npm run dev是執行了下列的命令
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev"
rollup是相似於webpack的打包工具。咱們能夠看到這條命令指向了一個地址scripts/config,以後還指定了一個Target。找到script/config,發現這個文件裏
有TARGET爲web-full-dev的配置。json

// Runtime+compiler development build (Browser)
  'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  }

來分析上面的代碼,入口文件的地址在web/entry-runtime-with-compiler.js。這個文件就是對Vue構造函數進行的第一層包裝了。因爲今天分析的是options相關的內容,而這層包裝裏沒有options相關的內容,因此這個文件咱們不展開講(以後有文章會詳細介紹)。可是注意這裏的代碼segmentfault

...
import Vue from './runtime/index'
...

咱們Vue構造函數的第二層包裝,就在這個文件裏了。忽略其餘的代碼,咱們來看關於Vue.options的部分api

...
import Vue from 'core/index' // 第三層包裝
import platformDirectives from './directives/index'
import platformComponents from './components/index'
...
// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)
...

// platformDirectives相關
// 這裏導出Vue全局指令model,show
import model from './model'
import show from './show'
export default {
  model,
  show
}

// platformComponents相關
// 這裏導出Vue全局組件Transition,TransitionGroup
import Transition from './transition'
import TransitionGroup from './transition-group'
export default {
  Transition,
  TransitionGroup
}

上面的代碼主要是給Vue.options.directives添加model,show屬性,給Vue.options.components添加Transition,TransitionGroup屬性。那麼還有filters,_base屬性,以及components中的KeepAlive又是怎麼來的呢?
這就要看Vue的第三層包裝裏都作了些什麼?找到core/index,一樣咱們只看Vue.options相關代碼。app

mport Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
...
initGlobalAPI(Vue)
...

instance/index 就是咱們第二篇文章——構造函數定義的那個文件。這個文件咱們以前看過,沒有和Vue構造函數options相關的代碼。那麼咱們剩下的沒有配置的options必定是在initGlobalAPI上配置了。接來下看看/global-api/index的代碼。函數

/* @flow */

import { ASSET_TYPES } from 'shared/constants'
...
export function initGlobalAPI (Vue: GlobalAPI) {
  ...
  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)
  ...
}

// shared/constants.js
export const ASSET_TYPES = [
  'component',
  'directive',
  'filter'
]

// core/components/index
import KeepAlive from './keep-alive'
export default {
  KeepAlive
}

上面這層包裝就把filters,_base和components中的KeepAlive都實現了。經過這三層包裝,Vue構造函數的options對象就生成了,看這些文字可能有點繞,咱們直接上圖。工具

包裝options的過程
回到resolveConstructorOptions的源碼中,當Ctor.super不存在時,直接返回基礎構造器的options。即上圖通過兩次包裝的options。那麼Ctor.super是什麼呢?
Ctor.super是經過Vue.extend構造子類的時候。Vue.extend方法會爲Ctor添加一個super屬性,指向其父類構造器

Vue.extend = function (extendOptions: Object): Function {
  ...
  Sub['super'] = Super
  ...
}

因此當Ctor時基礎構造器的時候,resolveConstructorOptions方法返回基礎構造器的options。除了Ctor是基礎構造器以外,還有一種是Ctor是經過Vue.extend構造的子類。這種狀況比較複雜,下一篇文章專門對其進行介紹,敬請期待!

相關文章
相關標籤/搜索