精通React/Vue系列之实现一个全局提示(Message)组件

前言

本文是笔者写组件设计的第十一篇文章, 今天带你们实现一个一样比较特殊的组件——全局提示(Message)组件。咱们看到的组件效果多是这样的:javascript

因为全局提示组件的设计原理和笔者上一篇写的精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification)是相似的,区别主要是布局和配置参数,因此说细节和实现原理部分就不在这篇文章介绍了,本文主要介绍设计思路和设计的方法。css

基础组件库主要按如下分类来划分前端

  • 通用型组件: 好比Button, Icon等.vue

  • 布局型组件: 好比Grid, Layout布局等.java

  • 导航型组件: 好比面包屑Breadcrumb, 下拉菜单Dropdown, 菜单Menu等.node

  • 数据录入型组件: 好比form表单, Switch开关, Upload文件上传等.react

  • 数据展现型组件: 好比Avator头像, Table表格, List列表等.webpack

  • 反馈型组件: 好比Progress进度条, Drawer抽屉, Modal对话框等.css3

  • 其余业务类型程序员

熟悉以上分类法是设计任何组件系统的前提,无论你是从零到一开发前端团队的UI库,仍是基于已有组件库二次开发业务组件,以上分类法则一样适用。

本文将会使用React来开发该组件,也会使用到Javascript中经常使用的一些设计模式,好比单例模式,可是无论你使用什么框架来实现,原理都是通用的,若是感兴趣的朋友能够用vue也实现一下。若是对设计模式不是很了解,能够移步:

15分钟带你了解前端工程师必知的javascript设计模式(附详细思惟导图和源码).

正文

在开始组件设计以前但愿你们对css3和js有必定的基础,并了解基本的react/vue语法.咱们先来解构一下Message组件, 一个Message分为如下几个部分:

每个区块均可以自定义配置, 也能够组合其余组件.咱们还能够配置全局提示出如今顶部的偏移量,相似于antd的组件同样。而且咱们都知道,antd或者element这种组件库,会自带一些主题状态,来提升用户的使用效率,好比会有success(成功状态),warning(警告状态),error(错误状态),info(通知状态)等,那么咱们本身实现的全局提示(Message)组件也因该具有这些功能。

如下是笔者使用React实现后的Message组件效果:

接下来咱们来看看通知提醒框(Message)的具体设计思路。

1. Message组件设计思路

按照以前笔者总结的组件设计原则,咱们第一步是要确认需求. 通知提醒框(Message)组件通常会有以下需求点:

  • 能控制Message自动关闭的时间

  • 能配置Message渲染节点的输出位置

  • 能自定义关闭图标

  • 能够手动选择全局提示类型

  • 能自定义全局提示的偏移量

  • 能设置全局提示的信息文本

  • 能自定义全局提示的Icon

  • 全局提示点击时提供回调函数

  • 全局提示关闭时提供回调函数

  • 能手动销毁通知框

需求收集好以后,做为一个有追求的程序员, 会得出以下线框图:

具体的设计细节能够参考个人上一篇Notification组件设计的文章。

2. 基于react实现一个全局提示(Message)组件

组件的核心部分咱们仍是采用React Notification的模式。

2.1 搭建通知提醒框(Notification)的基本骨架

首先按照笔者的代码风格,通常会考虑组件设计的框架,而后再一步步往里面填充内容和逻辑。经过这种渐进式的设计思路,能让咱们逻辑更严谨,更清晰。具体代码以下:

import Notification from 'rc-notification'import './index.less'
const xMessage = (function() {  let message = null  /**     * notice类型弹窗     * @param {config}  object 提示框配置属性     *   @param {type} string 提示窗类型     *   @param {btn}  ReactNode 自定义关闭按钮     *   @param {className}  string 自定义 CSS class     *   @param {duration}  number 默认 4.5 秒后自动关闭,配置为 null 则不自动关闭     *   @param {getContainer}  HTMLNode 配置渲染节点的输出位置     *   @param {icon}  ReactNode 自定义图标     *   @param {key}  string 当前提示惟一标志     *   @param {content}  string|ReactNode 提示标题,必选     *   @param {onClose}  func 点击默认关闭按钮时触发的回调函数     *   @param {onClick}  func 点击提示时触发的回调函数     *   @param {top}  number 消息从顶部弹出时,距离顶部的位置,单位像素     *   @param {closeIcon}  ReactNode 自定义关闭图标     */  const pop = (config) => {    const {      type, className,      duration = 4.5,      getContainer = () => document.body,      icon, key, content, onClose, onClick,      top, closable = true, closeIcon    } = config    message.notice({      content: <div className={classnames('xMessage', className )}>        <div className={classnames('iconWrap', type)}>            <Icon type={iconType[type]} />          </div>        <div className="xNoticeTit">          { content }        </div>      </div>,      key, closable, getContainer,      onClose() {        onClose && onClose()      },      onClick() {        onClick && onClick()      },      closeIcon, duration, style: { top }    })  }
  /**     * 提示提示组件, 全局参数     * @param {duration} number 默认自动关闭延时,单位秒     * @param {getContainer} HTMLNode 配置渲染节点的输出位置,默认document.body     * @param {closeIcon} HTMLNode 自定义关闭图标  */  const config = (config) => {}  const remove = (key) => {}  const destroy = () => {}
  if(message) {    return {      config, pop, remove, destroy    }  }  // 若是为建立实例,则建立默认实例  Notification.newInstance({}, (notice) => message = notice)
  return {    config, pop, remove, destroy  }})()
