Vue 2.0学习笔记:如何创建Vue插件

发布于 大漠

Vue插件是向应用程序添加全局特性的一种强大而又简单的方法。它有多种用途,从分发应用程序范围的组件到向应用程序添加路由和不可变数据存储等附加功能。从概念上讲,Vue插件是非常简单。它实际上只是一个带有install函数的对象,它接受两个参数:全局的Vue对象一个包含用户定义选项的对象。然而,一个像这样简单的Vue插件仍然可以得到相当大的效果。

Vue插件使用范围

插件通常会为Vue添加全局功能。插件的范围没有限制,一般有以下几种:

  • 添加全局方法或者属性,如 vue-custom-element
  • 添加全局资源:指令、过滤器、过渡等,如vue-touch
  • 通过全局mixins方法添加一些组件选项,如 vue-router
  • 添加Vue实例方法,通过把它们添加到Vue.prototype上实现
  • 一个库,提供自己的API,同时提供上面提到的一个或多个功能,如 vue-router

开发插件

Vue的插件有一个公开方法install。这个方法有两个参数,第一个参数是Vue构造器,第二个参数是一个可选的选项对象。比如:

export default {
    install(Vue, options) {
        // 1. 添加全局方法或属性
        Vue.myGlobalMethods = function () {
            console.log(`添加全局方法或属性`)
        }

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

        // 3. 注入组件
        Vue.mixin({
            created () {
                console.log(`注入组件`)
            }
        })

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

接下来,咱们简单的一一介绍一下。

为了更好的向大家演示,先在src目录下创建一个新的目录plugins,并且在这个目录下创建一个my-plugins.js文件。

添加全局方法或属性

// /src/plugins/my-plugins.js
export default {
    install (Vue, options) {
        Vue.$data = {
            firstName: '大漠',
            lastName: 'W3cplus'
        }
        console.log(`${Vue.$data.firstName}_${Vue.$data.lastName}`)
    }
}

install方法中,我们直接在Vue实例上声明了$data属性,并进行了赋值。当该插件注册后,只要存在Vue实例的地方你都可以获取到Vue.$data的值,比如我们上面的示例,输出Vue.$data中的firstNamelastName

console.log(`${Vue.$data.firstName}_${Vue.$data.lastName}`)

输出的结果如下:

能输出该值,那是因为其值直接绑定在Vue实例上。

添加全局资源

// /src/plugins/my-plugins.js

export default {
    install (Vue, options) {
        Vue.$data = {
            firstName: '大漠',
            lastName: 'W3cplus'
        }
        console.log(`${Vue.$data.firstName}_${Vue.$data.lastName}`)

        Vue.directive('hello', { 
            // 只调用一次,指令第一次绑定到元素时调用 
            bind: function (el) { 
                console.log('触发bind钩子函数!') 
            }, 
            // 被绑定元素插入父节点时调用 
            inserted: function (el) { 
                console.log('触发inserted钩子函数!') 
            }, 
            // 所在组件的`VNode`更新时调用,但是可能发生在其子元素的`VNode`更新之前 
            update: function (el) { 
                console.log('触发update钩子函数!') 
            }, 
            // 所在组件的`VNode`及其子元素的`VNode`全部更新时调用 
            componentUpdated: function (el) { 
                console.log('触发componentUpdated钩子函数!') 
            }, 
            // 只调用一次,指令与元素解绑时调用 
            unbind: function (el) { 
                console.log('触发unbind钩子函数!') 
            } 
        })
    }
}

添加全局资源包含了添加全局指令,过滤器、过渡等。上面的示例,通过Vue.directive()给Vue添加了一个全局的v-focus指令。这个指令包含了bindinsertedupdateupdatecomponentUpdatedunbind五种方法。

有关于Vue指令方面更详细的介绍可以阅读《自定义指令》和《Vue 自定义指令的魅力》。

添加全局的Mixins方法

// /src/plugins/my-plugins.js

export default {
    install (Vue, options) {
        Vue.$data = {
            firstName: '大漠',
            lastName: 'W3cplus'
        }
        console.log(`${Vue.$data.firstName}_${Vue.$data.lastName}`)

        Vue.directive('hello', { 
            // 只调用一次,指令第一次绑定到元素时调用 
            bind: function (el) { 
                console.log('触发bind钩子函数!') 
            }, 
            // 被绑定元素插入父节点时调用 
            inserted: function (el) { 
                console.log('触发inserted钩子函数!') 
            }, 
            // 所在组件的`VNode`更新时调用,但是可能发生在其子元素的`VNode`更新之前 
            update: function (el) { 
                console.log('触发update钩子函数!') 
            }, 
            // 所在组件的`VNode`及其子元素的`VNode`全部更新时调用 
            componentUpdated: function (el) { 
                console.log('触发componentUpdated钩子函数!') 
            }, 
            // 只调用一次,指令与元素解绑时调用 
            unbind: function (el) { 
                console.log('触发unbind钩子函数!') 
            } 
        })

        Vue.mixin({
            methods: {
                update (){ 
                    this.message = 'Hi! 大漠'
                }, 
                uninstall () { 
                    this.message = '' 
                }, 
                install () { 
                    this.message = 'Hello!W3cplus'
                } 
            }
        })
    }
}

mixin可以注册一个全局的mixins,其会影响注册之后创建的每个Vue实例。上面的示例,注册了updateuninstallinstall几个方法,在组件中可以直接调用这几个方法。如果实例中存在同名方法,则会覆盖mixin中创建的,如果是同名的钩子函数,将会在组件之前调用。

有关于Vue的mixins更详细的介绍,可以阅读《Vue中的Mixins》一文。

添加实例方法

// /src/plugins/my-plugins.js

export default {
    install (Vue, options) {
        Vue.$data = {
            firstName: '大漠',
            lastName: 'W3cplus'
        }
        console.log(`${Vue.$data.firstName}_${Vue.$data.lastName}`)

        Vue.directive('hello', { 
            // 只调用一次,指令第一次绑定到元素时调用 
            bind: function (el) { 
                console.log('触发bind钩子函数!') 
            }, 
            // 被绑定元素插入父节点时调用 
            inserted: function (el) { 
                console.log('触发inserted钩子函数!') 
            }, 
            // 所在组件的`VNode`更新时调用,但是可能发生在其子元素的`VNode`更新之前 
            update: function (el) { 
                console.log('触发update钩子函数!') 
            }, 
            // 所在组件的`VNode`及其子元素的`VNode`全部更新时调用 
            componentUpdated: function (el) { 
                console.log('触发componentUpdated钩子函数!') 
            }, 
            // 只调用一次,指令与元素解绑时调用 
            unbind: function (el) { 
                console.log('触发unbind钩子函数!') 
            } 
        })

        Vue.mixin({
            methods: {
                update (){ 
                    this.message = 'Hi! 大漠'
                }, 
                uninstall () { 
                    this.message = '' 
                }, 
                install () { 
                    this.message = 'Hello!W3cplus'
                } 
            }
        })

        Vue.prototype.$message = '我是一只小小鸟....'
        Vue.prototype.showMessage = value => {
            console.log(value)
        }
    }
}

添加实例方法是最常用的一种方法,其直接绑定在vue的原型链上。

使用插件

上面演示的是如何创建一个插件。如果插件未被调用的话,这个插件是不会起任何作用的。在Vue使用已创建好的插件很容易。

在Vue中可以通过全局方法Vue.use()使用插件。它需要在你调用new Vue()启动应用之前完成。

// main.js

import myPlugins from './plugins/my-plugins'
Vue.use(myPlugins)

使用Vue.use()调用插件时,也可以传入一个选项对象:

Vue.use(myPlugins, { someOption: true })

Vue.use() 会自动阻止多次注册相同插件,届时只会注册一次该插件。

Vue官方提供的一些插件 (例如 vue-router) 在检测到 Vue 是可访问的全局变量时会自动调用 Vue.use()。然而在例如 CommonJS 的模块环境中,你应该始终显式地调用 Vue.use()

// 用 Browserify 或 webpack 提供的 CommonJS 模块环境时
var Vue = require('vue')
var VueRouter = require('vue-router')

// 不要忘了调用此方法
Vue.use(VueRouter)

比如上面写的一个插件,其效果如下所示:

案例1:写一个toast插件

平时在写项目的时候都会碰到一个类似这样的需求,就是项目在接口或网络请求失败的时候,会有一个提示信息出来。针对这样的需求,再配合我们上面学习到的知识,我们就可以写一个toast方面的插件。

这样的一个需求很简单,其类似于一个Modal(简易型的模态弹框)。在<body>末尾处插入一个div用来显示提示信息,这个 div默认是隐藏的,当触发某个机制的时候才显示这个div,并且显示在页面的最顶层。

接下来我们就来写一上这样的插件。类似前面示例一样,先在src下面创建一个plugins目录,同时创建一个名叫toast.js文件,该文件用来放置这个插件所需要所有代码。

// src/plugins/toast.js

export default {
    install (Vue, options) {
        Vue.prototype.$toast = (tips) => {
            let toastTpl = Vue.extend({
                // 创建构造器,定义toast模板
                template: `<div class="toast"> ${tips} </div>`
            })

            // 创建实例,挂载到文档以后的地方
            let tpl = new toastTpl().$mount().$el

            // 把创建的实例添加到body中
            document.body.appendChild(tpl)

            // 延迟3000ms后移除toast模板
            setTimeout(() => {
                document.body.removeChild(tpl)
            }, 3000)
        }
    }
}

上面的示例,我们把toast持续的时间定在了3000ms,是一个写死的值,不太灵活,如果我们想要灵活一点,应该把这个时间提取出来,让使用者来控制。要实现这个需求,我们可以借助install()函数的第二个参数options来处理。这样在使用Vue.use()就可以通过options把所要持续的时间传进去。接下来就是修改后的代码:

// src/plugins/toast.js
export default {
    install (Vue, options) {
        let opt = {
            duration: '3000'
        }

        Vue.prototype.$toast = (tips) => {
            let toastTpl = Vue.extend({
                // 创建构造器,定义toast模板
                template: `<div class="toast"> ${tips} </div>`
            })

            // 创建实例,挂载到文档以后的地方
            let tpl = new toastTpl().$mount().$el

            // 把创建的实例添加到body中
            document.body.appendChild(tpl)

            // 延迟3000ms后移除toast模板
            setTimeout(() => {
                document.body.removeChild(tpl)
            }, opt.duration)
        }
    }
}

接下来,在main.js中调用toast插件。

import toast from "./plugins/toast";

Vue.use(toast, {
    duration: 2500
});

然后在需要出现toast组件的地方,使用即可:

<!-- App.vue -->
<template>
    <div id="app">
        <img width="25%" src="./assets/logo.png">
        <hr>
        <button @click="open">Open Toast</button>
    </div>
</template>

<script>

export default {
    name: "App",
    components: {
    },
    methods: {
        open() {
            this.$toast('网络请求失败,请刷新你的页面!')
        }
    }
};
</script>

最终效果如下:

@Joshua Bemenderfer分享过一篇《Easy Peasy Toast Notifications in Vue.js with vue-snotify》,大家感兴趣的话,可以花点时间阅读这篇文章。比上面的toast插件功能更强大。

总结

文章主要介绍了如何创建Vue插件以及怎么调用创建好的插件。Vue插件是向应用程序添加全局特性的一种强大而又简单的方法。它有多种用途,从分发应用程序范围的组件到向应用程序添加路由和不可变数据存储等附加功能。Air Jordan IV 4 Retro Denim