从零实现Vue的Toast插件

栏目: 编程语言 · 发布时间: 5年前

内容简介:实现一个简单的Toast插件,方便迁移到不同的项目中,用来全局提示、警告一些信息。概述: 在前端项目中,有时会需要通知、提示一些信息给用户,尤其是在后台系统中,操作的正确与否,都需要给与用户一些信息。在Vue组件的

实现一个简单的Toast插件,方便迁移到不同的项目中,用来全局提示、警告一些信息。

概述: 在前端项目中,有时会需要通知、提示一些信息给用户,尤其是在后台系统中,操作的正确与否,都需要给与用户一些信息。

1. 实例

在Vue组件的 methods 内,调用如下代码

this.$toast({
    content: "可自动关闭",
    autoClose: true
})
复制代码

在页面的右侧会出现一个Toast弹框,多次点击时,会依照顺序进行显示,并且Toast可自动关闭,具体效果如图。

从零实现Vue的Toast插件
代码地址: Github UI-Library

2. 原理

  • 代码结构

    将Toast插件分为两个部分:

    data
    
  • Toast方法

    为了能够通过 this.$toast({...}) 调用Toast组件,须在Vue的 prototype 上添加一个方法,如下

    let instances = []
    let initIndex = 0
    Vue.prototype.$toast = (options = {}) => {
        /* 创建一个Toast组件的实例 */
        let instance = generateInstance(options)
        /* 将单个toast存入队列中 */    
        instances.push(instance)
    }
    复制代码

    当调用该方法时,通过 generateInstance 创建一个Toast组件的实例,并将其放入 instances ,统一管理。

    /* 构造单个toast */
    const ToastConstructor = Vue.extend(Toast)
    const verticalOffset = 16
    function generateInstance(options) {
        // 利用ToastConstructor创建一个Toast的实例
        let instance = new ToastConstructor({
            propsData: options
        }).$mount(document.createElement('div'))
        if (typeof options.onClose === 'function') instance.onClose = options.onClose
        //计算instance verticalOffset
        let id = 'toast_' + initIndex++
        instance.id = id
        // 初始化Toast在空间中的垂直偏移量
        instance.verticalOffset = initVerticalOffset(instance.position)
        //监听组件close
        instance.$once('toastClose', function () {
            const curInstance = this
            // 当Toast组件关闭时,重新计算垂直方向的偏移量
            updateVerticalOffset(curInstance)
            typeof curInstance.onClose === 'function' && curInstance.onClose()
        })
        return instance;
    }
    复制代码

    generateInstance 函数中,首先利用 ToastConstructor 构造函数创建一个Toast组件的实例,并通过 propsData 传入属性值,同时通过 $mount(document.createElement('div')) 渲染该组件。

    ToastConstructor 是通过 Vue.extend 创造Toast组件的构造函数,关于这部分的具体原理,可以参考 基于Vue构造器创建Form组件的通用解决方案

    initVerticalOffset 函数计算Toast组件的初始偏移量

    /* 初始化每个toast对象在页面中的垂直属性 */
    function initVerticalOffset(position) {
        // 筛选同一方向的Toast组件
        let typeInstances = instances.filter(item => item.position === position)
        // 计算偏移量
        return typeInstances.reduce((sum, elem) => 
                    (elem.$el.offsetHeight + sum + verticalOffset), 
               verticalOffset)
    }
    复制代码

    之后当某个Toast关闭时,需要更新所有Toast实例在页面中偏移量

    /* 更新每个toast对象在页面中的垂直属性 */
    function updateVerticalOffset(removeInstance) {
        let index = 0
        let removeHeight = removeInstance.verticalOffset
        instances.find((elem, i) => {
            if (elem.id === removeInstance.id) index = i
        })
        // 删除关闭的Toast组件
        instances.splice(index, 1)
        // 更新在删除位置之后的组件的位置
        for (; index < instances.length; ++index) {
            if (instances[index].position === removeInstance.position) {
                [instances[index].verticalOffset, removeHeight] = 
                [removeHeight, instances[index].verticalOffset]
            }
        }
    }
    复制代码

    以上完成了Toast组件的创建、如何在页面中初始化、更新的位置。

  • Toast组件

    组件的功能比较简单,只需要展示信息,以及具备自动关闭、手动关闭两个功能,属性也要包括Toast的类型、位置、内容等。

    • 组件的生命周期
    let instance = new ToastConstructor({
        propsData: options
    }).$mount(document.createElement('div'))
    复制代码

    当Toast组件 $mount 调用时,会触发 mounted 的生命周期

    mounted() {
        // 挂载Toast在页面中
        document.body.appendChild(this.$el);
        // 需要自动关闭时,调用startTimer
        if (this.autoClose) this.startTimer();
    },
    beforeDestroy() {
        this.stopTimer();
        this.$el.removeEventListener("transitionend", this.destroyElement);
    },
    destroyed() {
        // 注销
        this.$el.parentNode.removeChild(this.$el);
    }
    复制代码
    • 自动关闭 需要自动时,就要在利用 setTimeout 完成该功能,并在注销时 clearTimeout 掉,防止泄露。
    startTimer() {
          if (this.duration > 0) {
            this.timer = setTimeout(() => {
                if (!this.closed) {
                    this.close();
                }
            }, this.duration);
          }
    },
    stopTimer() {
        if (this.timer) clearTimeout(this.timer);
    }
    复制代码

3. 使用

进一步将其封装成Vue的插件

export default {
    install (Vue) {
        Vue.prototype.$toast = (options = {}) => {...}
    }
}
复制代码

并且对所需要传入的必需属性,做处理异常处理

let requireProps = Object.keys(Toast.props)
                         .filter((elem) => (Toast.props[elem].required))
requireProps.forEach((elem) => {
    if (!options[elem]) throw `err: options lack ${elem} prop`
})
if ( options.type && !types.some(elem => elem === options.type) ) 
    throw `err: toast not exist ${options.type} type`
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Domain-Driven Design

Domain-Driven Design

Eric Evans / Addison-Wesley Professional / 2003-8-30 / USD 74.99

"Eric Evans has written a fantastic book on how you can make the design of your software match your mental model of the problem domain you are addressing. "His book is very compatible with XP. It is n......一起来看看 《Domain-Driven Design》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具