仿照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 对象的解释
Nov 24 Javascript
jquery移除button的inline onclick事件(已测试及兼容浏览器)
Jan 25 Javascript
jquery实现带单选按钮的表格行选中时高亮显示
Aug 01 Javascript
ie7+背景透明文字不透明超级简单的实现方法
Jan 17 Javascript
jQuery级联操作绑定事件实例
Sep 02 Javascript
JavaScript搜索字符串并将搜索结果返回到字符串的方法
Apr 06 Javascript
很棒的js选项卡切换效果
Jul 15 Javascript
jQuery判断是否存在滚动条的简单方法
Sep 17 Javascript
vue-cli webpack模板项目搭建及打包时路径问题的解决方法
Feb 26 Javascript
JS使用setInterval实现的简单计时器功能示例
Apr 19 Javascript
微信小程序结合mock.js实现后台模拟及调试
Mar 28 Javascript
自定义javascript验证框架示例【附源码下载】
May 31 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备份/还原MySQL数据库的代码
2011/01/06 PHP
PHP实现多图上传和单图上传功能
2018/05/17 PHP
滚动条变色 隐藏滚动条与双击网页自动滚屏显示代码
2009/12/28 Javascript
javascript函数中的arguments参数
2010/08/01 Javascript
Prototype源码浅析 String部分(二)
2012/01/16 Javascript
JavaScript AOP编程实例
2015/06/16 Javascript
JavaScript中输出信息的方法(信息确认框-提示输入框-文档流输出)
2016/06/12 Javascript
微信小程序 swiper组件轮播图详解及实例
2016/11/16 Javascript
JS新包管理工具yarn和npm的对比与使用入门
2016/12/09 Javascript
js 轮播效果实例分享
2016/12/28 Javascript
用jQuery实现圆点图片轮播效果
2017/03/19 Javascript
如何使用vuejs实现更好的Form validation?
2017/04/07 Javascript
JavaScript实现一个空中避难的小游戏
2017/06/06 Javascript
jQuery实现的别踩白块小游戏完整示例
2019/01/07 jQuery
Vue项目服务器部署之子目录部署方法
2019/05/12 Javascript
如何在JavaScript中谨慎使用代码注释
2019/06/21 Javascript
解决layui 三级联动下拉框更新时回显的问题
2019/09/03 Javascript
Vue 开发必须知道的36个技巧(小结)
2019/10/09 Javascript
解决Layui 表格自适应高度的问题
2019/11/15 Javascript
js实现点击生成随机div
2020/01/16 Javascript
Python插件virtualenv搭建虚拟环境
2017/11/20 Python
在Python中pandas.DataFrame重置索引名称的实例
2018/11/06 Python
python从子线程中获得返回值的方法
2019/01/30 Python
Django使用模板后无法找到静态资源文件问题解决
2019/07/19 Python
北欧最好的童装网上商店:Babyshop
2019/09/15 全球购物
美国购买韩国护肤和美容产品网站:Althea Korea
2020/11/16 全球购物
幼儿园中班上学期评语
2014/04/18 职场文书
村庄绿化方案
2014/05/07 职场文书
优秀毕业生就业推荐信
2014/05/22 职场文书
承诺书模板
2014/08/30 职场文书
水知道答案观后感
2015/06/08 职场文书
2015年教师个人业务工作总结
2015/10/23 职场文书
亲情作文之母爱
2019/09/25 职场文书
2019年公司快递收发管理制度模板
2019/11/20 职场文书
JS实现简单控制视频播放倍速的实例代码
2021/04/18 Javascript
Nginx防盗链与服务优化配置的全过程
2022/01/18 Servers