在作 vue 開發的時候你們必定常常接觸 Vue.use()
方法,官網給出的解釋是: 經過全局方法 Vue.use()
使用插件;我以爲把使用理解成註冊更合適一些,首先看下面常見的註冊場景。vue
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 實例,咱們來寫一個簡單的例子證實一下。vue-router
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()
方法的說明:vuex
Vue.use()
使用插件Vue.use()
方法至少傳入一個參數,該參數類型必須是 Object 或 Function,若是是 Object 那麼這個 Object 須要定義一個 install 方法,若是是 Function 那麼這個函數就被當作 install 方法。在 Vue.use()
執行時 install 會默認執行,當 install 執行時第一個參數就是 Vue,其餘參數是 Vue.use()
執行時傳入的其餘參數。官網說 Vue.use()
是用來使用插件的,那麼傳入的 Router 和 Vuex 就是這裏指的插件,而這個插件本質上又是一個 install 方法。至於 install 方法內部實現了什麼邏輯就由插件自身的業務決定了。api
首先說一下 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()
方法的定義:bash
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.jsapp
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
}
複製代碼
瞭解一下源碼實現了什麼邏輯echarts
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 裏的內容由開發者本身定義,通俗講就是一個鉤子可能更貼近語義化而已。
在 install 裏咱們能夠拿到 Vue 那麼和 Vue 相關的周邊工做均可以考慮放在 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 不會變的臃腫,更方便管理。
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({
...
})
複製代碼