面对面用Vue造一个Toast轮子(包含测试用例)

先上效果图...

toast.gif

面对面用Vue造一个Toast轮子(包含测试用例)_第1张图片
toast.png

Toast需求:

  • 弹出Toast自动关闭
  • 多少(N)秒后自动关闭
  • 弹出Toast用户可点击关闭
  • 用户点击关闭后回调(fn)
  • 保证只有一个Toast弹出,不会同时出现两个。

用法:

import Vue from 'vue'
import plugin from './plugin/index'
Vue.use(plugin)

this.$toast('操作成功!!!')

Github:欢迎Star


这里是分割线


开始面对面,看代码...

不管在哪里都可以直接使用this.$toast,具体如何实现??? 请看Vue官方文档

1、main.js

Vue.prototype.$toast = function () {
  console.log('我是苏宋霖');
}

2、App.vue


  export default {
    name: 'app',
    methods: {
      xxx() {
        this.$toast()
      },
    }
}

点击按钮控制台是不是打印出了我是苏宋霖,做到这里你已经成功一半了

clickToast.gif

但是这样并不是我们想要的,使用插件形式install,让用户主动去使用。

继续改造✨

新建plugin/index.js

export default {
  install(Vue,options){
    Vue.prototype.$toast = function(message){
      console.log(message);
    }
  }
}

main.js

import plugin from './plugin/index'
Vue.use(plugin)

使用

this.$toast('我是苏宋霖')

控制一样打印出我是苏宋霖, 说明你距离成功不远了 (这句话跟高中老师一样,经常对我们学生说前脚已经跨进北大的校门了,后来我们毕业了才知道北大是可以随便进出,哪怕登记一下就好)

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

继续改造✨✨

新建components/Toast.vue



在引入Toast.vue之前有个问题如何在js使用vue实例 ??

1、方应杭的Vue 动态创建实例
2、滴滴的cube-ui专门为这个场景实现了一个create-api, 可以将任意自定义组件制作成调用时动态创建的插件

plugin/index.js 引入Toast.vue

import Toast from '@/components/Toast.vue'
export default {
  install(Vue,options){
    Vue.prototype.$toast = function(message){
      let Constructor = Vue.extend(Toast)
      let toast = new Constructor()
      toast.$mount()
      document.body.appendChild(toast.$el)  //添加到页面中
    }
  }
}

通过代码可以实现动态创建实例(别用原生js实现如: let div = document.createElement("div"); document.body.appendChild(div),这样无法使用到vue的各种生命周期及Api)

appendChildToast.gif
继续改造✨✨✨

Toast.vue 文件不变,index.js通过插槽传值给Toast.vue
问题:如果在js中使用插槽传值啊??? 我的天
看文档渲染函数 & JSX
this.$slots.default // 子节点数组 注意这里接收的是数组 数组 数组

import Toast from '@/components/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] //slots必须要放在mount之前
      toast.$mount()
      document.body.appendChild(toast.$el)
    }
  }
}
slots.gif

添加简单样式


继续改造✨✨✨✨

实现3s后自动关闭,

  props: {
    // 自动关闭
    autoClose: {
      type: Boolean,
      default: true
    },
    // 关闭时间
    autoCloseDelay: {
      type: Number,
      default: 3
    }
  },
  mounted() {
    if (this.autoClose) {
      setTimeout(() => {
        this.close();
      }, this.autoCloseDelay * 1000);
    }
  },
  methods: {
    close() {
      this.$el.remove(); //删除
      this.$destroy(); //清除绑定的一些事件
    }
  }
autoClose.gif
继续改造✨✨✨✨✨

弹出Toast用户可点击关闭



plugin/index.js

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

使用

    this.$toast('我是苏宋霖',{
            closeButton:{
            text:'知道了',
            callback(){
              console.log('苏宋霖点击知道了');
            }
          }
        })
closeButton.gif

手动测试发现一个问题,内容过多的时候Toast并没有变化,
关闭按钮被挤压
如下:


面对面用Vue造一个Toast轮子(包含测试用例)_第2张图片
溢出.png

改造吧支持多行文字
使用js去给line添加高度,因为父元素的高度变成min-height。

//html 
 
{{closeButton.text}}
//js
mounted(){
    this.updateStyle()
},
  methods: {
     updateStyle() {
        this.$nextTick(() => {
          this.$refs.line.style.height =
            `${this.$refs.toast.getBoundingClientRect().height}px`
        })
      },
}
//css 新增的

面对面用Vue造一个Toast轮子(包含测试用例)_第3张图片
改造后....png

继续改造✨✨✨✨✨✨

Toast的位置
在.toast上加:class="toastClasses",通过props传过来的属性position 动态添加class

//html
 
{{closeButton.text}}
//js
props:{
    // 位置
    position: {
      type: String,
      default: "top",
      validator(value) {
        return ["top", "bottom", "middle"].indexOf(value) >= 0;
      }
    }
}

  computed: {
    toastClasses() {
      return {
        [`position-${this.position}`]: true
      };
    }
  },
//css
  &.position-top {
    top: 0;
    transform: translateX(-50%);
  }

  &.position-bottom {
    bottom: 0;
    transform: translateX(-50%);
  }
  &.position-middle {
    top: 50%;
    transform: translate(-50%, -50%);
  }

使用





position.gif

新问题:
用户重复点击Toast,会出现多个DOM??


重复DOM.gif

解决方案:
如果已经有一个Toast,就把之前的给删了
plugin/index.js

import Toast from '@/components/Toast.vue'

export default {
  install(Vue,options){
    let currentToast ;
    Vue.prototype.$toast = function(message,toaseOptions){
      if(currentToast){currentToast.close()}//如果有Toast就删除上一个
      currentToast =  createToast({Vue,propsData:toaseOptions,message})
    }
  }
}

function createToast({Vue,propsData,message}) {
  let Constructor = Vue.extend(Toast)
  let toast = new Constructor({ propsData})
  toast.$slots.default = [message]
  toast.$mount()
  document.body.appendChild(toast.$el)
  return toast
}
DOM正常.gif

ps:样式啥的请自行修改。


轮子完毕!!! 接下来测试用例..

未完待续...

提示:

npm 配置淘宝源:npm config set registry https://registry.npm.taobao.org/

你可能感兴趣的:(面对面用Vue造一个Toast轮子(包含测试用例))