實(shí)例教學(xué)如何寫vue插件
在學(xué)習(xí)之前,先問問自己,為什么要編寫vue的插件。
在一個項(xiàng)目中,尤其是大型項(xiàng)目,有很多部分需要復(fù)用,比如加載的loading動畫,彈出框。如果一個一個的引用也稍顯麻煩,而且在一個vue文件中引用的組件多了,會顯得代碼臃腫,所以才有了封裝vue插件的需求。
說完需求,就來看看具體實(shí)現(xiàn)。目前我嘗試了兩種不一樣的插件編寫的方法,逐個介紹。

這是我的項(xiàng)目目錄,大致的結(jié)構(gòu)解釋這樣,盡量簡單,容易理解。
一個是loading插件,一個是toast插件,不同的地方在于:loading插件是作為組件引入使用,而toast插件是直接添加在掛載點(diǎn)里,通過方法改變狀態(tài)調(diào)用的。
目前使用起來是醬紫的:

toast插件
toast文件下有兩個文件,后綴為vue的文件就是這個插件的骨架,js文件一個是將這個骨架放入Vue全局中,并寫明操作邏輯。
可以看一下toast.vue的內(nèi)容:
<template>
<transition name="fade">
<div v-show="show">
{{message}}
</div>
</transition>
</template>
<script>
export default {
data() {
return {
show: false,
message: ""
};
}
};
</script>
<style lang="scss" scoped>
.toast {
position: fixed;
top: 40%;
left: 50%;
margin-left: -15vw;
padding: 2vw;
width: 30vw;
font-size: 4vw;
color: #fff;
text-align: center;
background-color: rgba(0, 0, 0, 0.8);
border-radius: 5vw;
z-index: 999;
}
.fade-enter-active,
.fade-leave-active {
transition: 0.3s ease-out;
}
.fade-enter {
opacity: 0;
transform: scale(1.2);
}
.fade-leave-to {
opacity: 0;
transform: scale(0.8);
}
</style>
這里面主要的內(nèi)容只有兩個,決定是否顯示的show和顯示什么內(nèi)容的message。
粗看這里,有沒有發(fā)現(xiàn)什么問題?
這個文件中并沒有props屬性,也就是無論是show也好,message也好,就沒有辦法通過父子組件通信的方式進(jìn)行修改,那他們是怎么正確處理的呢。別急,來看他的配置文件。
index.js:
import ToastComponent from './toast.vue'
const Toast = {};
// 注冊Toast
Toast.install = function (Vue) {
// 生成一個Vue的子類
// 同時這個子類也就是組件
const ToastConstructor = Vue.extend(ToastComponent)
// 生成一個該子類的實(shí)例
const instance = new ToastConstructor();
// 將這個實(shí)例掛載在我創(chuàng)建的div上
// 并將此div加入全局掛載點(diǎn)內(nèi)部
instance.$mount(document.createElement('div'))
document.body.appendChild(instance.$el)
// 通過Vue的原型注冊一個方法
// 讓所有實(shí)例共享這個方法
Vue.prototype.$toast = (msg, duration = 2000) => {
instance.message = msg;
instance.show = true;
setTimeout(() => {
instance.show = false;
}, duration);
}
}
export default Toast
這里的邏輯大致可以分成這么幾步:
創(chuàng)建一個空對象,這個對象就是日后要使用到的插件的名字。此外,這個對象中要有一個install的函數(shù)。使用vue的extend方法創(chuàng)建一個插件的構(gòu)造函數(shù)(可以看做創(chuàng)建了一個vue的子類),實(shí)例化該子類,之后的所有操作都可以通過這個子類完成。之后再Vue的原型上添加一個共用的方法。
這里需要著重提的是Vue.extend()。舉個例子,我們?nèi)粘J褂胿ue編寫組件是這個樣子的:
Vue.component('MyComponent',{
template:'<div>這是組件</div>'
})
這是全局組件的注冊方法,但其實(shí)這是一個語法糖,真正的運(yùn)行過程是這樣的:
let component = Vue.extend({
template:'<div>這是組件</div>'
})
Vue.component('MyComponent',component)
Vue.extend會返回一個對象,按照大多數(shù)資料上提及的,也可以說是返回一個Vue的子類,既然是子類,就沒有辦法直接通過他使用Vue原型上的方法,所以需要new一個實(shí)例出來使用。
在代碼里console.log(instance)
得出的是這樣的結(jié)果:

可以看到$el:div.toast
也就是toast組件模板的根節(jié)點(diǎn)。
疑惑的是,我不知道為什么要創(chuàng)建一個空的div節(jié)點(diǎn),并把這個實(shí)例掛載在上面。我嘗試注釋這段代碼,但是運(yùn)行會報(bào)錯。

查找這個錯誤的原因,貌似是因?yàn)?/p>
document.body.appendChild(instance.$el)
這里面的instance.$el的問題,那好,我們console下這個看看。WTF!?。?!結(jié)果居然是undefined。
那接著
console.log(instance)

