vue插件编写学习笔记
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>
上一篇: Vue 解决跨域问题 --- proxyTable配置
下一篇: Vue源码学习笔记