其实在 Vue 文档已经对插件的使用和功能有详细的说明,这边也就重复再稍微补充实例。
插件的作用
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
-
添加全局方法或者 property。如:vue-custom-element
-
添加全局资源:指令/过滤器/过渡等。如 vue-touch
-
通过全局混入来添加一些组件选项。如 vue-router
-
添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
-
一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
可以发现,插件的作用主要是帮我们实现一些 vue 的 “全局功能”,比如全局指令,过滤器,组件,注入到原型对象(prototype)的方法或属性。
插件的使用
通过全局方法 Vue.use() 使用插件。它需要在你调用 new Vue() 启动应用之前完成:
// 调用 `MyPlugin.install(Vue)`
Vue.use(MyPlugin)
new Vue({
// ...组件选项
})
也可以传入一个可选的选项对象:
Vue.use(MyPlugin, { someOption: true })
Vue.use 会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件。
插件的注册原理
明白插件的使用后,我们可以看看插件是如何注册的,主要包括如何自动调用 install 方法,插件选项传递,及防止重复注册。
/**
* Convert an Array-like object to a real Array.
*/
export function toArray (list: any, start?: number): Array<any> {
start = start || 0
let i = list.length - start
const ret: Array<any> = new Array(i)
while (i--) {
ret[i] = list[i + start]
}
return ret
}
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)
// 根据this指向调用者 this === Vue
args.unshift(this)
// 兼容
if (typeof plugin.install === 'function') {
// 插件选项传递 此时第一个参数为Vue
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
// 缓存注册列表
installedPlugins.push(plugin)
return this
}
}
use 方法的源码非常简单,从函数的注释中我们可以很清楚地解释上面的疑问。
开发插件
插件的开发其实就是利用 use 方法中传入的 Vue 函数,利用 Vue.directive Vue.component 等全局方法来实现我们的功能。
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或 property
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 3. 注入组件选项
Vue.mixin({
created: function () {
// 逻辑...
}
...
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}
可能你和我会有一样的疑惑,为什么需要使用 use 调用插件来完成注册并实现功能,在 use 之外不是也可以正常调用 Vue 的静态方法来实现我们需要的功能么?答案当然可以,但我想在 Vue 中定义插件应该是为了更好的封装及解耦,将插件代码独立封装,和业务代码解耦,同时定义了插件的统一安装注册规范,标准化让调用更加简单且统一。
插件开发实例
网上有很多插件开发实例,这里借用别人的一个loading插件实现。
首先使用组件的方式来创建UI
<template>
<div class="container" v-show="show">
<div class="loading">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</template>
<script>
export default {
data () {
return {
show: false
};
}
};
</script>
<style lang="less" scoped>
.container {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 1000;
span {
display: inline-block;
width: 8px;
height: 80px;
background: rgb(255, 107, 186);
border-radius: 4px;
animation: load 1.6s ease infinite;
}
@keyframes load {
0%,
100% {
height: 80px;
background: rgb(255, 107, 186);
}
50% {
height: 20px;
margin-top: 60px;
background: rgb(245, 15, 53);
}
}
span:nth-child(2) {
animation-delay: 0.2s;
}
span:nth-child(3) {
animation-delay: 0.4s;
}
span:nth-child(4) {
animation-delay: 0.6s;
}
span:nth-child(5) {
animation-delay: 0.8s;
}
}
</style>
编写插件函数
import LoadingComponent from './loading.vue';
// 组件实例
let $vm;
const MyPlugin = {
install (Vue) {
if (!$vm) {
// 通过extend创建子类并挂载节点
const TemplateConstructor = Vue.extend(LoadingComponent);
$vm = new TemplateConstructor().$mount(document.createElement('div'));
document.body.appendChild($vm.$el);
}
$vm.show = false;
// 这边其实是个闭包的运用
const loading = {
show () {
$vm.show = true;
},
hide () {
$vm.show = false;
}
};
// 挂载在原型对象 实现vue实例this调用
Vue.prototype.$loading = loading;
}
};
export default MyPlugin;
注册插件
import MyPlugin from './loading'
Vue.use(MyPlugin);
在vue实例中使用
mounted() {
this.$loading.show();
}
总结
学习Vue.use的原因是之前面试的时候遇到面试官问Vue.use的原理没有回答出来,其实如果有去了解过的都能弄明白,原理很简单。谁让自己没去了解过呢,只好哑巴吃黄连了。下次你再问我试试?
参考
欢迎到前端菜鸟群一起学习交流~516913974