開發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…

相關文章
相關標籤/搜索