博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从零实现Vue的组件库(一)- Toast 实现
阅读量:6223 次
发布时间:2019-06-21

本文共 4161 字,大约阅读时间需要 13 分钟。

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

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

1. 实例

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

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

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

实例地址:

代码地址:

2. 原理

  • 代码结构

    将Toast插件分为两个部分:

    • Toast组件本身,包含本身的dom结构以及data,并在其生命周期完成在页面中的挂载与销毁;
    • 在组件外构建一层代理并提供相关方法用于调用、并控制多个Toast在页面中显示的顺序。
  • Toast方法

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

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

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

    /* 构造单个toast */const ToastConstructor = Vue.extend(Toast)const verticalOffset = 16function 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组件的构造函数,关于这部分的具体原理,可以参考 。

    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    // 兼容ie 去掉find method    for (; index < instances.length; index++) {        if (instances[index].id === removeInstance.id) break;    }    instances.splice(index, 1);    // 删除关闭的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`复制代码

4. 总结

通过封装一个Toast插件,提取不同业务之间公共的部分,减少业务的工作量。

往期文章:

原创声明: 该文章为原创文章,转载请注明出处。

你可能感兴趣的文章
Delphi 发展历史
查看>>
sharepoint2010 treeview实现
查看>>
转:为什么Maven Update Project JDK变回1.5
查看>>
航电 Problem 2044一直小蜜蜂
查看>>
luoguP1357 花园
查看>>
正则表达式
查看>>
运用<body>属性,渲染页面效果
查看>>
作业:实现简单的shell sed替换功能和修改haproxy配置文件
查看>>
原来商家登录系统的commonjs
查看>>
[python机器学习及实践(3)]Sklearn实现K近邻分类
查看>>
用pillow和 opencv做透明通道的两图混全(blend)
查看>>
POJ 1002 487-3279
查看>>
netty 4.x 用户手册翻译
查看>>
Acoustic:一个异常强大的wordpress商业模板
查看>>
逆元求法(转)
查看>>
HDU 4162 Shape Number【字符串最小表示】
查看>>
项目Beta冲刺(团队1/7)
查看>>
图片垂直居中的方法(适合只有一行文字和图片)
查看>>
HTTP、HTTP1.0、HTTP1.1、HTTP2.0、HTTPS
查看>>
XML约束技术
查看>>