咱們先來看一個簡單的事例
首先我使用官方腳手架新建一個項目vue init webpack vue-demo
而後我建立兩個文件index.js plugins.js
.
我將這兩個文件放置在src/classes/vue-use
目錄下javascript
接下來對這兩個文件進行編寫vue
// 文件: src/classes/vue-use/plugins.js const Plugin1 = { install(a, b, c) { console.log('Plugin1 第一個參數:', a); console.log('Plugin1 第二個參數:', b); console.log('Plugin1 第三個參數:', c); }, }; function Plugin2(a, b, c) { console.log('Plugin2 第一個參數:', a); console.log('Plugin2 第二個參數:', b); console.log('Plugin2 第三個參數:', c); } export { Plugin1, Plugin2 };
// 文件: src/classes/vue-use/index.js import Vue from 'vue'; import { Plugin1, Plugin2 } from './plugins'; Vue.use(Plugin1, '參數1', '參數2'); Vue.use(Plugin2, '參數A', '參數B');
而後咱們在入口文件main.js
引用這段代碼java
// 文件: src/main.js import Vue from 'vue'; import '@/classes/vue-use'; import App from './App'; import router from './router'; Vue.config.productionTip = false; /* eslint-disable no-new */ new Vue({ el: '#app', router, render: h => h(App), });
此時咱們執行npm run dev
打開8080端口開啓開發調試工具能夠看到控制檯輸出如下信息...]webpack
從中能夠發現咱們在plugin1
中的install
方法編寫的三個console都打印出來,第一個打印出來的是Vue對象,第二個跟第三個是咱們傳入的兩個參數。
而plugin2
沒有install
方法,它自己就是一個方法,也能打印三個參數,第一個是Vue對象,第二個跟第三個也是咱們傳入的兩個參數。web
那麼如今咱們是否是大概對Vue.use
有一個模糊的猜測~npm
好咱們仍是不要猜測,直接上源碼api
// Vue源碼文件路徑:src/core/global-api/use.js 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 } // additional parameters 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 } }
從源碼中咱們能夠發現vue首先判斷這個插件是否被註冊過,不容許重複註冊。
而且接收的plugin
參數的限制是Function | Object
兩種類型。
對於這兩種類型有不一樣的處理。
首先將咱們傳入的參數整理成數組 => const args = toArray(arguments, 1)
。
(toArray源碼)數組
// Vue源碼文件路徑:src/core/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
對象添加到這個數組的起始位置args.unshift(this)
,這裏的this 指向Vue
對象
若是咱們傳入的plugin
(Vue.use的第一個參數)的install
是一個方法。也就是說若是咱們傳入一個對象,對象中包含install
方法,那麼咱們就調用這個plugin
的install
方法並將整理好的數組當成參數傳入install
方法中。 => plugin.install.apply(plugin, args)
若是咱們傳入的plugin
就是一個函數,那麼咱們就直接調用這個函數並將整理好的數組當成參數傳入。 => plugin.apply(null, args)
以後給這個插件添加至已經添加過的插件數組中,標示已經註冊過 => installedPlugins.push(plugin)
最後返回Vue對象。app
經過以上分析咱們能夠知道,在咱們之後編寫插件的時候能夠有兩種方式。
一種是將這個插件的邏輯封裝成一個對象最後將最後在install編寫業務代碼暴露給Vue對象。這樣作的好處是能夠添加任意參數在這個對象上方便將install函數封裝得更加精簡,可拓展性也比較高。
還有一種則是將全部邏輯都編寫成一個函數暴露給Vue。
其實兩種方法原理都同樣,無非第二種就是將這個插件直接當成install函數來處理。
我的以爲第一種方式比較合理。
舉個?函數
export const Plugin = { install(Vue) { Vue.component... Vue.mixins... Vue... // 咱們也能夠在install裏面執行其餘函數,Vue會將this指向咱們的插件 console.log(this) // {install: ...,utils: ...} this.utils(Vue) // 執行utils函數 console.log(this.COUNT) // 0 }, utils(Vue) { Vue... console.log(Vue) // Vue }, COUNT: 0 } // 咱們能夠在這個對象上添加參數,最終Vue只會執行install方法,而其餘方法能夠做爲封裝install方法的輔助函數 const test = 'test' export function Plugin2(Vue) { Vue... console.log(test) // 'test' // 注意若是插件編寫成函數形式,那麼Vue只會把this指向null,並不會指向這個函數 console.log(this) // null } // 這種方式咱們只能在一個函數中編寫插件邏輯,可封裝性就不是那麼強了
小弟不才,對vue源碼的理解暫且到這。歡迎大佬們多指教~