Vue原始碼學習二 ———— Vue原型物件包裝

apy發表於2018-12-16

Vue原型物件的包裝

在Vue官網直接通過 script 標籤匯入的 Vue包是 umd模組的形式。在使用前都通過 new Vue({})。記錄一下 Vue建構函式的包裝。

在 src/core/instance/index.js 這個檔案是 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) {
  // 使用安全模式來提醒要使用new操作符來呼叫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)
}

/**
 * 在執行npm run dev構建執行時執行, 包裝Vue.prototype。為其新增一些屬性和方法
 */
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

首先匯入了五個方法

  • initMixin
  • stateMixin
  • renderMixin
  • eventsMixin
  • lifecycleMixin

如果不是在生產環境下,且不通過 new 來呼叫Vue 會得到警告。

接下來執行 initMixin方法, 到 initMixin來原始檔看。

export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (){}
}

首先會在 Vue這個建構函式的原型物件上定義一個 _init方法。這個方法是在通過 new Vue({})的時候執行。在 Vue 建構函式中可以看到 this._init(options)。

接著將 Vue建構函式作為引數傳遞給下一個 stateMixin方法, 到stateMixin來原始檔看。

export function stateMixin (Vue: Class<Component>) {
  // flow somehow has problems with directly declared definition object
  // when using Object.defineProperty, so we have to procedurally build up
  // the object here.
  const dataDef = {}
  dataDef.get = function () { return this._data }
  const propsDef = {}
  propsDef.get = function () { return this._props }
  // 設定兩個只讀的屬性 $data $props
  if (process.env.NODE_ENV !== `production`) {
    dataDef.set = function () {
      warn(
        `Avoid replacing instance root $data. ` +
        `Use nested data properties instead.`,
        this
      )
    }
    propsDef.set = function () {
      warn(`$props is readonly.`, this)
    }
  }
  Object.defineProperty(Vue.prototype, `$data`, dataDef)
  Object.defineProperty(Vue.prototype, `$props`, propsDef)

  Vue.prototype.$set = set
  Vue.prototype.$delete = del

  Vue.prototype.$watch = function (){}
  }

其中
Object.defineProperty(Vue.prototype, `$data`, dataDef)
Object.defineProperty(Vue.prototype, `$props`, propsDef)
這是在 Vue的原型物件上定義了兩個屬性 $data$props。其中分別代理了 _data_props。看 dataDef 這個物件上定義了一個 get 方法, 最終返回當前例項物件的 _data。props 也是這樣子的。

if (process.env.NODE_ENV !== `production`) {
    dataDef.set = function () {
      warn(
        `Avoid replacing instance root $data. ` +
        `Use nested data properties instead.`,
        this
      )
    }
    propsDef.set = function () {
      warn(`$props is readonly.`, this)
    }
  }

當你在非生產環境時, 如果修改 $data 和 $props會得到警告資訊。
最後在 Vue.prototype上還定義了 $set、$delete以及 $watch。。

接下來是 eventMixin方法, 進入這個方法的來原始檔

    export function eventsMixin (Vue: Class<Component>) {
        Vue.prototype.$on = function(){};
        Vue.prototype.$once = function(){};
        Vue.prototype.$off = function(){};
    }

又再 Vue.prototype 上定義了三個方法, 分別是 $on$once$off

接下來執行 lifecycleMixin 方法, 看lifecycleMixin方法的來原始檔:

export function lifecycleMixin (Vue: Class<Component>) {
  Vue.prototype._update = function() {}
  Vue.prototype.$forceUpdate = function() {}
  Vue.prototype.$destroy = function(){}

在 lifecycleMixin 方法中又向 Vue的原型物件 prototype上新增了三個方法。分別是 _update$$forceUpdate$$destroy

$forceUpdate: 迫使 Vue 例項重新渲染。注意它僅僅影響例項本身和插入插槽內容的子元件,而不是所有子元件。

最後一個執行 renderMixin方法。在renderMixin來原始檔可以到。

首先執行了一個 installRenderHelpers(Vue.prototype),這個方法的主要作用也是向 Vue.prototype上新增方法, 看它原始檔是:

// 這個函式主要在Vue.prototype上面新增一些方法
export function installRenderHelpers (target: any) {
  target._o = markOnce
  target._n = toNumber
  target._s = toString
  target._l = renderList
  target._t = renderSlot
  target._q = looseEqual
  target._i = looseIndexOf
  target._m = renderStatic
  target._f = resolveFilter
  target._k = checkKeyCodes
  target._b = bindObjectProps
  target._v = createTextVNode
  target._e = createEmptyVNode
  target._u = resolveScopedSlots
  target._g = bindObjectListeners
}

緊接著又向 Vue.prototype物件上新增了 $nextTick和_render方法。

export function renderMixin (Vue: Class<Component>) {
  // install runtime convenience helpers
  installRenderHelpers(Vue.prototype)

  Vue.prototype.$nextTick = function (fn: Function) {
    return nextTick(fn, this)
  }

  Vue.prototype._render = function (): VNode {}

執行完 renderMixin這個方法, Vue建構函式的出生檔案也執行完了。也就是指在 npm run dev命令構建時執行。這裡的每一個方法 *Mixin的作用就是包裝 Vue.prototype, 對其掛載一些屬性和方法。最後 export default Vue將其匯出這個建構函式。此時 Vue.prototype上新增的屬性和方法有這些。

然後在哪裡用到了呢。在 src/core/index.js中匯入了Vue的出生檔案。看Vue原始碼學習三 ———— Vue建構函式包裝

相關文章