vue.use()方法從原始碼到使用

船長_發表於2019-05-11

關於 vue.use 我們都知道些什麼?

在做 vue 開發的時候大家一定經常接觸 Vue.use() 方法,官網給出的解釋是: 通過全域性方法 Vue.use() 使用外掛;我覺得把使用理解成註冊更合適一些,首先看下面常見的註冊場景。

import Router from 'vue-router'
Vue.use(Router)

import Vuex from 'vuex'
Vue.use(Vuex)

import Echarts from 'echarts'
Vue.prototype.$echarts = Echarts
複製程式碼

關於 echarts 的註冊很簡單,直接掛在 Vue 方法的原型上,通過原型鏈繼承的關係可以在任意一個元件裡通過 this.$echarts 訪問到 echarts 例項,我們來寫一個簡單的例子證明一下。

function myVue(title){
  this.title = title
}
myVue.prototype.myUse = '在原型上新增公共屬性'
const A = new myVue('我是例項A')
const B = new myVue('我是例項B')
console.log(A.title, B.title, A.myVue, B.myVue, )
// 我是例項A 我是例項B 在原型上新增公共屬性 在原型上新增公共屬性
複製程式碼

而 Router 和 Vuex 的註冊就要去分析 Vue.use() 的原始碼了,在分析原始碼之前先總結一下官方對 Vue.use() 方法的說明:

  • 通過全域性方法 Vue.use() 使用外掛
  • Vue.use 會自動阻止多次註冊相同外掛
  • 它需要在你呼叫 new Vue() 啟動應用之前完成
  • Vue.use() 方法至少傳入一個引數,該引數型別必須是 Object 或 Function,如果是 Object 那麼這個 Object 需要定義一個 install 方法,如果是 Function 那麼這個函式就被當做 install 方法。在 Vue.use() 執行時 install 會預設執行,當 install 執行時第一個引數就是 Vue,其他引數是 Vue.use() 執行時傳入的其他引數。

官網說 Vue.use() 是用來使用外掛的,那麼傳入的 Router 和 Vuex 就是這裡指的外掛,而這個外掛本質上又是一個 install 方法。至於 install 方法內部實現了什麼邏輯就由外掛自身的業務決定了。

原始碼分析

首先說一下 Flow,vue原始碼中那些奇怪的寫法 Vue.use = function (plugin: Function | Object) 是 Flow 的語法,Flow 是 facebook 出品的 JavaScript 靜態型別檢查工具。JavaScript 是動態型別語言,它的靈活性有目共睹,但是過於靈活的副作用是很容易就寫出非常隱蔽的隱患程式碼,在編譯期甚至看上去都不會報錯,但在執行階段就可能出現各種奇怪的 bug。

下面我們正式開始分析原始碼,Vue.use() 的原始碼很簡單30行都不到,首先看 src/core/global-api/use.js 下 Vue.use() 方法的定義:

import { toArray } from '../util/index'
export function initUse (Vue: GlobalAPI) {
  Vue.use = function (plugin: Function | Object) {
    const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
    if (installedPlugins.indexOf(plugin) > -1) {
      return this
    }
    const args = toArray(arguments, 1)
    args.unshift(this)
    if (typeof plugin.install === 'function') {
      plugin.install.apply(plugin, args)
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args)
    }
    installedPlugins.push(plugin)
    return this
  }
}
複製程式碼

上面原始碼中使用了工具函式 toArray ,該函式定義在 src/shared/util.js

export function toArray (list: any, start?: number): Array<any> {
  start = start || 0
  let i = list.length - start
  const ret: Array<any> = new Array(i)
  while (i--) {
    ret[i] = list[i + start]
  }
  return ret
}
複製程式碼

瞭解一下原始碼實現了什麼邏輯

Vue.use = function (plugin: Function | Object) {
在全域性api Vue 上定義了 use 方法,接收一個 plugin 引數可以是 Function 也可以是 Object,這就和前面官方規定的 Vue.use() 第一個引數要求的型別對應上了。

if (installedPlugins.indexOf(plugin) > -1) {
用來判斷該外掛是不是已經註冊過,防止重複註冊。

const args = toArray(arguments, 1)
arguments是 Vue.use() 方法的引數列表是一個類陣列,後面的 1 先理解成一個常量,toArray 方法的作用就是把第一個 Array 引數從下標為1擷取到最後。也就拿到了 Vue.use() 方法除去第一個之外的其他引數,這些引數準備在呼叫 instll 方法的時候傳入。

if (typeof plugin.install === 'function') {
} else if (typeof plugin === 'function') {
這裡的if語句是判斷 Vue.use() 傳入的第一個引數是 Object 還是 Function。

plugin.install.apply(plugin, args)
plugin.apply(null, args)
判斷完之後執行那個對應的 install 方法,用 apply 改變 this 指向,並把 toArray 得到的剩餘引數傳入。

installedPlugins.push(plugin)
最後記錄該元件已經註冊過了

現在我們發現 Vue.use() 的註冊本質上就是執行了一個 install 方法,install 裡的內容由開發者自己定義,通俗講就是一個鉤子可能更貼近語義化而已。

Vue.use()有什麼用

在 install 裡我們可以拿到 Vue 那麼和 Vue 相關的周邊工作都可以考慮放在 Vue.use() 方法裡,比如:

  • directive註冊
  • mixin註冊
  • filters註冊
  • components註冊
  • prototype掛載
  • ...

echarts 用 Vue.use() 來註冊

main.js

import Vue from 'vue'
import echarts from './echarts.js'
Vue.use(echarts)

new Vue({
  ...
})
複製程式碼

echarts.js

import Echarts from 'echarts'
export default {
  install(Vue){
    Vue.prototype.$echarts = Echarts
  }
}
複製程式碼

這樣寫的好處是可以在 install 的檔案裡做更多配置相關的工作,main.js 不會變的臃腫,更方便管理。

全域性元件用 Vue.use() 來註冊

base.js

import a from './a'
import b from './b'
let components = { a, b }
const installBase = {
  install (Vue) {
    Object.keys(components).map(key => Vue.component(key, components[key]))
  }
}
複製程式碼

main.js

import Vue from 'vue'
import base from './base.js'
Vue.use(base)

new Vue({
  ...
})
複製程式碼

相關文章