vue插件编写学习笔记

版权声明: https://blog.csdn.net/zgpeterliu/article/details/82226504

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

扫描二维码关注公众号,回复: 2976530 查看本文章

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>
 

猜你喜欢

转载自blog.csdn.net/zgpeterliu/article/details/82226504