1. 程式人生 > >vue的原始碼學習之四——1.入口檔案

vue的原始碼學習之四——1.入口檔案

  • 介紹

        版本:2.5.17

  • 從package.json檔案開始

    根目錄下的package.json檔案。在 web 應用下,我們來分析 Runtime + Compiler 構建出來的 Vue.js,它的入口是 src/platforms/web/entry-runtime-with-compiler.js,因此這裡我們只看第一個,也就是npm run dev所執行的命令。

     

    •  npm run dev

    "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",

     

    • scripts/config.js

它會根據傳入引數的不同,而根據不同版本的Vue.js形成構建工具rollup的不同配置 。config.js文章介紹請看(vue的原始碼學習之三——原始碼構建

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 (opts) {
  ...
}

if (process.env.TARGET) {
  module.exports = genConfig(builds[process.env.TARGET])
} else {
  exports.getBuild = name => genConfig(builds[name])
  exports.getAllBuilds = () => Object.keys(builds).map(name => genConfig(builds[name]))
}

     我們看到它呼叫了getConfig(builds[process.env.TARGET]),getConfig用於生成rollup的配置檔案。builds是一個物件,獲取它的process.env.TARGET值,在package.json中,我們看到dev中有TARGET:web-full-dev引數。 
根據web-full-dev這個引數,我們到找到該版本的Vue.js的相關配置,配合alias.js這樣入口檔案我們就找到了,也就是/src/platforms/web/entry-runtime-with-compiler.js。

  • 入口檔案

     

    • 尋找Vue物件的所在

         入口檔案:/src/platforms/web/entry-runtime-with-compiler.js。 
         在檔案中我們看到:

    import Vue from './runtime/index'

        src/platforms/web/runtime/index.js:

    import Vue from 'core/index'

         src/core/index.js:

    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) {
      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)
    }
    //
    // 每個Mixin就是往vue定的原型方法 Vue.prototype.xx = yy 用function方便程式碼的管理,組裝,新增原型方法各個檔案各個模板,用class,需要一個主檔案,新增主屬性等。所以vue使用function
    initMixin(Vue)
    stateMixin(Vue)
    eventsMixin(Vue)
    lifecycleMixin(Vue)
    renderMixin(Vue)
    
    export default Vue
    
    import Vue from './instance/index'

       src/core/instance/index.js:

       至此我們找到了Vue物件的所在。

     

    • 分析Vue物件

     在src/core/instance/index.js中我們找到了定義Vue物件的所在之處。它的建構函式及其簡單:

    
    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)
    }
    
    

     首先判斷如果是不是生產環境,且不是通過new關鍵字來建立物件的話,就在控制檯列印一個warning,之後呼叫了this._init(options)函式。 
    下面的幾個函式,分別在Vue.prototype原型上綁定了一些例項方法,Vue的例項方法

    // _init
    initMixin(Vue)  
    // $set、$delete、$watch
    stateMixin(Vue)
    // $on、$once、$off、$emit
    eventsMixin(Vue)
    // _update、$forceUpdate、$destroy
    lifecycleMixin(Vue)
    // $nextTick、_render、以及多個內部呼叫的方法
    renderMixin(Vue)

    我們終於看到了 Vue 的廬山真面目,它實際上就是一個用 Function 實現的類,我們只能通過 new Vue 去例項化它。

    為何 Vue 不用 ES6 的 Class 去實現呢?我們往後看這裡有很多 xxxMixin 的函式呼叫,並把 Vue 當引數傳入,它們的功能都是給 Vue 的 prototype 上擴充套件一些方法,Vue 按功能把這些擴充套件分散到多個模組中去實現,而不是在一個模組裡實現所有,這種方式是用 Class 難以實現的。這麼做的好處是非常方便程式碼的維護和管理,這種程式設計技巧也非常值得我們去學習。

     

    • initGlobalAPI

      我們按照剛才所提到的檔案引入順序一步步來看。src/core/instance/index.js執行之後,是src/core/index.js檔案 

    import { initGlobalAPI } from './global-api/index'
    import { isServerRendering } from 'core/util/env'
    import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
    
    // 全域性方法
    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

    該檔案首先呼叫了initGlobalAPI,引自src/core/global-api/index.js。 

    • 擴充套件全域性的靜態方法

         src/core/global-api/index.js中定義了給 Vue 這個物件本身擴充套件全域性的靜態方法 

/* @flow */

import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'

import {
  warn,
  extend,
  nextTick,
  mergeOptions,
  defineReactive
} from '../util/index'

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)

  // exposed util methods.
  // NOTE: these are not considered part of the public API - avoid relying on
  // them unless you are aware of the risk.
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios.
  Vue.options._base = Vue

  extend(Vue.options.components, builtInComponents)

  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  initAssetRegisters(Vue)
}

 這裡就是在 Vue 上擴充套件的一些全域性方法的定義,Vue 官網中關於全域性 API 都可以在這裡找到,這裡不會介紹細節,會在之後的章節我們具體介紹到某個 API 的時候會詳細介紹。有一點要注意的是,Vue.util 暴露的方法最好不要依賴,因為它可能經常會發生變化,是不穩定的

  • 總結

  1. 我們找到了vue建構函式,並知道了向vue原型中添加了很多例項方法
  2. 知道了通過 initGlobalAPI 向vue中添加了很多的靜態方法

學習文件:https://ustbhuangyi.github.io/vue-analysis/components/lifecycle.html