开发Vue插件四种方式

在平常开发中,可能只须要一两个插件就能够完成对系统开发须要。若是引入整个组件库,最后打包项目体积比较大。例如element-ui中的message提示组件等。下面会在vuejs官网提供的插件建议,根据四种方法开发不一样的vuejs插件javascript

插件

插件一般用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——通常有下面几种:css

  1. 添加全局方法或者属性。如: vue-custom-element
  2. 添加全局资源:指令/过滤器/过渡等。如 vue-touch
  3. 经过全局混入来添加一些组件选项。如 vue-router
  4. 添加 Vue 实例方法,经过把它们添加到 Vue.prototype 上实现。
  5. 一个库(例如element-ui),提供本身的 API,同时提供上面提到的一个或多个功能。如vue-router

initUse安装插件函数

vuejs源码src/core/global-api/use.js中能够阅读到vuejs插件须要export一个install函数。vue使用indexOf检测插件是否已注册,防止插件的重复注册。html

import { toArray } from '../util/index'

export function initUse (Vue: GlobalAPI) {
  Vue.use = function (plugin: Function | Object) {
    const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
    if (installedPlugins.indexOf(plugin) > -1) {
      return this
    }

    // additional parameters
    const args = toArray(arguments, 1)
    args.unshift(this)
    if (typeof plugin.install === 'function') {
      plugin.install.apply(plugin, args)
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args)
    }
    installedPlugins.push(plugin)
    return this
  }
}
复制代码

指令directive方式开发插件

一个指令定义对象能够提供以下几种钩子函数(都为可选):vue

指令钩子函数

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性初始化设置
  • inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不必定已被插入文档中)。
  • update:所在组件的VNode更新时调用,可是可能发生在其子VNode更新以前。指令值可能发生了变化,也可能没有发生变华。
  • componentUpdated:指令所在组件的VNode及子VNode所有更新后调用
  • unbind:只调用一次,指令与元素解绑时调用

钩子函数参数

指令钩子函数会被传入如下参数:java

  • el:指令所绑定的元素,能够用来直接操做DOM
  • binding:一个对象,包含如下属性
    • name:指令名,不包含v-前缀
    • value:指令的绑定值,例如:v-my-direactive= "1+1"中表达式值为2
    • oldValue:指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用。不管值是否改变均可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1"中,表达式为 "1 + 1"
  • arg:传给指令的参数,可选。例如 v-my-directive:foo中,参数为 foo
  • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }

上面说明文档是在vuejs官方文档教程中摘抄的,能够经过这里阅读而更多react

v-time插件

vill-directive插件地址git

vuejs插件须要提供一个install函数向外暴露。在src/directives/time下的index.js中一共开发了三个不一样关于time的指令v-timev-clockv-downgithub

import Time from "./time.js";
export default {
  install(Vue, options = {}) {}
};
复制代码
  • v-time显示当前时间指令

v-time获取一个传入的时间戳值binding.value,而后返回一个符合格式的timevue-router

import Time from "./time.js";
export default {
  install(Vue, options = {}) {
     Vue.directive("time", {
        bind(el, binding) {
          el.innerHTML = el.innerHTML ? el.innerHTML : el.textContent;
          el.innerHTML = Time.getFormatTime(binding.value);
        }
     });
  }
};
复制代码
  • v-clock时钟指令 v-clock每隔一秒获取一次当前时间实现时钟的效果。
import Time from "./time.js";
export default {
  install(Vue, options = {}) {
    Vue.directive("clock", {
      bind(el, binding) {
        el.timeout = setInterval(function() {
          const value = Date.now();
          el.innerText = Time.getFormatTime(value);
        }, 1000);
      },
      unbind() {
        clearInterval(el.timeout);
        delete el.timeout;
      }
    });
  }
};
复制代码
  • v-down给定时间倒计时指令

v-down传入将来的一个时间戳,计算倒计时间。express