和上一張圖片比對一下,發(fā)現(xiàn)了什么?對,$el消失了,換句話說在我注釋了
instance.$mount(document.createElement('div'))
這句話之后,掛載點(diǎn)也不存在了。接著我試著改了一下這句:
instance.$mount(instance.$el)
$el又神奇的回來了………………
暫時沒有發(fā)現(xiàn)這種改動有什么問題,可以和上面一樣運(yùn)行。但無論如何,這也就是說instance實(shí)例必須掛載在一個節(jié)點(diǎn)上才能進(jìn)行后續(xù)操作。
之后的代碼就簡單了,無非是在Vue的原型上添加一個改變插件狀態(tài)的方法。之后導(dǎo)出這個對象。
接下來就是怎么使用的問題了。來看看main.js是怎么寫的:
import Vue from 'vue'
import App from './App'
// import router from './router'
import Toast from './components/taost'
Vue.use(Toast)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
// router,
render: h => h(App)
}).$mount('#app')
這樣就可以在其他vue文件中直接使用了,像這樣:
// app.vue
<template>
<div id="app">
<loading duration='2s' :isshow='show'></loading>
<!-- <button @click="show = !show">顯示/隱藏loading</button> -->
<button @click="toast">顯示taost彈出框</button>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
show: false
};
},
methods: {
toast() {
this.$toast("你好");
}
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
通過在methods中增加一個方法控制寫在Vue原型上的$toast對toast組件進(jìn)行操作。
這樣toast組件的編寫過程就結(jié)束了,可以看到一開始gif圖里的效果。
loading插件
經(jīng)過上一個插件的講解,這一部分就不會那么細(xì)致了,畢竟大多數(shù)都沒有什么不同,我只指出不一樣的地方。
<template>
<div class='wrapper' v-if="isshow">
<div class='loading'>
<img src="./loading.gif">
</div>
</div>
</template>
<script>
export default {
props: {
duration: {
type: String,
default: "1s" //默認(rèn)1s
},
isshow: {
type: Boolean,
default: false
}
},
data: function() {
return {};
}
};
</script>
<style lang="scss" scoped>
</style>
這個就只是一個模板,傳入兩個父組件的數(shù)據(jù)控制顯示效果。
那再來看一下該插件的配置文件:
import LoadingComponent from './loading.vue'
let Loading = {};
Loading.install = (Vue) => {
Vue.component('loading', LoadingComponent)
}
export default Loading;
這個和taoat的插件相比,簡單了很多,依然是一個空對象,里面有一個install方法,然后在全局注冊了一個組件。
比較
那介紹了這兩種不同的插件編寫方法,貌似沒有什么不一樣啊,真的是這樣么?
來看一下完整的main.js和app.vue這兩個文件:
// main.js
import Vue from 'vue'
import App from './App'
// import router from './router'
import Toast from './components/taost'
import Loading from './components/loading'
Vue.use(Toast)
Vue.use(Loading)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
// router,
render: h => h(App)
}).$mount('#app')
// app.vue
<template>
<div id="app">
<loading duration='2s' :isshow='show'></loading>
<!-- <button @click="show = !show">顯示/隱藏loading</button> -->
<button @click="toast">顯示taost彈出框</button>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
show: false
};
},
methods: {
toast() {
this.$toast("你好");
}
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
可以看出來,loading是顯示的寫在app.vue模板里的,而toast并沒有作為一個組件寫入,僅僅是通過一個方法控制顯示。
來看一下html結(jié)構(gòu)和vue工具給出的結(jié)構(gòu):


看出來了么,toast插件沒有在掛載點(diǎn)里面,而是獨(dú)立存在的,也就是說當(dāng)執(zhí)行
vue.use(toast)
之后,該插件就是生成好的了,之后的所有操作無非就是顯示或者隱藏的問題了。
相關(guān)文章
vue3+vite項(xiàng)目中按需引入vant報(bào)錯:Failed?to?resolve?import的解決方案
最近在vue項(xiàng)目中引入vant的時候發(fā)現(xiàn)報(bào)錯了,經(jīng)過嘗試發(fā)現(xiàn)了問題,現(xiàn)將完整引入流程提供給大家參考,下面這篇文章主要給大家介紹了關(guān)于vue3+vite項(xiàng)目中按需引入vant報(bào)錯:Failed?to?resolve?import的解決方案,需要的朋友可以參考下2022-12-12
vuex頁面刷新數(shù)據(jù)丟失問題的四種解決方式
vuex是大家使用vue時大多數(shù)都會選擇的,但是當(dāng)頁面刷新之后vuex數(shù)據(jù)會丟失,下面這篇文章主要給大家介紹了關(guān)于vuex頁面刷新數(shù)據(jù)丟失問題的四種解決方式,需要的朋友可以參考下2022-02-02
前端vue?uni-app?cc-countdown倒計(jì)時組件使用詳解
cc-countdown是一個倒計(jì)時組件,它可以顯示剩余時間、天數(shù)、小時數(shù)、分鐘數(shù)和秒數(shù),在本文中,我們將介紹如何在uni-app中使用cc-countdown組件,需要的朋友可以參考下2023-08-08

