vue组件库开发 vue插件开发

用过vue的童靴对vue组件肯定都很熟悉,一般我们在开发中会根据具体情况来封装一些可复用的组件。想loading alert等等。我们会在页面中引入,并通过components注册来实现复用。通过props传值和$emit自定义事件来完成数据传递。

vue插件

像一些常用的组件我们想通过全局注册来实现更方便的使用,比如vux elementUI等UI库,既提供了一些组件也提供了一些常用插件。插件可以封装组件,组件可以暴露数据给插件。

插件分类

  1. 添加全局方法或者属性,如: ‘vue-custom-element’

  2. 添加全局资源:指令/过滤器/过渡等,如 ‘vue-touch’

  3. 通过全局 mixin 方法添加一些组件选项,如: ‘vue-router’

  4. 添加 Vue 实例方法,通过把它们添加到 ‘Vue.prototyp’e 上实现。

  5. 一个库,提供自己的 API,同时提供上面提到的一个或多个功能,如 ’vue-router‘

插件的使用

import MyPlugin from 'xxx'
Vue.use(MyPlugin)   or
Vue.use(MyPlugin, { someOption: true })

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

插件的开发

首先插件应该有一个公开方法 install。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象

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 上实现
            // 逻辑...
        }
    }
}

:::info 社区里公认的做法是:添加在Vue prototype里的任何属性都要以美元符$作为其前缀 :::

toast简单插件开发

最简单的toast插件

// toast.js
var Toast = {};
Toast.install = function (Vue, options) {
    Vue.prototype.$toast = (tips) => {
        let toastTpl = Vue.extend({     // 1、创建构造器,定义好提示信息的模板
            template: '<div class="vue-toast">' + tips + '</div>'
        });
        let tpl = new toastTpl().$mount().$el;  // 2、创建实例,挂载到文档以后的地方
        document.body.appendChild(tpl);     // 3、把创建的实例添加到body中
        setTimeout(function () {        // 4、延迟2.5秒后移除该提示
            document.body.removeChild(tpl);
        }, 2500)
    }
}
module.exports = Toast;

//useage
this.$toast('haha')

增加添加弹出位置

var Toast = {};
Toast.install = function (Vue, options) {
    let opt = {
        defaultType:'bottom',   // 默认显示位置
        duration:'2500'         // 持续时间
    }
    for(let property in options){
        opt[property] = options[property];  // 使用 options 的配置
    }
    Vue.prototype.$toast = (tips,type) => {
        if(type){
            opt.defaultType = type;         // 如果有传type,位置则设为该type
        }
        if(document.getElementsByClassName('vue-toast').length){
            // 如果toast还在,则不再执行
            return;
        }
        let toastTpl = Vue.extend({
            template: '<div class="vue-toast toast-'+opt.defaultType+'">' + tips + '</div>'
        });
        let tpl = new toastTpl().$mount().$el;
        document.body.appendChild(tpl);
        setTimeout(function () {
            document.body.removeChild(tpl);
        }, opt.duration)
    }
    ['bottom', 'center', 'top'].forEach(type => {
        Vue.prototype.$toast[type] = (tips) => {
            return Vue.prototype.$toast(tips,type)
        }
    })
}
module.exports = Toast;


// useage
this.$toast.top('top')

alert插件封装组件实例

//alert.vue组件
<template>
    <transition name='fade'>
        <div class="alert-box" v-show='show'>
            <div class='alert-title'>
                {{title}}
            </div>            
            <div class='alert-subtitle' v-if='subtitle'>
                {{subtitle}}
            </div>
            <div class='alert-body' v-if='body'>
                {{body}}
            </div>
            <slot></slot>
        <div class="close icon" @click='close'></div>
        </div>
    </transition>