import Time from "./time.js";
export default {
  install(Vue, options = {}) {
    Vue.directive("down", {
      bind(el, binding) {
        const value = binding.value;
        el.__handle__ = setInterval(() => {
          if (Time.getDownTime(value).clear) {
            clearInterval(el.__handle__);
            el.innerText = Time.getDownTime(value).time;
            return;
          }
          el.innerText = Time.getDownTime(value);
        }, 1000);
      },
      unbind() {
        clearInterval(el.__timeout__);
        delete el.__timeout__;
      }
    });
  }
};
复制代码
  • 格式化时间函数

getFormatTimeYYYY-MM-DD hh:mm:ss时间格式的函数,computeTime是计算传入时间与当前时间差值得格式时间。

export default {
  getFormatTime(value) {
    if (isNaN(value)) {
      console.error("the value is not a number");
      return;
    }
    const date = new Date(value);
    const year = date.getFullYear();
    const month = this.format(date.getMonth() + 1);
    const day = this.format(date.getDate());
    const hours = this.format(date.getHours());
    const minutes = this.format(date.getMinutes());
    const seconds = this.format(date.getSeconds());
    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  },

  format(value) {
    return value > 9 ? value : "0" + value;
  },
  getDownTime(value) {
    const date = new Date(value);
    const now = Date.now();
    const count = (date - now) / 1000;
    if (count <= 0) {
      return {
        clear: true,
        time: "00天00时00分00秒"
      };
    }
    return this.computeTime(count);
  },
  computeTime(value) {
    const day = this.format(Math.floor(value / 86400));
    const hours = this.format(Math.floor((value % 86400) / 3600));
    const minutes = this.format(Math.floor(((value % 86400) % 3600) / 60));
    const seconds = this.format(Math.floor(((value % 86400) % 3600) % 60));
    return `${day}${hours}${minutes}${seconds}秒`;
  }
};
复制代码

使用和运行效果

  • 使用方法
//在入口文件main.js
import time from 'vill-directive'
Vue.use(time);
//其余组件
<template>
  <div class="demo">
    <h4>v-time 指令</h4>
    <span v-time="now"></span>
    <h4>v-clock 指令</h4>
    <span v-clock></span>
    <h4>v-down 指令</h4>
    <span v-down="time"></span>
  </div>
</template>
<script>
export default {
  name: "Demo",
  data() {
    return {
      time: "2019-03-20 13:16:00"
    };
  },
  computed: {
    now() {
      return Date.now();
    }
  }
};
</script>
<style scoped></style>
复制代码
  • 运行效果

demo

prototype方式开发插件

经过prototype方式开发插件,是在vue的原型上添加属性或者方法。例如:

// 添加实例方法
  Vue.prototype.$myMethod = function (methodOptions) {
    // 逻辑...
 }
复制代码

message提示插件

message插件地址

  • 挂载方法或属性到prototype

src/lib/vill-message中的index.js中,定义了vuejsinstall函数,主要是把方法和属性添加到vue的原型上。

import Message from './main.js';
export default {
    install(Vue,options={}){
        Vue.prototype.$message = Message;
    }
};
复制代码
  • 引入DOM元素并进行挂载

Vue.extend使用基础 Vue 构造器,建立一个“子类”。options是能够传入的参数配置。而后对数据进行手动挂载$mount,最后加入document文档中。

import Vue from "vue";
import MessageTpl from "./message.vue";
const NoticeConstructor = Vue.extend(MessageTpl);
let nId = 1;
const Message = options => {
  if(JSON.stringify(options) == undefined)
	return false
  let id = "notice-" + nId++;
  options = options || {};
  if (typeof options === "string") {
    options = {
      message: options
    };
  }

  const NoticeInstance = new NoticeConstructor({
    data: options
  });
  NoticeInstance.id = id;
  NoticeInstance.vm = NoticeInstance.$mount();
  NoticeInstance.vm.visible = true;
  NoticeInstance.dom = NoticeInstance.vm.$el;
  document.body.appendChild(NoticeInstance.dom);
  return NoticeInstance.vm;
};
["success", "warning", "info", "error"].forEach(type => {
  Message[type] = options => {
    if (typeof options === "string") {
      options = {
        message: options
      };
    }
    options.type = type;
    return Message(options);
  };
});
export default Message;
复制代码
  • message.vue模板

