Vue提高19 插件

Vue插件学习笔记。

插件和组件的区别

(1)组件分为全局注册和局部注册,全局注册使用Vue.component('componetName', component)实现,全局注册后可以在Vue系统中任意使用,局部注册的组件每次使用都需要import,然后在组件的componentes中注册,它的目的是复用模板和逻辑,影响的范围大多数是组件自身范围内

(2)插件的范围和能力比组件更大,插件内可以包含多个组件,可以在插件内注册全局组件,并且可以实现其他功能,比如:

  • 添加全局方法或者属性(挂载到Vue的静态方法)
  • 添加Vue实例方法(挂载到Vue.prototype上实现),在组件中通过this调用
  • 通过全局混入来添加组件选项(通过Vue.mixin实现),比如在所有组件created的钩子上完成一些功能
  • 添加自定义指令(通过Vue.directive实现)

使用组件

使用组件需要通过全局方法Vue.use()实现,需要在调用new Vue之前完成

1
2
3
4
5
6
7
8
9
10
11
Vue.use(MyDatePicker);

new Vue({
el: '#app',
data: {
eventBus: new Vue()
},
router,
components: { App },
template: '<App/>'
});

也可以传入一个可选的选项对象:

1
Vue.use(MyDatePicker, { someOption: true });

Vue.use会自动阻止多次注册相同插件,即使多次调用也只会注册一次该插件。

开发插件

插件应该暴露一个install方法,提供给Vue.use()调用。

install方法第一个参数是Vue构造器,第二个参数是传入的可选的选项对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或属性
Vue.myGlobalMethod = function () {
// 逻辑...
}

// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})

// 3. 注入组件选项
Vue.mixin({
created: function () {
// 逻辑...
}
...
})

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

通过在Vue构造器上添加一些属性、方法,就可以实现添加全局的方法和属性。

install方法也可以通过Vue.component注册全局组件:

1
2
3
4
5
6
7
import MyDatePicker from './MyDatePicker'

export default {
install(Vue, options) {
Vue.component('MyDatePicker', MyDatePicker);
}
}

例子:开发Loading插件

首先创建一个MyLoading.vue,接受一个msg参数显示在组件内部,loading的效果通过CSS实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<template>
<div class="container my-loading">
<div class="logo"></div>
<p class="text">This is My Loading -- {{msg}}</p>
</div>
</template>

<script>
export default {
props: [ 'msg' ],
name: 'MyLoading'
}
</script>

<style scoped>
.container {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
overflow: hidden;
padding-top: 200px;
}
.logo {
margin: 0 auto;
width: 100px;
height: 100px;
border: 10px solid darkcyan;
border-right-color: darkgray;
border-radius: 50%;
animation: myRotate 2s linear infinite
}
@keyframes myRotate {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
.text {
margin-top: 20px;
text-align: center;
}
</style>

然后创建插件主体文件,创建index.js,这个文件需要导出一个对象,对象中必须有一个install方法,在install方法中来完成主要的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import MyLoading from './MyLoading'

export default {
install(Vue, options) {
const comp = Vue.extend(MyLoading);
Vue.prototype.showMyLoading = (msg) => {
if (document.querySelector('.my-loading')) {
return
}
const tpl = new comp({ propsData: { msg } }).$mount().$el;
document.body.appendChild(tpl);
setTimeout(function () {
document.body.removeChild(tpl);
}, 3000)
}
}
}

在上面的代码中,定义了实例的showPicker方法,在这个方法中首先使用Vue.extend来创建一个继承自MyLoading的Vue子类,然后使用new关键字将其实例化。

注意,使用new创建的实例,想要传递给组件props,必须使用propsData参数

1
2
3
4
5
6
7
8
9
10
var Comp = Vue.extend({
props: ['msg'],
template: '<div>{{ msg }}</div>'
})

var vm = new Comp({
propsData: {
msg: 'hello'
}
})

然后使用$mount方法创建一个未挂载的实例,这个实例没有关联的DOM元素,它返回实例自身,所以可以链式调用其他实例方法,然后通过$el获取实例自身的根DOM元素,通过appendChild将这个元素手动的挂载到DOM中

使用插件的时候在main.js中:

1
2
import MyLoading from '@/plugin/myLoading/index'
Vue.use(MyLoading);

这样在任意Vue中间中就可以调用实例的this.showPicker()方法动态的创建一个Loading元素,3秒后自动消失。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template>
<div>
<h1>使用MyLoading插件</h1>
<button @click="showLoading"> Show Loading</button>
</div>
</template>

<script>
export default {
name: 'demo33',
props: [],
data() {
return {
title: ''
}
},
mounted() {
},
methods: {
showLoading(){
this.showMyLoading('你好')
}
}
}
</script>
<style scoped>
</style>

这是命令式的创建动态实例,适合Loading、对话框等形式的元素。主要的思路和之前总结的笔记《Vue提高09 动态创建实例》类似。

当然也可以在插件中注册全局组件,类似于ElementUI的用法。

参考