vue插件编写与实战

前言

热爱vue开发的同学肯定知道awesome-vue 这个github地址,里面包含了数以千计的vue开源插件,而这些插件大都来自第三方开发者们,是他们为vue社区提供了大量的技术支持和解决方案。本文立足vue开源的理念,主要为vue开发者讲解编写vue插件的方法和步骤,通过理论与实践相结合的方式来加深大家对vue插件编写的认识。

vue插件介绍

1. 插件与组件

在讲解插件之前,我们首先来了解下vue插件和组件的关系,在我们的vue项目中我们使用组件的频率往往会大于插件,关系如下图所示:

插件与组件

在没有封装组件之前,如果不使用第三方插件,那么很多情况下我们会编写几个常用的组件来提供给页面使用,如Alert/Loading组件,而你可能需要在很多页面中引入并且通过components注册组件,但是像这样使用率很高的组件一般我们希望全局注册后直接就可以在相应页面使用,因此我们需要将他们封装成插件,比如像vux这样的ui组件库,即提供了组件功能也提供了某些全局注册的插件。

用一句话简单概括两者的关系就是:插件可以封装组件,组件可以暴露数据给插件。

2. 插件分类

插件分类

vue插件的编写方法一般分为4类,如上图所示。主要注册与绑定机制如下:

export default {
    install(Vue, options) {
        Vue.myGlobalMethod = function () {  // 1. 添加全局方法或属性,如:  vue-custom-element
            // 逻辑...
        }

        Vue.directive('my-directive', {  // 2. 添加全局资源:指令/过滤器/过渡等,如 vue-touch
            bind (el, binding, vnode, oldVnode) {
                // 逻辑...
            }
            ...
        })
    
        Vue.mixin({
            created: function () {  // 3. 通过全局 mixin方法添加一些组件选项,如: vuex
                // 逻辑...
            }
            ...
        })    
    
        Vue.prototype.$myMethod = function (options) {  // 4. 添加实例方法,通过把它们添加到 Vue.prototype 上实现
            // 逻辑...
        }
    }
}

上方代码使用了es6部分语法列出了4种编写插件的方法,而install是注册插件主要调用的方法,包含了两个参数(Vue实例和自定义配置属性options),我们可以将以上代码存储到plugins.js中。

3. 插件使用

在plugins.js中我们仅仅编写了一个插件的空壳子,假如现在需要全局注册该插件,我们可以在入口文件,比如main.js中注册:

...

import Vue from 'vue'
import MyPlugin from './plugins/plugins.js'

Vue.use(MyPlugin);

...

通过全局方法 Vue.use() 即可使用该插件,其自动会调用install方法。Vue.use会自动阻止注册相同插件多次,届时只会注册一次该插件。

vue插件编写方法

上述我们提到了编写插件的4种方法,接下来我们对其一一进行讲解:

1. 添加全局方法或属性

export default {
    install(Vue, options) {
        Vue.$myName = '劳卜';
    }
}

在install方法中,我们直接在Vue实例上声明了$myName属性并进行了赋值,当该插件注册后只要存在Vue实例的地方你都可以获取到Vue.$myName的值,因为其直接绑定在了Vue实例上。

2. 添加全局资源

export default {
    install(Vue, options) {
        Vue.directive('focus', {
            bind: function() {},

            // 当绑定元素插入到 DOM 中。
            inserted: function(el, binding, vnode, oldVnode) {

                // 聚焦元素
                el.focus();
            },

            update: function() {},
            componentUpdated: function() {},
            unbind: function() {}
        });
    },
}

添加全局资源包含了添加全局的指令/过滤器/过渡等,上方代码我们通过Vue.directive()添加了一个全局指令v-focus,其主要包含了5种方法,其中inserted代表当绑定元素插入到 DOM 中执行,而el.focus()代表聚焦绑定的元素,这样如果我们在一个input输入框上绑定该指令就会自动进行focus聚焦。

其他directive提供的方法及用途可以参考:vue自定义指令

3. 添加全局mixin方法

export default {
    install(Vue, options) {
        Vue.mixin({
            methods: {
                greetingFn() {
                    console.log('greeting');
                }
            }
        });
    },
}

mixin代表混合的意思,我们可以全局注册一个混合,其会影响注册之后创建的每个 Vue 实例,上方代码注册后会在每个组件实例中添加greetingFn方法,在单文件组件中可以直接通过this.greetingFn()调用。当然如果实例中存在同名方法,则mixin方法中创建的会被覆盖,同时mixin对象中的钩子将在组件自身钩子之前调用。

4. 添加实例方法

export default {
    install(Vue, options) {
        Vue.prototype.$myName = '劳卜';
        Vue.prototype.showMyName = value => {
            console.log(value);
        };
    },
}