message是传入的参数值,是提示的内容值;Icon是一个图标组件。

<template>
  <transition name="vill-message-fade">
    <div v-if="visible" :class="wrapClasses">
        <Icon :iconType="type"></Icon>
        <span :class="[prefixCls+'-content']">
          {{message}}
        </span>
    </div>        
  </transition>
</template>

<script>
const prefixCls = "vill-message";
import Icon from "../vill_icon/index.js";
export default {
  name: "vill-message",
  data() {
    return {
      visible: false,
      type: "info",
      message: "",
      duration: 1500,
      prefixCls: prefixCls
    };
  },
  components: {
    Icon: Icon
  },
  computed: {
    wrapClasses() {
      return [`${prefixCls}`, `${prefixCls}-${this.type}`];
    }
  },
  methods: {
    setTimer() {
      setTimeout(() => {
        this.close(); // 3000ms以后调用关闭方法
      }, this.duration);
    },
    close() {
      this.visible = false;
      setTimeout(() => {
        this.$destroy(true);
        this.$el.parentNode.removeChild(this.$el); // 从DOM里将这个组件移除
      }, 500);
    }
  },
  mounted() {
    this.setTimer(); // 挂载的时候就开始计时,3000ms后消失
  }
};
</script>
复制代码

Icon图标组件采起的render函数进行渲染,根据传入的参数successerrorwarninginfo,直接渲染同名SVG图标(有点取巧),这样避免v-ifv-else的多个条件判断的作法。

<script>
const prefixCls = "vill-icon";
export default {
  name: "vill-icon",
  data() {
    return {
      prefixCls: prefixCls
    };
  },
  props: {
    iconType: {
      type: String,
      default: "info"
    }
  },
  render: function(h) {
    return h(
      "i",
      {
        attrs: {
          class: `${this.prefixCls}`
        }
      },
      [
        h("img", {
          attrs: {
            src: require(`./assets/${this.iconType}.svg`),
            class: "icon"
          }
        })
      ]
    );
  }
};
</script>
<style scoped lang="scss">
.vill-icon {
  display: inline-block;
  width: 20px;
  height: 20px;
  color: #fff;
  overflow: hidden;
  border-radius: 50%;
  margin: 0 15px;
  & .icon {
    display: inline-block;
    width: 100%;
    height: 100%;
  }
}
</style>
复制代码
  • 使用方式
//在main入口
import message from 'vill-message'
Vue.use(message);
//在其余vue文件
this.$message({
    duration:2000,
    type:'success',
    message:'hello vill-message'
});
this.$message({
    duration:2000,
    type:'error',
    message:'hello vill-message'
});
this.$message.success("hello vill-message");
this.$message.error("hello vill-message");
this.$message.warning("hello vill-message");
this.$message.info("hello vill-message");
复制代码
  • 运行效果

demo1

Mixin开发插件

"混入 (mixin) 提供了一种很是灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象能够包含任意组件选项。当组件使用混入对象时,全部混入对象的选项将被“混合”进入该组件自己的选项。"

mixin使用比较简单,能够定义经常使用method或者生命周期函数在Minxin中,而后混入各组件之中。

// 定义一个混入对象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// 定义一个使用混入对象的组件
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component() // => "hello from mixin!"
复制代码

添加Vue全局方法或者属性方式

添加vue全局方法和属性开发vue插件跟prototype比较相似,差异只是在把属性或者方法绑定在prototype改为直接绑定在vue实例上。以下所示:

Vue.$myMethod = function (methodOptions) {
    // 逻辑...
 }
复制代码

其余message.vue组件模板彻底和prototype原型上同样。

若是以为喜欢能够给个赞~

vill-directive地址:github.com/Harhao/vill…

vill-message地址:github.com/Harhao/vill…