仿照Element-ui实现一个简易的$message方法


Posted in Javascript onSeptember 14, 2020

前言

在需要对用户进行提示时,有时会遇到这种场景:使用模态框提示太过硬核,使用toast提示又太轻了,这时候可以选择使用页面顶部滑下的消息提示。本文仿照element-ui的实现一个类似$message的方法。

思路梳理

首先我们来看下element-ui中消息提示的效果是怎么样的,找些思路。

仿照Element-ui实现一个简易的$message方法

从图中我们可以看消息提示是可以同时显示多条的,并且定位看起来都是fixed,居中展示,我们自然可以想到使用数组来存储这些消息的信息,并且根据每一条提示消息的显示隐藏更改每一项的top值,然后就是加一些动画(使用transition)以及细节处理了。

组件编写

新建两个组件MsgBox.vue和Msg.vue,前者负责收集和处理传入的消息数据(如:{type: 'success', message: '提示消息'}),对数组进行一定处理后,再将每一项传给Msg.vue展示。

MsgBox组件

ts部分

我们首先在MsgBox.vue中编写方法处理数组的方法addMsg、resetTop和clear,其中addMsg负责收集消息数据,给每一个msg添加一个负责控制该条消息显示隐藏的属性show;resetTop负责控制消息距顶距离的属性top及各条消息的显示隐藏;clear负责当数组中所有消息都处于隐藏状态时将消息数组清空:

private addMsg(msg: Msg) {
 this.msgs.push({...msg, show: true})
 this.resetTop()
}

private resetTop(ind1 = -1) {
 this.clear()
 let ind = 0
 const msgs = this.msgs.map((msg: MsgInfo, i: number) => {
  if (i === ind1) {
   msg.show = false
  }
  if (msg.show) {
   msg.top = 20 + ind * 72
   ind++
  }
  return msg
 })
 this.msgs = [...msgs]
}

private clear() {
 clearTimeout(this.timer)
 this.timer = setTimeout(() => {
  const allFalse = this.msgs.some((t) => t.show)
  if (!allFalse) {
   this.msgs = []
  }
 }, 1000)
}

每次有新消息加入,或者原有消息隐藏时都会触发resetTop方法,用来重新计算各条消息的位置。

template部分

html部分就比较简单了,只是遍历msgs数组,将每一项传给子组件Msg。

<template>
<div>
  <msg-box v-for="(msg,i) of msgs" :msg="msg" :key="i" :ind="i" @resetTop="resetTop" :msgs="msgs"></msg-box>
</div>
</template>

这里传入数组msgs的原因是在每次调用resetTop更改数组时,子组件监听不到msg发生的变化,只好将msgs传入,直接从msgs中取相关数据,如果哪位大佬看出问题了希望可以指点下。

Msg组件

ts部分

子组件中逻辑较少,主要是在组件挂载时启动一个定时器,在一定时间后通过emit触发父组件中的resetTop方法将组件关闭。 另外还有一些根据参数获取当前消息信息的computed方法。

private get info() {
 const msgs = this.msgs as MsgInfo[]
 return msgs[this.ind]
}

private get boxClass() {
 const type = this.msg.type
 return type ? `box-item-${type}` : ''
}

@Emit('resetTop')
private close() {
  return this.ind
}

private mounted() {
 if (this.msg.delay !== 0) {
 const delay = parseInt(this.msg.delay) || 3000
 setTimeout(() => {
  this.close()
 }, delay)
 }
}

template部分

视图部分也比较简单,主要是使用了vue自带的transition组件实现的动画效果,注意要加上appear属性才有入场动画效果。

<template>
 <transition name="msg" appear>
  <div :class="['box-item', boxClass]" v-if="info.show" :style="{top: info.top + 'px'}">
   <div class="msg-container">
   <i :class="['iconfont', iconClass]"></i>
    {{ info.msg }}
   </div>
   <span @click="close">
    <i class="iconfont icon-cc-close"></i>
   </span>
  </div>
 </transition>
</template>

css部分

样式部分主要是借鉴了element-ui的样式,以及使用了animation做了简单的动画效果

.box-item {
 height: 16px;
 position: fixed;
 min-width: 380px;
 // element-ui抄来的样式
 border-width: 1px;
 border-style: solid;
 border-color: #EBEEF5;
 position: fixed;
 left: 50%;
 transform: translateX(-50%);
 background-color: #edf2fc;
 transition: opacity .3s,transform .4s,top .4s;
 padding: 15px 15px 15px 20px;
 display: flex;
 align-items: center;
 justify-content: space-between;
 &-success{
  background-color: #f0f9eb;
  border-color: #e1f3d8;
 }
 &-warning {
  background-color: #fdf6ec;
  border-color: #faecd8;
 }
 &-error {
  background-color: #fef0f0;
  border-color: #fde2e2;
 }
}

.msg-container {
  display: flex;
  align-items: center;
  .iconfont {
   margin-right: 5px;
  }
}

.msg-enter-active {
 animation: anim 0.5s;
}

.msg-leave-active {
 animation: anim 0.5s reverse;
}

@keyframes anim {
 0% {
  opacity: 0;
  transform: translate(-50%, -200%);
 }

 100% {
  opacity: 1;
  transform: translate(-50%, 0);
 }
}

到此为止,除css代码外不到150行实现了消息提示组件。

将组件中方法放到Vue原型链上

但是我们该怎么调用呢,参考element-ui中的使用方式this.$message,是把组件的入口方法挂载到Vue的原型链上,并且在此之前应该实例化了该组件,接下来我们就要实例化组件,然后将组件实例挂载到body上,并且将实例上的入口方法加在Vue原型链上。