</template>
<script>
    
    export default {
        name: 'x-alert',
        components: {},
        props:{
            // 父组件v-model='xxx'
            value: {
                type: Boolean,
                default: false
            },
            title: {
                type: String,
                default:''
            },
            subtitle: {
                type: String,
                default:''
            },
            body: {
                type: String,
                default:''
            }
        },
        data() {
            return {
                show: this.value
            }
        },
        mounted() {

        },
        methods: {
            close() {
                this.show = false 
                this.$emit('close')
            }
        },
        watch: {
            value(val) {
                this.show = val;
            },
            show(val) {
            <!--v-model -->
                this.$emit('input', val);
            }
        },
        computed: {

        }
    }
</script>
//alert.js插件
import Alert from './alert.vue';

const plugin = {
	install(vue, props = {}) {
		const AlertPlugin = Vue.extend(Alert);

		let $vm = new AlertPlugin({
			el: document.createElement('div')
		});
		document.body.appendChild($vm.$el);

		function alert(options = {}) {
			if ($vm.show) return;
			// 如果传参为字符串,则直接显示body 的文本
			if (typeof options === 'string') {
				options = {
					body: arguments[0]
				};
			}
			//配置优选级:实例配置 > 全部配置 > 默认配置
			$vm = Object.assign($vm, props, options,{delayed: true});

			$vm.show = true;
			// 支持延时消失 默认支持
			if ($vm.delayed) {
				setTimeout(_=>{
					$vm.show = false
				},3000)
			}
			return new Promise((resolve, reject) => {
				$vm.$watch('show', val => {
					resolve();
				});
			});
		}

		if (!vue.$x) {
			vue.$x = {
				alert
			};
		} else {
			vue.$x.alert = alert;
		}
		vue.mixin({
			created: function() {
				this.$x = vue.$x;
			}
		});
	}
};
export default plugin;

alert插件用法useage

import AlertPlugin from './alert.js'
Vue.use(AlertPlugin)
this.#x.alert('呵呵') // 字符串的话则只默认显示body部分
this.$x.alert({
    title: '你好',
    subtitle: '我是副标题',
    body:'呵呵',
    delayed: false, //取消延时消失
})
this.#x.alert('hehe').then(_=> console.log('显隐时触发回调'))

自动安装插件

对于那些没有在应用中使用模块化系统的用户,他们往往将通过script标签引用你的插件,并期待插件无需调用Vue.use()便会自动安装 。你可以在插件最后添加如下几行代码来实现自动安装:


// 如果Vue是全局对象自动安装插件
if (typeof window !== 'undefined' && window.Vue) {
  window.Vue.use(MyPlugin)
}

input高阶组件

<template>
    <div class="form-item" clearfix>
        <i v-if="prefix" class="prefix">{{ prefix }}</i>
        <input
            type="text"
            class="fr"
            :value="value"
            v-bind="$attrs"
            v-on="listeners"
            v-if="type == 'input'"
        />
        <textarea
            :placeholder="suffix"
            class="fr"
            :value="value"
            v-bind="$attrs"
            v-on="listeners"
            v-else-if="type == 'textarea'"
        ></textarea>
    </div>
</template>
<script>
export default {
    name: "",
    components: {},
    data() {
        return {};
    },
    props: {
        prefix: {
            type: String,
            default: ""
        },
        suffix: {
            type: String,
            default: ""
        },
        type: {
            type: String,
            default: "input"
        },
        value: {
            type: [String, Number],
            default: ""
        }
    },
    mounted() {},
    methods: {
        // inputSomeThing(e) {
        //     this.$emit("input", e.target.value);
        // }
    },
    watch: {},
    computed: {
        listeners() {
            return {
                ...this.$listeners,
                input: e => this.$emit("input", e.target.value)
            };
        }
    }
};
</script>
---------
useage:
<c-input
    v-model="phoneNum"
    prefix="手机号"
    maxlength="100"
    placeholder='请输入手机号'
></c-input>

npm 发布插件

不多说

npm login
cd 目录
npm publish

说下简单的目录结构

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

可以向Awesome-Vue提交PR。很多人会来这里寻找插件。or Vue论坛,Vue Gitter Channel,或者在Twitter上@ #vuejs。

Last Updated: 8/5/2019, 6:49:21 PM