美文网首页
toast组件的实现

toast组件的实现

作者: sweetBoy_9126 | 来源:发表于2018-11-14 00:38 被阅读1次

当前目录结构,入口是app.js然后新建一个toast.vue组件,在app.js里使用这个组件,因为我们需要在全局里直接调用它,比如我们点击按钮的时候提示一个信息:当前功能不稳定,最好的办法就是直接调用它的this.toast('当前功能不稳定')。那么我们如何能在app.js里拿到toast那?

  • 方法一:直接在toast.vue组件里写
import Vue from 'vue'
//这里为什么Vue.prototype.$toast 不用放在export default中导出也可以用,因为你每次都要引入vue,vue引入了Vue.prototype.$toast自然也就引入了
Vue.prototype.$toast = function(){
  console.log('i am toast')
}
export default {
    name: 'GuluToast'
}

然后直接在app.js里使用

new Vue({
  created(){
    this.$toast()
  }
})

运行后直接就会在页面打印出i am toast

问题:

  1. 你不知道到底能不能改写prototype.$toast
  2. 有可能用户用的不是vue而是vue2,那么你直接写import Vue from 'vue'就会出问题
  • 方法二:通过自己写插件来解决上述两个问题
    新建一个plugin.js
export default {
    install(Vue, options){
        Vue.prototype.$toast = function(){
            console.log('i am toast')
        }
    }
}

然后只需要在app.js里引入plugin.js再通过vue使用就行

import plugin from './plugin'
Vue.use(plugin)//会去执行install方法
new Vue({
  created(){
    this.$toast()
  }
})

通过插件的方法同样可以得到i am toast,而且因为是用户主动写的Vue.use(plugin)所以解决了第一个你不确定能不能改这个prototype.$toast的问题,而且我们没有import vue,那么vue是哪来的?是Vue.use(plugin)中的use把它前面的Vue传给我们的

实现点击按钮动态创建元素

  1. js写法
    plugin.js
 export default {
    install(Vue, options){
     Vue.prototype.$toast = function(message){
         let div = document.createElement('div')
         div.textContent = message
         document.body.appendChild(div)
      }
    }
  }

在app.js里写

new Vue({
  methods: {
    showToast(){
      this.$toast('hello')
    }
   }
 })

然后在index.html里绑定事件

<div id="app">
    <button @click="showToast">你好</button>
</div>
<script src="./src/app.js"></script>

toast.vue

<template>
    <div class="toast">
        <slot></slot>
    </div>
</template>
<script>

export default {
    name: 'GuluToast'
}
</script>

问题因为我们是在用vue,如果直接用通过js动态添加元素不太友好,所以我们不推荐

  1. vue组件写法
    plugin.js
import Toast from './toast.vue'
export default {
    install(Vue, options){
        Vue.prototype.$toast = function(message){
            let Constructor = Vue.extend(Toast)
            let toast = new Constructor
            toast.$slots.default = [message] //将你传入的message的值传给组件插槽
            toast.$mount() //挂载到页面上
            document.body.appendChild(toast.$el) //将dom放到body下面
        }
    }
}

设置按钮几秒后自动消失,点击按钮执行一个回调,然后用户可以传入点击关闭的文本和回调
toast.vue

<template>
    <div class="toast" @click="onClickClose">
        <slot></slot>
        <div class="line"></div>
        <span class="close" v-if="closeBtn">
            {{closeBtn.text}}
        </span>
    </div>
</template>
<script>

export default {
    name: 'GuluToast',
    props: {
        autoClose: {
            type: Boolean,
            default: true
        },
        autoCloseDelay: {
            type: Number,
            default: 5
        },
        closeBtn: {
            type: Object,
            //如果你的default的值是一个对象,那么你不能直接写这个对象,你要一个函数return一个对象
            default: ()=>{
                return {
                    text: '关闭',
                    callback: undefined
                }
            }
        }
    },
    created(){
        console.log(this.closeBtn)
    },
    mounted() {
        if(this.autoClose){
            setTimeout(()=>{
                this.close()
            },this.autoCloseDelay*1000)
        }
    },
    methods: {
        close(){
            //移除这个dom元素
            this.$el.remove()
            //完全销毁一个实例
            this.$destroy()
        },
        log(){
            console.log('aaaa')
        },
        onClickClose(){
            this.close()
            if(this.closeBtn && typeof this.closeBtn.callback === 'function'){
                this.closeBtn.callback(this) //this是toast组件实例,也就是app.js里传入的形参a
            }
        }
    }
}
</script>

app.js

import Toast from './toast'
import plugin from './plugin'
Vue.component('g-toast',Toast)
Vue.use(plugin)//会去执行install方法
new Vue({
  created(){
        this.$toast('hello',{
            closeBtn: {
                callback: (a)=>{
                    a.log()
                }
            }
        }) 

    }
})

plugin.js

import Toast from './toast.vue'
export default {
    install(Vue, options){
        Vue.prototype.$toast = function(message, dataoptions={}){
            let Constructor = Vue.extend(Toast)
            let toast = new Constructor({
                propsData: {
                    closeBtn: dataoptions.closeBtn
                }
            })
            toast.$slots.default = [message]
            toast.$mount()
            document.body.appendChild(toast.$el)
        }
    }
}

  • propsData:创建实例时传递 props

修改因为内容太多,弹出框高度撑不下问题,将高度改为最小高度

  • this.$nextTick处理异步操作
<template>
    <div class="toast" @click="onClickClose" ref="b">
        <slot v-if="!enage"></slot>
        <div v-else v-html="$slots.default[0]"></div>
        <div class="line" ref="a"></div>
        <span class="close" v-if="closeBtn">
            {{closeBtn.text}}
        </span>
    </div>
</template>
<script>
  export default {
    mounted() {
         //将b的高度赋值给a
        this.$nextTick(function(){
            this.$refs.a.style.height = this.$refs.b.offsetHeight + 'px'
        })

    }
  }
</script>

小技巧:如果你发现你眼睛观察到的不是0,而你debugger的时候确是0,那就是异步的问题


相关文章

网友评论

      本文标题:toast组件的实现

      本文链接:https://www.haomeiwen.com/subject/kserfqtx.html