export default xMessage

首先咱们根据需求把属性罗列出来, 经过分析咱们因该对外提供四个接口供开发者使用,分别为:

  • config —— Message全局配置,用来控制全局的偏移量,样式,渲染容器等;

  • pop —— 用来建立全局提示实例的方法,同时能够控制实例的属性

  • remove —— 用来删除指定实例

  • destroy —— 用来销毁全局的Message

首先咱们来实现一下config:

const config = (config) => {  const { duration, getContainer, closeIcon } = config
  Notification.newInstance({    getContainer: getContainer,    duration: duration || 4.5,    closeIcon  }, (notice) => message = notice)}

固然咱们还能够根据本身的需求去自定义扩展。

pop方法的实现:

const pop = (config) => {    const {      type,      className,      duration = 4.5,      getContainer = () => document.body,      icon,      key,      content,      onClose,      onClick,      top,      closable = true,      closeIcon    } = config    message.notice({      content: <div className={classnames('xMessage', className )}>        {          (icon || ['info', 'success', 'error', 'warning'].indexOf(type) > -1) &&          <div className={classnames('iconWrap', type)}>            {              icon ? icon : <Icon type={iconType[type]} />            }          </div>        }        <div className="xNoticeTit">          { content }        </div>      </div>,      key,      closable,      getContainer,      onClose() {        onClose && onClose()      },      onClick() {        onClick && onClick()      },      closeIcon,      duration,      style: { top }    })  }

该方法主要用来自定义建立全局消息的实例,咱们能够这么调用:

xNotification.pop({type: 'success', content: '你的请求被审批经过啦!'})

实际效果以下:

antd一样的方式会这么调用:

// antdNotification.info({//...})

笔者之因此会这么作是由于info,success,warning这样的状态其实dom结构彻底能够复用,因此经过配置方式能够极大的减小冗余代码。

remove和destroy方法都比较简单,咱们直接上代码:

const remove = (key) => {    message.removeNotice(key)  }
const destroy = () => {  message.destroy()}

由上能够看出他们的实现都是基于message实例自带的API。

2.2 实现通知框类型type和自定义icon

首先咱们先定义一个类型和icon的映射关系:

const iconType = {    success: 'FaRegCheckCircle',    warning: 'FaRegMeh',    info: 'FaRegLightbulb',    error: 'FaRegTimesCircle' }

这四种类型对应着不一样的icon图标类型,那么咱们就能够根据用户传入的类型来展现不一样icon图标了:

<div className={classnames('iconWrap', type)}>    <Icon type={iconType[type]} /></div>

不过咱们还须要考虑的一点就是若是用户传入了自定义的icon,咱们理论上应该展现自定义icon,因此type因该和icon这两个属性是有联系的。还有一种状况就是若是用户即没有配置type,有没有传入icon,那么其实是不须要显示icon的,综合考虑以后咱们的代码以下:

{  (icon || ['info', 'success', 'error', 'warning'].indexOf(type) > -1) &&  <div className={classnames('iconWrap', type)}>    {      icon ? icon : <Icon type={iconType[type]} />    }  </div>}

实现效果以下图:

经过以上步骤, 全局提示(Message)组件就完成了.实现方式和Notification组件有不少类似点,若是不懂的能够在评论区提问,笔者看到后会第一时间解答.

2.3 使用全局提示(Message)组件

咱们能够经过以下方式使用它:

<Button type="warning" onClick={        () => {            message.pop({                type: 'error',                content: '趣谈前端学习打卡'            })        }    }>错误信息通知(error)</Button>

配置全局属性:

import { message } from '@alex_xu/xui'message.config({ duration: 6 })

笔者已经将实现过的组件发布到npm上了,你们若是感兴趣能够直接用npm安装后使用,方式以下:

npm i @alex_xu/xui
// 导入xuiimport {   Button, Skeleton, Empty, Progress,  Message, Tag, Switch, Drawer, Badge, Alert} from '@alex_xu/xui'

该组件库支持按需导入,咱们只须要在项目里配置babel-plugin-import便可,具体配置以下:

// .babelrc"plugins": [  ["import", { "libraryName": "@alex_xu/xui", "style": true }]]

npm库截图以下:

最后

后续笔者将会继续实现

  • table(表格),

  • tooltip(工具提示条),

  • Skeleton(骨架屏),

  • form(form表单),

  • switch(开关),

  • 日期/日历,

  • 二维码识别器组件

等组件, 来复盘笔者多年的组件化之旅.

若是对于react/vue组件设计原理不熟悉的,能够参考个人以前写的组件设计系列文章:

笔者已经将组件库发布到npm上了, 你们能够经过npm安装的方式体验组件.

若是想获取组件设计系列完整源码, 或者想学习更多H5游戏, webpacknodegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》加入咱们的技术群一块儿学习讨论,共同探索前端的边界。

原创不易,记得点赞关注哦,你的支持是我最好的动力~