Vue——插件的两种实现方式
为什么要使用Vue插件?随着项目编写的进行,代码量越来越多,重复的内容随之增加,比如每个页面可能都会有Toast提示,用户交互的每个操作都可能有提示框,同个项目中这些提示的样式都是统一的,那么我们没必要每个页面每个交互动作都写一个提示组件,我们可以创建一个通用的组件(全局组件),然后再每个需要的地方调用,减少代码量,简化代码结构,同时如果有新的UI需求需要修改也方便快捷,只需修改全局组件即可。..
为什么要使用Vue插件?
随着项目编写的进行,代码量越来越多,重复的内容随之增加,比如每个页面可能都会有Toast提示,用户交互的每个操作都可能有提示框,同个项目中这些提示的样式都是统一的,那么我们没必要每个页面每个交互动作都写一个提示组件,我们可以创建一个通用的组件(全局组件),然后再每个需要的地方调用,减少代码量,简化代码结构,同时如果有新的UI需求需要修改也方便快捷,只需修改全局组件即可。
现在我们来创建两个组件分别是Toast和Loading。不同的地方在于:loading插件是作为组件引入使用,而toast插件是直接添加在挂载点里,通过方法改变状态调用的。
首先看下效果图:
代码实现如下:
Toast插件
Toast.vue(组件文件)
<template>
<transition name="fade">
<div class="toast" 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>
Toast.js(配置文件)
import ToastComponent from './toast.vue'
const Toast = {};
// 注册Toast
Toast.install = function (Vue) {
// 生成一个Vue的子类
// 同时这个子类也就是组件
const ToastConstructor = Vue.extend(ToastComponent)
// 生成一个该子类的实例
const instance = new ToastConstructor();
// 将这个实例挂载在我创建的div上
// 并将此div加入全局挂载点内部
instance.$mount(document.createElement('div'))
document.body.appendChild(instance.$el)
// 通过Vue的原型注册一个方法
// 让所有实例共享这个方法
Vue.prototype.$toast = (msg, duration = 2000) => {
instance.message = msg;
instance.show = true;
setTimeout(() => {
instance.show = false;
}, duration);
}
}
export default Toast
这里的逻辑大致可以分成这么几步:
- 1、创建一个空对象,这个对象就是日后要使用到的插件的名字。此外,这个对象中要有一个install的函数。
- 2、使用vue的extend方法创建一个插件的构造函数(可以看做创建了一个vue的子类),实例化该子类,之后的所有操作都可以通过这个子类完成。
- 3、之后再Vue的原型上添加一个共用的方法。
这里需要着重提的是Vue.extend()。举个例子,我们日常使用vue编写组件是这个样子的:
Vue.component('MyComponent',{
template:'<div>这是组件</div>'
})
这是全局组件的注册方法,但其实这是一个语法糖,真正的运行过程是这样的:
let component = Vue.extend({
template:'<div>这是组件</div>'
})
Vue.component('MyComponent',component)
Vue.extend会返回一个对象,按照大多数资料上提及的,也可以说是返回一个Vue的子类,既然是子类,就没有办法直接通过他使用Vue原型上的方法,所以需要new一个实例出来使用。
在代码里console.log(instance)得到的结果是:
$el:div.toast
也就是toast组件模板的根节点。
疑惑的是,我不知道为什么要创建一个空的div节点,并把这个实例挂载在上面。我尝试注释这段代码,但是运行会报错。
查找这个错误的原因,貌似是因为
document.body.appendChild(instance.$el)
这里面的instance.$el的问题,那好,我们打印下instance的结果看看,WTF!!!!居然是undefined。
和上一张图片比对一下,发现了什么?对,$el消失了,换句话说在我注释了
instance.$mount(document.createElement('div'))
这句话之后,挂载点也不存在了。接着我试着改了一下这句:
instance.$mount(instance.$el)
$el又神奇的回来了………………
暂时没有发现这种改动有什么问题,可以和上面一样运行。但无论如何,这也就是说instance实例必须挂载在一个节点上才能进行后续操作。
之后的代码就简单了,无非是在Vue的原型上添加一个改变插件状态的方法。之后导出这个对象。
接下来就是怎么使用的问题了。来看看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组件进行操作。
这样toast组件的编写过程就结束了,可以看到一开始gif图里的效果。
Loading插件
Loading.vue
<template>
<div class='wrapper' v-if="isshow">
<div class='loading'>
<img src="./loading.gif" alt="" width="40" height="40">
</div>
</div>
</template>
<script>
export default {
props: {
duration: {
type: String,
default: "1s" //默认1s
},
isshow: {
type: Boolean,
default: false
}
},
data: function() {
return {};
}
};
</script>
<style lang="scss" scoped>
</style>
这个就只是一个模板,传入两个父组件的数据控制显示效果。
那再来看一下该插件的配置文件:
Loading.js
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结构和vue工具给出的结构:
看出来了么,toast插件没有在挂载点里面,而是独立存在的,也就是说当执行
vue.use(toast)
之后,该插件就是生成好的了,之后的所有操作无非就是显示或者隐藏的问题了。
原文链接:简单的例子实现vue插件
更多推荐
所有评论(0)