在日常开发中,可能只需要一两个插件就可以完成对系统开发需要。如果引入整个组件库,最后打包项目体积比较大。例如
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.getDay());
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地址:https://github.com/Harhao/vill-directive
vill-message地址:https://github.com/Harhao/vill-messgae
网友评论