閱讀此文章,你可以了解到:
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原理
exportfunctioninitUse(Vue: GlobalAPI){? Vue.use =function(plugin: Function | Object){// 獲取已經安裝的插件constinstalledPlugins = (this._installedPlugins || (this._installedPlugins = []))// 看看插件是否已經安裝,如果安裝了直接返回if(installedPlugins.indexOf(plugin) >-1) {returnthis}// toArray(arguments, 1)實現的功能就是,獲取Vue.use(plugin,xx,xx)中的其他參數。// 比如 Vue.use(plugin,{size:'mini', theme:'black'}),就會回去到plugin意外的參數constargs = toArray(arguments,1)// 在參數中第一位插入Vue,從而保證第一個參數是Vue實例args.unshift(this)// 插件要么是一個函數,要么是一個對象(對象包含install方法)if(typeofplugin.install ==='function') {// 調用插件的install方法,并傳入Vue實例plugin.install.apply(plugin, args)? ? }elseif(typeofplugin ==='function') {? ? ? plugin.apply(null, args)? ? }// 在已經安裝的插件數組中,放進去installedPlugins.push(plugin)returnthis}}復制代碼
總結:
Vue.use方法主要做了如下的事:
檢查插件是否安裝,如果安裝了就不再安裝
如果沒有沒有安裝,那么調用插件的install方法,并傳入Vue實例
我們知道了Vue.use做了什么之后。我們看看那些我們常見的插件,是如何利用這個use方法的。
Element中的install
constinstall =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方法的部分拆解出來,只關注其最最核心的邏輯
如果不想讀源碼,可以直接看源碼后面的文字簡單總結
importViewfrom'./components/view'importLinkfrom'./components/link'exportlet_Vueexportfunctioninstall(Vue){? _Vue = VueconstisDef =v=>v !==undefinedconstregisterInstance =(vm, callVal) =>{leti = vm.$options._parentVnodeif(isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {? ? ? i(vm, callVal)? ? }? }? Vue.mixin({? ? beforeCreate () {// 如果該組件是根組件if(isDef(this.$options.router)) {//? 設置根組件叫_routerRootthis._routerRoot =this// 根組件的_router屬性為,new Vue傳進去的router// $options是在mains.js中,new Vue里的參數,在這里我們傳入的參數,this._router =this.$options.routerthis._router.init(this)// 通過defineReactive方法,來把this._router.history.current變成響應式的,這個方法的底層就是object.definePropertyVue.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 () {returnthis._routerRoot._router }? })// 同理,這樣就是把自身的$route,代理到根組件傳入的routeObject.defineProperty(Vue.prototype,'$route', {? ? get () {returnthis._routerRoot._route }? })// 注冊 <router-view>組件Vue.component('RouterView', View)// 注冊<router-link>組件Vue.component('RouterLink', Link)conststrats = Vue.config.optionMergeStrategies// use the same hook merging strategy for route hooksstrats.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實例與插件的一座橋梁。
來源鏈接:https://juejin.cn/post/6844903946343940104