欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

vue插件编写学习笔记

程序员文章站 2022-03-28 11:17:45
...

https://segmentfault.com/a/1190000008869576

 

vue插件化------------------------Vue.extend(xxx.vue)----------------------模板构造器
                                 new constructor().$mount().$el-----------生成页面实例
                                 document.body.appendChild(页面实例)-------挂载到document上
                                 Object.assign(vm, defaultOptions, options)------拷贝默认数据和传递数据/方法/回调函数到页面实例对象中

 

 

 

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

 

MyPlugin.install = function (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 上实现
    // 逻辑...
  }
}

 

接下来要讲到的 vue-toast 插件则是通过添加实例方法实现的。我们先来看个小例子。先新建个js文件来编写插件:toast.js

var Toast = {};
Toast.install = function (Vue, options) {
    Vue.prototype.$msg = 'Hello World';
}
module.exports = Toast;

 

在 main.js 中,需要导入 toast.js 并且通过全局方法 Vue.use() 来使用插件:

// main.js
import Vue from 'vue';
import Toast from './toast.js';
Vue.use(Toast);

 

然后,我们在组件中来获取该插件定义的 $msg 属性。

// App.vue
export default {
    mounted(){
        console.log(this.$msg);         // Hello World
    }
}

 

通过插件的再次封装,可以一次性引入多个插件:

index.js中:
import plugins from './components/plugins'
Vue.use(plugins)

plugins.js中:
import Toast from './toast'
import Loading from './loading'
import Dialog from './dialog'
import Alert from './alert'
import Ads from '../../components/common/ads'

export default {
    install (Vue) {
        Vue.use(Toast)
        Vue.use(Loading)
        Vue.use(Alert)
        Vue.use(Dialog)
        Vue.use(Ads)
    }
}

 

// 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;

这时候注意到 Toast.install(Vue,options) 里的 options 参数,我们可以在 Vue.use() 通过 options 传进我们想要的参数。最后修改插件如下:

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;

 

例子:

dialog.js文件:

import dialog from './dialog.vue'
import { extend } from '../utils'

let vm
const defaultOptions = {
}

const Dialog = {
    install (Vue, options = {}) {
        const DialogConstructor = Vue.extend(dialog)
        vm = new DialogConstructor().$mount(document.createElement('div'))
        extend(vm, options)
        Object.keys(vm.$options.props).forEach(key => {
            defaultOptions[key] = options[key] || vm.$options.props[key].default
        })
        document.body.appendChild(vm.$el)

        const show = (opt = {}) => {
            Object.assign(vm, defaultOptions, opt)
            vm.show = true
            vm.$on('OnFirstEvent', () => {
                vm.show = false
                vm.firstCallback()
            })
            vm.$on('OnSecondEvent', () => {
                vm.show = false
                vm.secondCallback()
            })
        }

        Vue.prototype.$dialog = show
    }
}

export default Dialog

 

utils.js文件:

export const extend = (target, source) => {
    for (const key in source) {
        target[key] = source[key]
    }
}

export const delay = time => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve()
        }, time)
    })
}

 

dialog.vue文件:
<template>
    <transition name="fade">
        <div class="dialog-wrap fixed-100 fixed-gray" v-show="show">
            <div class="dialog-container">
                <div class="dialog-content">
                    <div class="dialog-content-title" v-html="title"></div>
                    <div class="dialog-content-text" v-html="text"></div>
                </div>
                <div class="dialog-btn">
                    <div class="btn second" v-text="secondBtnText" @click="onSecond()"></div>
                    <div class="btn first" v-text="firstBtnText" @click="onFirst()"></div>
                </div>
            </div>
        </div>
    </transition>
</template>

<script>
    export default {
        name: 'dialog-component',
        props: {
            title: {
                type: String,
                default: '标题'
            },
            text: {
                type: String,
                default: '提示内容'
            },
            firstBtnText: {
                type: String,
                default: '优先动作'
            },
            secondBtnText: {
                type: String,
                default: '次级动作'
            },
            firstCallback: {
                type: Function,
                default: () => {}
            },
            secondCallback: {
                type: Function,
                default: () => {}
            }
        },
        data () {
            return {
                show: false
            }
        },
        methods: {
            onFirst () {
                this.$emit('OnFirstEvent')
            },
            onSecond () {
                this.$emit('OnSecondEvent')
            }
        }
    }
</script>

<style lang="stylus" scoped>
.fade-enter-active, .fade-leave-active
    transition: opacity 1.2s
.fade-enter, .fade-leave-to
    opacity: 0
.fixed-gray
    background: rgba(128, 128, 128, .8)
.dialog-wrap
    z-index 100
    .dialog-container 
        position absolute
        top 50%
        left .3rem
        right .3rem
        background #fff
        border-radius .1rem
        transform scale(1, 1) translate(0, -50%)
        .dialog-content 
            padding .48rem
            text-align center
            border-bottom 1px solid rgb(241, 241, 241)
            .dialog-content-title
                font-size .35rem
                font-weight 700
            .dialog-content-text
                margin-top .16rem
                font-size .26rem
                color rgb(96, 96, 96)
        .dialog-btn
            .btn
                height 1rem
                line-height 1rem
                font-size .35rem   
                font-weight 700
                text-align center
                display inline-block
                width 49%
                color rgb(51,51,51)
            .first
                color rgb(220, 30, 50)
                border-left 1px solid rgb(241, 241, 241)
</style>

 

使用到该插件的文件:

<template>
    <div class="dialog-demo">
        <div class="dialog-item" @click="click1">无内容 dialog</div>
        <div class="dialog-item" @click="click2">详细内容 dialog</div>
    </div>
</template>

<script>
    export default {
        methods: {
            click1 () {
                this.$dialog({
                    title: '无内容的对话框',
                    firstBtnText: '确认哦',
                    secondBtnText: '取消哦',
                    firstCallback: () => {
                        this.$toast('确认了')
                    },
                    secondCallback: () => {
                        this.$toast('取消了')
                    }
                })
            },
            click2 () {
                this.$dialog({
                    title: '详细内容的对话框',
                    text: '为什么这样子,你拉着我的手说你有些犹豫,我拉不住你,他的手应该比我更暖',
                    firstBtnText: '确认哦',
                    secondBtnText: '取消哦',
                    firstCallback: () => {
                        this.$toast('确认了')
                    },
                    secondCallback: () => {
                        this.$toast('取消了')
                    }
                })
            }
        }
    }
</script>

<style lang="stylus" scoped>
    .dialog-demo
        width 100%
        height 100%
        padding .2rem
        .dialog-item
            width 100%
            margin-top .2rem
            background rgba(220,30,50,.9)
            color white
            height 1rem
            line-height 1rem
            text-align center
            border-radius .1rem
</style>