從入口文件找到Vue構造函數


version: 2.5.17-beta.0

首先從package.json文件開始看,scripts里有一條命令是dev:

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

它運行了scripts/config.js文件,這個文件生成了rollup打包器的配置,這個文件里web-full-dev對應的配置是這樣的:

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

entry入口是web/entry-runtime-with-compiler.js這個文件,但是直接找不到web目錄在哪里,原來這里是自己定義了一個路徑別名,scripts/config.js里有方法專門來轉換別名路徑變成真正的路徑:

const aliases = require('./alias')
const resolve = p => {
  const base = p.split('/')[0]
  if (aliases[base]) {
    return path.resolve(aliases[base], p.slice(base.length + 1))
  } else {
    return path.resolve(__dirname, '../', p)
  }
}

這里引入的alias正是別名對應的真實路徑:

const path = require('path')

const resolve = p => path.resolve(__dirname, '../', p)

module.exports = {
  vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
  compiler: resolve('src/compiler'),
  core: resolve('src/core'),
  shared: resolve('src/shared'),
  web: resolve('src/platforms/web'),
  weex: resolve('src/platforms/weex'),
  server: resolve('src/server'),
  entries: resolve('src/entries'),
  sfc: resolve('src/sfc')
}

這時就能知道,web/entry-runtime-with-compiler.js對應的路徑是src/platforms/web/entry-runtime-with-compiler.js。

打開entry-runtime-with-compiler.js文件,發現這樣一句:

import Vue from './runtime/index'

於是打開runtime/index.js,發現這樣一句:

import Vue from 'core/index'

打開core/index.js,發現這樣一句:

import Vue from './instance/index'

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

這里做了判斷,如果是開發環境而不是生產環境,而且實例化vue對象的時候沒有使用new操作符,就會報一個警告,然后針對傳入的options執行之后的操作。

為什么(this instanceof Vue)這一句可以判斷是否使用了new操作符?

已new來調用構造函數會經歷4個步驟:

  1. 創建一個新對象;
  2. 將構造函數的作用域賦給新對象(因此this 就指向了這個新對象);
  3. 執行構造函數中的代碼(為這個新對象添加屬性);
  4. 返回新對象。

而instanceof用來檢測Vue構造函數的prototype是否存在於this的原型鏈上,換句話說,如果使用new實例化的時候,this就指向了這個新創建的對象,這時this instanceof Vue這句話的意思就是判斷新創建的對象是否是Vue類型的,也就相當於判斷新實例對象的constructor是否是Vue構造函數。

initGlobalAPI
從上面依據文件反向找到構造函數的過程,我們發現了這些文件處理的順序是這樣的:
1.Vue構造函數所在的文件:core/instance/index.js
2.接着是core/index.js
3.然后是platforms/web/runtime/index.js
這其中core/index.js里有這樣一句:
initGlobalAPI(Vue)

從函數名字可以看出它的意思,為Vue構造函數初始化全局API,也就是官方文檔里列出來的那一串API列表,它們都是直接掛在Vue構造函數上的屬性。

initGlobalAPI在core/global-api/index.js這個文件里定義。

//initGlobalAPI初始化全局API
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)
  //為Vue構造函數定義一個叫做config的屬性,官方API文檔里寫“Vue.config 是一個對象,包含 Vue 的全局配置。”
  //通過上面對configDef這個屬性描述符的定義,可以發現這個Vue.config是一個訪問器屬性,而不是數據屬性
  //因為它擁有get和set方法,專門處理讀取和修改它的動作
  //它的get方法直接返回core/config.js這個文件定義的全局配置
  //它的set方法會報一個警告,不要替換整個Vue.config對象,而是設置單個配置項

  // 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里的方法並不是全局API,避免依賴它們除非你知道它們的風險
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }
 //warn方法,在core/util/debug.js里,報錯方法   
  //extend方法,在shared/util.js里,用於將一個對象的多個屬性加到另一個對象上去    
  //mergeOptions,在core/util/options.js里,用於按不同參數的策略合並option   
  //defineReactive,在core/observer/index

  Vue.set = set
  //set方法,在core/observer/index.js里,用於給對象設置一個響應式屬性。如果這個屬性被刪除那就會觸發通知。
  Vue.delete = del
  //delete方法,在core/observer/index.js里,刪除一個屬性並且觸發試圖更新如果需要的話
  Vue.nextTick = nextTick
  //nextTick方法,在core/util/next-tick.js里,DOM更新循環結束之后執行延遲回調

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

  // 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)
  //給Vue.options.components增加內建component,KeepAlive,
  //(extend方法,在shared/util.js里,用於將一個對象的多個屬性加到另一個對象上去)
  
  initUse(Vue)//Vue.use,core/global-api/use.js
  initMixin(Vue)//Vue.mixin,core/global-api/mixin.js
  initExtend(Vue)//Vue.extend,core/global-api/extend.js
  initAssetRegisters(Vue)//Vue.component,Vue.directive,Vue.filter,core/global-api/assets.js
}

initGlobalAPI為Vue構造函數添加很多全局API方法和屬性。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM