在日常開發中,可能只需要一兩個外掛就可以完成對系統開發需要。如果引入整個元件庫,最後打包專案體積比較大。例如
element-ui
中的message
提示元件等。下面會在vuejs
官網提供的外掛建議,根據四種方法開發不同的vuejs
外掛
外掛
外掛通常用來為 Vue 新增全域性功能。外掛的功能範圍沒有嚴格的限制——一般有下面幾種:
- 新增全域性方法或者屬性。如: vue-custom-element
- 新增全域性資源:指令/過濾器/過渡等。如 vue-touch
- 通過全域性混入來新增一些元件選項。如 vue-router
- 新增 Vue 例項方法,通過把它們新增到
Vue.prototype
上實現。 - 一個庫(例如
element-ui
),提供自己的 API,同時提供上面提到的一個或多個功能。如vue-router
initUse
安裝外掛函式
在vuejs
原始碼src/core/global-api/use.js
中可以閱讀到vuejs
外掛需要export
一個install
函式。vue
使用indexOf
檢測外掛是否已註冊,防止外掛的重複註冊。
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
方式開發外掛
一個指令定義物件可以提供如下幾種鉤子函式(都為可選):
指令鉤子函式
bind
:只呼叫一次,指令第一次繫結到元素時呼叫。在這裡可以進行一次性初始化設定inserted
:被繫結元素插入父節點時呼叫(僅保證父節點存在,但不一定已被插入文件中)。update
:所在元件的VNode
更新時呼叫,但是可能發生在其子VNode
更新之前。指令值可能發生了變化,也可能沒有發生變華。componentUpdated
:指令所在元件的VNode
及子VNode
全部更新後呼叫unbind
:只呼叫一次,指令與元素解綁時呼叫
鉤子函式引數
指令鉤子函式會被傳入以下引數:
el
:指令所繫結的元素,可以用來直接操作DOM
binding
:一個物件,包含以下屬性name
:指令名,不包含v-
字首value
:指令的繫結值,例如:v-my-direactive= "1+1"
中表示式值為2
oldValue
:指令繫結的前一個值,僅在update
和componentUpdated
鉤子中可用。無論值是否改變都可用。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
官方文件教程中摘抄的,可以通過這裡閱讀而更多
v-time
外掛
vuejs
外掛需要提供一個install
函式向外暴露。在src/directives/time
下的index.js
中一共開發了三個不同關於time
的指令v-time
、v-clock
、v-down
import Time from "./time.js";
export default {
install(Vue, options = {}) {}
};
複製程式碼
v-time
顯示當前時間指令
v-time
獲取一個傳入的時間戳值binding.value
,然後返回一個符合格式的time
值
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
傳入未來的一個時間戳,計算倒數計時間。
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__;
}
});
}
};
複製程式碼
- 格式化時間函式
getFormatTime
是YYYY-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>
複製程式碼
- 執行效果
prototype
方式開發外掛
通過prototype
方式開發外掛,是在vue
的原型上新增屬性或者方法。例如:
// 新增例項方法
Vue.prototype.$myMethod = function (methodOptions) {
// 邏輯...
}
複製程式碼
message
提示外掛
- 掛載方法或屬性到
prototype
在src/lib/vill-message
中的index.js
中,定義了vuejs
的install
函式,主要是把方法和屬性新增到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
函式進行渲染,根據傳入的引數success
、error
、warning
、info
,直接渲染同名SVG
圖示(有點取巧),這樣避免v-if
和v-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");
複製程式碼
- 執行效果
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…