Vue2.5.16原始碼筆記

小軒雪發表於2018-05-30

主線

Vue中資料狀態改變後會採用virtual DOM的方式更新DOM,暫且不去深究virtual DOM內部具體實現,你只需要知道virtual DOM分為三個步驟:

  • createElement(): 用 JavaScript物件(虛擬樹) 描述 真實DOM物件(真實樹)
  • diff(oldNode, newNode) : 對比新舊兩個虛擬樹的區別,收集差異
  • patch() : 將差異應用到真實DOM樹

有的時候 第二步 可能與 第三步 合併成一步(Vue 中的patch就是這樣)。

Vue 的建構函式

使用 new 操作符來呼叫 Vue,那麼也就是說 Vue 應該是一個建構函式。

執行 npm run dev 命令:

"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
複製程式碼

rollup配置檔案使用了scripts/config.js檔案,並設定環境變數 TARGET:web-full-dev,開啟看一下關鍵程式碼

// builds 物件
const builds = {
  ...
  // 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
  },
  ...
}
// 生成配置的方法
function genConfig (name) {
  const opts = builds[name]
  const config = {
    input: opts.entry,
    external: opts.external,
    ...
  }
  ...
  return config
}

if (process.env.TARGET) {
  module.exports = genConfig(process.env.TARGET)
} else {
  exports.getBuild = genConfig
  exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
複製程式碼
  1. genConfig 函式返回一個 config 物件,從這個物件上面大概可以看出入口檔案為src/platfroms/web/entry-runtime-with-compiler.js
import Vue from './runtime/index'
複製程式碼
  1. 繼續到檔案src/platfroms/web/runtime/index.js中找建構函式
import Vue from 'core/index'
複製程式碼
  1. 繼續到檔案src/core/index.js中找建構函式
import Vue from './instance/index'
複製程式碼
  1. 繼續到檔案src/core/instance/index.js中找建構函式
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')
  }
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue
複製程式碼

定義 Vue 建構函式,然後以Vue建構函式為引數,5個**Mixin(Vue)函式作用:在Vue 的原型 prototype 上掛載方法或屬性,最後匯出 Vue。

// initMixin(Vue)    src/core/instance/init.js *****************
Vue.prototype._init = function (options?: Object) {}

// stateMixin(Vue)    src/core/instance/state.js ****************
Vue.prototype.$data
Vue.prototype.$set = set
Vue.prototype.$delete = del
Vue.prototype.$watch = function(){}

// eventsMixin(Vue)    src/core/instance/events.js **************
Vue.prototype.$on = function (event: string, fn: Function): Component {}
Vue.prototype.$once = function (event: string, fn: Function): Component {}
Vue.prototype.$off = function (event?: string, fn?: Function): Component {}
Vue.prototype.$emit = function (event: string): Component {}

// lifecycleMixin(Vue)    src/core/instance/lifecycle.js ***************
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
Vue.prototype.$forceUpdate = function () {}
Vue.prototype.$destroy = function () {}

// renderMixin(Vue)    src/core/instance/render.js ************
Vue.prototype.$nextTick = function (fn: Function) {}
Vue.prototype._render = function (): VNode {}
複製程式碼

追溯路線往回走,那麼下一個處理 Vue 建構函式的應該是src/core/index.js檔案,

initGlobalAPI(Vue)

Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})

Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
  value: FunctionalRenderContext
})

Vue.version = '__VERSION__'

export default Vue
複製程式碼

src/core/instance/index.js中匯入已經在原型上掛載了方法和屬性後的 Vue,然後匯入 initGlobalAPI 和 isServerRendering,之後將Vue作為引數傳給 initGlobalAPI ,最後又在 Vue.prototype 上掛載了 $isServer ,在 Vue 上掛載了 version 屬性。

相關文章