vue---使用vue编写插件,基于vue2.x编写常用toast loading插件,一键复用组件

来源:互联网 发布:特比尔定位软件 编辑:程序博客网 时间:2024/06/04 18:04

一直都觉得vue的插件生涩难懂,但是又很好奇,在看了几篇文章,试着写了写之后觉得也没那么难,这篇文就是总结一下这个过程,加深记忆,也可以帮助后来的人。

why


在学习之前,先问问自己,为什么要编写vue的插件。

在一个项目中,尤其是大型项目,有很多部分需要复用,比如加载的loading动画,弹出框。如果一个一个的引用也稍显麻烦,而且在一个vue文件中引用的组件多了,会显得代码臃肿,所以才有了封装vue插件的需求。

说完需求,就来看看具体实现。目前我尝试了两种不一样的插件编写的方法,逐个介绍。
这里写图片描述
一个是loading插件,一个是toast插件,不同的地方在于:loading插件是作为组件引入使用,而toast插件是直接添加在挂载点里,通过方法改变状态调用的。

目前使用起来是酱紫的:

这里写图片描述

toast插件


toast文件下有两个文件,后缀为vue的文件就是这个插件的骨架,js文件一个是将这个骨架放入Vue全局中,并写明操作逻辑。

可以看一下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>

这里面主要的内容只有两个,决定是否显示的show和显示什么内容的message。

粗看这里,有没有发现什么问题?

这个文件中并没有props属性,也就是无论是show也好,message也好,就没有办法通过父子组件通信的方式进行修改,那他们是怎么正确处理的呢。别急,来看他的配置文件。

index.js:

import ToastComponent from './toast.vue'const Toast = {};// 注册ToastToast.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的问题,那好,我们console下这个看看。WTF!!!!结果居然是undefined。
那接着:

console.log(instance)

这里写图片描述
和上一张图片比对一下,发现了什么?对,$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插件


经过上一个插件的讲解,这一部分就不会那么细致了,毕竟大多数都没有什么不同,我只指出不一样的地方。

<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>

这个就只是一个模板,传入两个父组件的数据控制显示效果。

那再来看一下该插件的配置文件:

import LoadingComponent from './loading.vue'let Loading = {};Loading.install = (Vue) => {    Vue.component('loading', LoadingComponent)}export default Loading;

这个和taoat的插件相比,简单了很多,依然是一个空对象,里面有一个install方法,然后在全局注册了一个组件。

比较


那介绍了这两种不同的插件编写方法,貌似没有什么不一样啊,真的是这样么?

来看一下完整的main.js和app.vue这两个文件:

// main.jsimport 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)

之后,该插件就是生成好的了,之后的所有操作无非就是显示或者隐藏的问题了。

转载