添加实例方法是最常用的一种方法,其直接绑定在vue的原型链上,我们可以回想一下 JS 里的类的概念。实例方法可以在组件内部,通过this.$myMethod来调用。

5. 插件封装组件

上方4点只讲解了插件自身的4中编写方法,并没有涉及组件的内容,如果我们要在组件的基础上编写插件,我们可以使用Vue.extend(component)来进行,可以见下方loading插件实例。

loading插件

<!-- loading.vue组件 -->
<template>
    <div class="loading-box" v-show="show">
        <div class="loading-mask"></div>
        <div class="loading-content">
            <div class="animate">
            </div>
            <div class="text">{{text}}</div>
        </div>
    </div>
</template>

<script>
export default {
    props: {
        show: Boolean,
        text: {
          type: String,
          default: '正在加载中...'
        },
    }
}
</script>

以上是一个loading.vue组件,省略了样式部分,在没有封装插件之前,我们只能通过import引入并注册到components对象中才能在页面中使用,如:

<template>
    <div>
        <loading :show="true"></loading>
    </div>
</template>
<script>
import Loading from './loading.vue'

export default {
    ...
    
    components: {
        Loading
    }
    
    ...
}
</script>

下面我们便来封装一下该组件:

// loading.js
import LoadingComponent from '../components/loading.vue'

let $vm

export default {
    install(Vue, options) {
        if (!$vm) {
            const LoadingPlugin = Vue.extend(LoadingComponent);

            $vm = new LoadingPlugin({
                el: document.createElement('div')
            });

            document.body.appendChild($vm.$el);
        }

        $vm.show = false;

        let loading = {
            show(text) {
                $vm.show = true;

                $vm.text = text;
            },
            hide() {
                $vm.show = false;
            }
        };

        if (!Vue.$loading) {
            Vue.$loading = loading;
        }

        // Vue.prototype.$loading = Vue.$loading;

        Vue.mixin({
            created() {
                this.$loading = Vue.$loading;
            }
        })
    }
}

以上我们新建一个loading.js文件,引入我们的loading.vue组件,然后通过Vue.extend()方法创建了一个构造器LoadingPlugin,其次我们再通过new LoadingPlugin()创建了$vm实例,并挂载到一个div元素上。最后我们需要通过document.body.appendChild($vm.$el)将其插入到DOM节点中。

当我们创建了$vm实例后,我们可以访问该实例的属性和方法,比如通过$vm.show就可以改变loading组件的show值来控制其显示隐藏。

最终我们通过Vue.mixin或者Vue.prototype.$loading来全局添加了$loading事件,其又包含了show和hide两个方法。我们可以直接在页面中使用this.$loading.show()来显示加载,使用this.$loading.hide()来关闭加载。

插件发布

插件编写完后我们的目的除了本地引用注册外,可能更希望发布到线上供他人或其他项目使用,因此我们需要了解插件发布的方法。

1. 发布准备

在发布插件前你需要一个npm账号,你可以访问:https://www.npmjs.com/ 进行注册

2. 发布命令

npm login
cd 目录
npm publish

拥有账号后,你需要在控制台输入npm login命令来登录你的账号,并且输入邮箱地址。然后打开你的插件目录,允许npm publish发布。最简单的一个插件目录如下:

3. 发布目录

├── lib // 插件源码
│   ├── components // 组件目录
│   │   └── loading.vue // 组件文件
│   └── index.js  // 插件入口文件
├── index.js // 入口文件
└── package.json  // 包管理文件

你可以在项目中安装刚刚的loading插件来进行参考,我已经发布至npm:

npm install super-loading --save

当然你也可以访问github仓库:https://github.com/luozhihao/super-loading 进行下载。

结语

本文的最终目的并不是教大家如何去编写一个loading插件,而是帮助大家了解在编写插件的过程中所使用的技巧和方法,只有掌握了技巧和方法才能写出各种各样的插件,正所谓水到自然渠成。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容

  • 转载 :OpenDiggawesome-github-vue 是由OpenDigg整理并维护的Vue相关开源项目库...
    果汁密码阅读 22,980评论 8 125
  • 来源:github.com Vue.js开源项目速查表:https://www.ctolib.com/cheats...
    zhangtaiwei阅读 11,506评论 1 159
  • 20170714化解计划赶不上变化:收集彻底 外事筛选 减少日程 批量处理 第一种,假性变化。原本就存在,因收集不...
    阿小杜阅读 131评论 0 0
  • 你对于读书的看法是什么? 我挺喜欢听别人讲故事,有时候听他们说我自己没有发生过的事情,就感觉这世界如此神奇,还会有...
    郁锁清秋阅读 242评论 0 0
  • 百年前的阳光妩媚 是斯人明眸垂泪 白发千年诗句唱之不尽 灵魂幻化丝竹林 色彩生灵无声愚蠢 鸣叫悲啼 落花水边戏树影...
    锦书性空山阅读 210评论 0 0