这里使用到了我们公司一位大佬参考react写的vue中的传送门方法portal,主要思路是将组件挂载新的Vue上,并实例化,然后再将新实例挂载到body下面(这样也防止DOM层级嵌套产生的zIndex无法覆盖等问题),最后将指定方法添加到Vue原型链上。

import Vue, {VueConstructor} from "vue";

interface Param {
 cmp: VueConstructor & {instance?: () => any};
 name: string;
 method?: string;
 target?: string | HTMLElement;
 props?: any;
}

export default function portal(param: Param){
  let {cmp, name, method, target = document.body, props = {}} = param
  if(typeof target === 'string') target = document.querySelector(target) as HTMLElement;
  method = method || 'show'
  cmp.instance = ()=>{
    let instance = new Vue({
      render(h){
        return h(cmp, {props})
      }
    })

    instance.$mount();

    // 将instance.$el放到想放的位置
    (target as HTMLElement).appendChild(instance.$el);
    return instance.$children[0];
  }
  const instance = cmp.instance()
  
  Vue.prototype[`$${name}`] = instance[method];
}

接着,在Vue项目入口文件中使用传送门方法将Msg组件挂载上去就可以在组件中使用了。

portal({
 name: 'msg',
 cmp: MsgBox,
 method: 'addMsg'
})

到此这篇关于仿照Element-ui实现一个简易的$message方法的文章就介绍到这了,更多相关Element-ui $message内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
javascript之典型高阶函数应用介绍
Jan 10 Javascript
AngularJS入门教程(一):静态模板
Dec 06 Javascript
Javascript实现飞动广告效果的方法
May 25 Javascript
javascript删除元素节点removeChild()用法实例
May 26 Javascript
JS如何判断是否为ie浏览器的方法(包括IE10、IE11在内)
Dec 13 Javascript
Angular 根据 service 的状态更新 directive
Apr 03 Javascript
解决jQuery ajax请求在IE6中莫名中断的问题
Jun 20 Javascript
Angular中使用MathJax遇到的一些问题
Dec 15 Javascript
JS+HTML实现的圆形可点击区域示例【3种方法】
Aug 01 Javascript
微信小程序登录按钮遮罩浮层效果的实现方法
Dec 16 Javascript
javascript+HTML5 canvas绘制时钟功能示例
May 15 Javascript
JS严格模式原理与用法实例分析
Apr 27 Javascript
vue swipeCell滑动单元格(仿微信)的实现示例
Sep 14 #Javascript
JavaScript 如何计算文本的行数的实现
Sep 14 #Javascript
JavaScript实现串行请求的示例代码
Sep 14 #Javascript
浅谈JavaScript 声明提升
Sep 14 #Javascript
详解vue 中 scoped 样式作用域的规则
Sep 14 #Javascript
详解JavaScript 高阶函数
Sep 14 #Javascript
vue实现简单计算商品价格
Sep 14 #Javascript
You might like
用PHP函数解决SQL injection
2006/10/09 PHP
php获取开始与结束日期之间所有日期的方法
2016/11/29 PHP
php常用数组array函数实例总结【赋值,拆分,合并,计算,添加,删除,查询,判断,排序】
2016/12/07 PHP
jquery $(document).ready() 与window.onload的区别
2009/12/28 Javascript
ECMAScript 6即将带给我们新的数组操作方法前瞻
2015/01/06 Javascript
仿JQuery输写高效JSLite代码的一些技巧
2015/01/13 Javascript
js仿土豆网带缩略图的焦点图片切换效果实现方法
2015/02/23 Javascript
jquery弹出遮掩层效果【附实例代码】
2016/04/28 Javascript
js简单正则验证汉字英文及下划线的方法
2016/11/28 Javascript
Bootstrap3 图片(响应式图片&amp;图片形状)
2017/01/04 Javascript
Angular.js中ng-if、ng-show和ng-hide的区别介绍
2017/01/20 Javascript
Vuex 进阶之模块化组织详解
2018/01/12 Javascript
详解基于mpvue的小程序markdown适配解决方案
2018/05/08 Javascript
纯JS实现出生日期[年月日]下拉菜单效果
2018/06/01 Javascript
微信小程序实现加入购物车滑动轨迹
2020/11/18 Javascript
vue + el-form 实现的多层循环表单验证
2020/11/25 Vue.js
深入讲解Python中的迭代器和生成器
2015/10/26 Python
利用Python实现Windows下的鼠标键盘模拟的实例代码
2017/07/13 Python
浅谈Python3识别判断图片主要颜色并和颜色库进行对比的方法
2019/10/25 Python
Python读取Excel一列并计算所有对象出现次数的方法
2020/09/04 Python
Python操作word文档插入图片和表格的实例演示
2020/10/25 Python
html5 canvas-2.用canvas制作一个猜字母的小游戏
2013/01/07 HTML / CSS
html5教程调用绘图api画简单的圆形代码分享
2013/12/04 HTML / CSS
移动端Html5页面生成图片解决方案
2018/08/07 HTML / CSS
试解释COMMIT操作和ROLLBACK操作的语义
2014/07/25 面试题
计算机个人求职信范例
2014/01/24 职场文书
共产党员公开承诺书
2014/03/25 职场文书
甜品店创业计划书
2014/08/14 职场文书
企业群众路线教育实践活动心得体会
2014/11/03 职场文书
2014年乡镇团委工作总结
2014/12/18 职场文书
员工拾金不昧表扬稿
2015/05/05 职场文书
学生会任命书范本
2015/09/21 职场文书
合理缓解职场压力,让你随时保持最佳状态!
2019/06/21 职场文书
asyncio异步编程之Task对象详解
2022/03/13 Python
MySQL优化及索引解析
2022/03/17 MySQL
在windows server 2012 r2中安装mysql的详细步骤
2022/07/23 Servers