前言
閱讀此文章,你可以瞭解到:
- Vue.use(plugin)基礎概念(什麼是Vue.use(plugin))
- Vue.use的簡單使用
- 為什麼在引入Vue-Router、ElementUI的時候需要Vue.use()?而引入axios的時候,不需要Vue.use()?
- Vue-Router、ElementUI在Vue.use()分別做了什麼?
- Vue.use原理
- 如何編寫一個Vue外掛?
什麼是Vue.use(plugin)
Vue.use
是用來安裝外掛的。
用法:
Vue.use(plugin)
- 如果外掛是一個物件,必須提供
install
方法。 - 如果外掛是一個函式,它會被作為 install 方法。install 方法呼叫時,
會將 Vue 作為引數傳入
。 - Vue.use(plugin)呼叫之後,外掛的install方法就會預設接受到一個引數,這個引數就是Vue(原理部分會將)
該方法需要在呼叫 new Vue()
之前被呼叫。
當 install 方法被同一個外掛多次呼叫,外掛將只會被安裝一次。(原始碼解析的時候會解析如何實現)
總結:Vue.use是官方提供給開發者的一個api,用來註冊、安裝型別Vuex、vue-router、ElementUI之類的外掛的。
Vue.use的簡單使用
看一下具體的例子:
我們在用Vue-cli3.0裡面初始化專案的時候,會生成一個入口檔案main.js
在main.js
中,如何我們安裝了Vue-Router、Vuex、ElementUI,並且想要在專案中使用,就得在入口檔案main.js
中呼叫一下Vue.use()
Vue.use(ElementUi);
Vue.use(Vuex);
Vue.use(Router);
複製程式碼
這樣就算是完成了對三個外掛的安裝,我們就可以在元件中呼叫 this.$router
、this.$route
、this.$store
、this.$alert()
(ElementUI的彈窗元件)引數(方法)。
為什麼在引入Vue-Router、Vuex、ElementUI的時候需要Vue.use()?而引入axios的時候,不需要Vue.use()?
我們在講什麼是Vue.use的時候,已經說明要用use安裝的外掛,要麼是一個物件裡面包含install方法,要麼本身就是一個方法(自身就是install方法)。
也就是說,這個題目的答案,本質就是:Vue-Router、Vuex、ElementUI三者都具有install方法,並且外掛的執行依賴於install方法裡的一些操作,才能正常執行,而axios沒有install方法也能正常執行。
看到這裡你一定會疑惑:
- 同樣是外掛,為什麼有些外掛要有install方法才能正常執行(如VueRouter),有一些卻可以沒有install方法也可以使用(如axios)?
- 外掛的install方法,可以為我們做什麼?
Vue-Router、ElementUI在install裡面到底做了什麼?
在探究這個問題之前,我們先看看Vue.use這個方法到底做了什麼。
Vue中的use原理
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
// 獲取已經安裝的外掛
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
// 看看外掛是否已經安裝,如果安裝了直接返回
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// toArray(arguments, 1)實現的功能就是,獲取Vue.use(plugin,xx,xx)中的其他引數。
// 比如 Vue.use(plugin,{size:'mini', theme:'black'}),就會回去到plugin意外的引數
const args = toArray(arguments, 1)
// 在引數中第一位插入Vue,從而保證第一個引數是Vue例項
args.unshift(this)
// 外掛要麼是一個函式,要麼是一個物件(物件包含install方法)
if (typeof plugin.install === 'function') {
// 呼叫外掛的install方法,並傳入Vue例項
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
// 在已經安裝的外掛陣列中,放進去
installedPlugins.push(plugin)
return this
}
}
複製程式碼
總結:
Vue.use方法主要做了如下的事:
- 檢查外掛是否安裝,如果安裝了就不再安裝
- 如果沒有沒有安裝,那麼呼叫外掛的install方法,並傳入Vue例項
我們知道了Vue.use做了什麼之後。我們看看那些我們常見的外掛,是如何利用這個use方法的。
Element中的install
const install = function(Vue, opts = {}) {
locale.use(opts.locale);
locale.i18n(opts.i18n);
// components是ElementUI的元件陣列,裡面有Dialog、Input之類的元件
// 往Vue上面掛載元件
components.forEach(component => {
Vue.component(component.name, component);
});
Vue.use(Loading.directive);
// 自定義一些引數
Vue.prototype.$ELEMENT = {
size: opts.size || '',
zIndex: opts.zIndex || 2000
};
// 在Vue原型上註冊一些方法,這就是為什麼我們可以直接使用this.$alert、this.$loading的原因,值就是這麼來的。
Vue.prototype.$loading = Loading.service;
Vue.prototype.$msgbox = MessageBox;
Vue.prototype.$alert = MessageBox.alert;
Vue.prototype.$confirm = MessageBox.confirm;
Vue.prototype.$prompt = MessageBox.prompt;
Vue.prototype.$notify = Notification;
Vue.prototype.$message = Message;
};
複製程式碼
同樣的方法,我們來看看Vue-Router的install又做了什麼。
Vue-Router中的install
我們先把這個install方法的部分拆解出來,只關注其最最核心的邏輯
如果不想讀原始碼,可以直接看原始碼後面的文字簡單總結
import View from './components/view'
import Link from './components/link'
export let _Vue
export function install (Vue) {
_Vue = Vue
const isDef = v => v !== undefined
const registerInstance = (vm, callVal) => {
let i = vm.$options._parentVnode
if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
i(vm, callVal)
}
}
Vue.mixin({
beforeCreate () {
// 如果該元件是根元件
if (isDef(this.$options.router)) {
// 設定根元件叫_routerRoot
this._routerRoot = this
// 根元件的_router屬性為,new Vue傳進去的router
// $options是在mains.js中,new Vue裡的引數,在這裡我們傳入的引數,
this._router = this.$options.router
this._router.init(this)
// 通過defineReactive方法,來把this._router.history.current變成響應式的,這個方法的底層就是object.defineProperty
Vue.util.defineReactive(this, '_route', this._router.history.current)
} else {
// 如果該元件不是根元件,那麼遞迴往上找,知道找到根元件的。
// 因為Vue渲染元件是先渲染根元件,然後渲染根元件的子元件啊,然後再渲染孫子元件。
// 結果就是每一個元件都有this._routerRoot屬性,該屬性指向了根元件。
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
}
registerInstance(this, this)
},
destroyed () {
registerInstance(this)
}
})
// 把自身$router代理為this._routerRoot(根元件的)的_router
// 根元件的_router,就是new Vue傳入的 router
// 這樣就實現了,每一個Vue元件都有$router、$route屬性
Object.defineProperty(Vue.prototype, '$router', {
get () { return this._routerRoot._router }
})
// 同理,這樣就是把自身的$route,代理到根元件傳入的route
Object.defineProperty(Vue.prototype, '$route', {
get () { return this._routerRoot._route }
})
// 註冊 <router-view>元件
Vue.component('RouterView', View)
// 註冊<router-link>元件
Vue.component('RouterLink', Link)
const strats = Vue.config.optionMergeStrategies
// use the same hook merging strategy for route hooks
strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
}
複製程式碼
總結:vue-router的install方法主要幫我們做了如下事情:
- 通過minxi混入的方式,如果自身是根元件,就把根元件的_router屬性對映為new Vue傳入的router例項(this.$options.router)。
- 如果自身不是根元件,那麼層層往上找,直到找到根元件,並用_routerRoot標記出根元件
- 為每一個元件代理
$router
、$route
屬性,這樣每一個元件都可以去到$router
、$route
- 註冊
<router-link>
、<router-view>
元件
看到這裡,你應該明白了,為什麼vueRouter需要install才能使用了吧。
底層一點的理由就是,vueRouter需要在install方法,對Vue例項做一些自定義化的操作:比如在vue.prototype中新增$router、$route
屬性、註冊<router-link>
元件
為什麼axios不需要安裝,可以開箱即用?
其實理由也很簡單,跟上面需要install的相反的。因為axios是基於Promise封裝的庫,是完全獨立於Vue的,根本不需要掛載在Vue上也能實現傳送請求。
而因為VueRouter需要為我們提供$router、$routers
之類的屬性,要依賴與Vue或者操作Vue例項才能實現。
Vue.use實際上就是Vue例項與外掛的一座橋樑。
如何自己編寫一個外掛?
我這裡打算分享一下自己以前做的專案裡,把axios改寫成一個類似外掛的思路。
要寫其他外掛的思路也相似的。
// api.js
import login from './login'; // login頁面所有的aixos請求封裝在此
import home from './home'; // home頁面的所有請求封裝在此
import detail from './detail'; // 詳細頁面的請求封裝在此
const apiList = {
...login,
...home,
...detail,
};
const install = (Vue) => {
if (install.installed) return;
install.installed = true;
/* 定義屬性到Vue原型中
這樣每一個元件就可以通過this.$api.xxx(data) 去傳送請求
*/
Object.defineProperties(Vue.prototype, {
$api: {
get() {
return apiList;
},
},
});
};
// 匯出一個物件,裡面有install方法。install方法裡就把$api代理到Vue中
export default {
install,
};
複製程式碼
然後在mains.j
s中,就可以這樣寫了
import apis from './apis';
Vue.use(apis);
new Vue(引數);
複製程式碼