先舉個?
我們先來看一個簡單的事例
首先我使用官方腳手架建立一個項目
vue init webpack vue-demo
然後我建立兩個檔案
index.js plugins.js
.
我将這兩個檔案放置在
src/classes/vue-use
目錄下
接下來對這兩個檔案進行編寫
// 檔案: 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
引用這段代碼
// 檔案: 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端口開啟開發調試工具可以看到控制台輸出以下資訊
...]
從中可以發現我們在
plugin1
中的
install
方法編寫的三個console都列印出來,第一個列印出來的是Vue對象,第二個跟第三個是我們傳入的兩個參數。
而
plugin2
沒有
install
方法,它本身就是一個方法,也能列印三個參數,第一個是Vue對象,第二個跟第三個也是我們傳入的兩個參數。
那麼現在我們是不是大概對
Vue.use
有一個模糊的猜想~
分析源碼
好我們還是不要猜想,直接上源碼
// 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對象。
小結
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
}
// 這種方式我們隻能在一個函數中編寫插件邏輯,可封裝性就不是那麼強了