精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification)

徐小夕 等级 354 0 0

精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification)

前言

本文是笔者写组件设计的第十篇文章, 今天带大家实现一个比较特殊的组件——通知提醒框(Notification)。 该组件在诸如Antd或者elementUI等第三方组件库中也都会出现,主要用来为用户提供系统通知信息的.我们在调用它时并不像其他组件一样,通过引入组件标签来调用。比如Modal组件,我们一般这样来调用:

<Modal title="xui基础弹窗" centered mask={false} visible={false}>
    <p>我是弹窗内容</p>
    <p>我是弹窗内容</p>
    <p>我是弹窗内容</p>
    <p>我是弹窗内容</p>
</Modal>

但是通知提醒框(Notification),大多数场景下是使用js API的方式调用:

notification.open({
  message: '趣谈前端React',
  description: '学前端,学React/vue/Node,快快加入我们吧'
});

我们看到的组件效果可能是这样的:

精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification) 那么我们如何实现这样的调用方式呢?不用急,接下来笔者会一步步教你实现。

先来巩固以下组件的分类法:

  • 通用型组件: 比如Button, Icon等.
  • 布局型组件: 比如Grid, Layout布局等.
  • 导航型组件: 比如面包屑Breadcrumb, 下拉菜单Dropdown, 菜单Menu等.
  • 数据录入型组件: 比如form表单, Switch开关, Upload文件上传等.
  • 数据展示型组件: 比如Avator头像, Table表格, List列表等.
  • 反馈型组件: 比如Progress进度条, Drawer抽屉, Modal对话框等.
  • 其他业务类型

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

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

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

正文

在开始组件设计之前希望大家对css3和js有一定的基础,并了解基本的react/vue语法.我们先来解构一下Notification组件, 一个Notification分为以下几个部分: 精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification) 每一个区块都可以自定义配置, 也可以组合其他组件.并且我们可以配置提醒框出现的位置,就像antd的组件一样,我们有左上,左下,右上,右下这几个位置可以配置,也可以配置基于这几个位置的偏移量。并且我们都知道,antd或者element这种组件库,会自带一些主题状态,来提高用户的使用效率,比如会有success(成功状态),warning(警告状态),error(错误状态),info(通知状态)等,那么我们自己实现的组件也因该具备这些功能。

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

精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification)

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

1. Notification组件设计思路

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

  • 能控制Notification自动关闭的时间
  • 能配置Notification渲染节点的输出位置
  • 能控制Notification的弹出位置
  • 能自定义关闭图标
  • 可以手动选择通知窗类型
  • 能自定义通知框的偏移量
  • 能设置通知框的信息和提示文本
  • 能自定义通知框的Icon
  • 通知框点击时提供回调函数
  • 通知框关闭时提供回调函数
  • 能手动销毁通知框

需求收集好之后,作为一个有追求的程序员, 会得出如下线框图: 精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification) 其实通知提醒框要考虑的东西挺多的,所以在设计组件之前,一定要想理清需求和功能划分,这样才能有条不絮的去实现它,和我们实现一个复杂系统是一样的,一个组件就是一个小系统。

2. 基于react实现一个通知提醒框(Notification)

通知框的API调用实现思路其实就是通过jsx动态渲染约定好的标签,然后通过ReactDom的Render API将dom渲染到指定容器内挂在到页面,其中要想实现Notification.info这样的方式还需要考虑到创建实例的问题,我们应该使用单例模式来控制实例的创建个数。伪代码如下:

const xNotification = (function() {
    let notification = null;
    if(notification) {
        return {
            render(dom){},
            config(config){},
            info(config){},
            error(config){}
            // ...
        }
    }else {
        notification = new Notification({})
        return {
            render(dom){},
            config(config){},
            info(config){},
            error(config){}
            // ...
        }
    }
})()
// 使用
xNotification.info({...})
xNotification.error({...})

但是真正要实现以上需求讨论的那些通知框的功能,实际上我们还是要写很多代码来处理不同的情况的,所以为了方便大家理解,我们这里使用React Notification这个第三方库来帮我们处理基本的逻辑,笔者会基于它,来实现上面我们讨论的那些功能。

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

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

import Notification from 'rc-notification'
import './index.less'

const xNotification = (function() {
  let notification = null
  /**
     * notice类型弹窗
     * @param {config}  object 通知框配置属性
     *   @param {type} string 通知窗类型
     *   @param {btn}  ReactNode 自定义关闭按钮
     *   @param {bottom}  number 消息从底部弹出时,距离底部的位置,单位像素
     *   @param {className}  string 自定义 CSS class
     *   @param {description}  string|ReactNode 通知提醒内容,必选
     *   @param {duration}  number 默认 4.5 秒后自动关闭,配置为 null 则不自动关闭
     *   @param {getContainer}  HTMLNode 配置渲染节点的输出位置
     *   @param {icon}  ReactNode 自定义图标
     *   @param {key}  string 当前通知唯一标志
     *   @param {message}  string|ReactNode 通知提醒标题,必选
     *   @param {onClose}  func 点击默认关闭按钮时触发的回调函数
     *   @param {onClick}  func 点击通知时触发的回调函数
     *   @param {top}  number 消息从顶部弹出时,距离顶部的位置,单位像素
     *   @param {closeIcon}  ReactNode 自定义关闭图标
     */
  const pop = (config) => {
    const {
      type, bottom, className, description, duration = 4.5,
      getContainer = () => document.body, icon, 
      key, message, onClose, onClick, top, closable = true, closeIcon
    } = config
    notification.notice({
      content: <div className={classnames('xNotice', className )}>
        <div className={classnames('iconWrap', type)}>
            <Icon type={iconType[type]} />
        </div>
        <div>
          <div className="xNoticeTit">
            { message }
          </div>
          <div className="xNoticeDesc">
            { description }
          </div>
        </div>
      </div>
    })
  }
  /**
     * 通知提示组件, 全局参数
     * @param {bottom} number 消息从底部弹出时,距离底部的位置,单位像素, 默认24
     * @param {duration} number 默认自动关闭延时,单位秒
     * @param {getContainer} HTMLNode 配置渲染节点的输出位置,默认document.body
     * @param {placement} string 弹出位置,可选 topLeft topRight bottomLeft bottomRight
     * @param {top} number 消息从顶部弹出时,距离顶部的位置,单位像素
     * @param {closeIcon} HTMLNode 自定义关闭图标
  */
  const config = (config) => {
    const { duration, getContainer, placement, closeIcon } = config
    Notification.newInstance({
      getContainer: getContainer,
      duration: duration || 4.5,
      closeIcon
    }, (notice) => notification = notice)
  }

  if(notification) {
    return {
      config,
      pop
    }
  }
  // 如果为创建实例,则创建默认实例
  Notification.newInstance({}, (notice) => notification = notice)
  return {
    config,
    pop
  }
})()

export default xNotification

首先我们根据需求把一项项的属性都罗列出来。我们在全局使用的配置方法是xNotification.config(config), 在通知框实例中我们使用xNotification.pop(config)。这点和antd的使用方式有点不同,笔者是把通知框类型放到pop的config来处理了,比如说要渲染一个成功的通知框,我们可以这么做:

xNotification.pop({type: 'success'})

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

// antd
Notification.info({//...})

笔者之所以会这么做是因为info,success,warning这样的状态其实dom结构完全可以复用,所以通过配置方式可以极大的减少冗余代码。

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

笔者其实在搭建组件框架的时候已经完成了部分属性的配置,所以这里就不一一介绍了,笔者将会介绍一些比较重要的方法的实现。

通过观察我们可以知道要想实现不同的通知框类型,只需要根据类型来动态替换icon就行了。icon图标部分采用笔者已经实现的Icon组件,具体用法和antd的Icon组件类似,如果想学习如何封装属于自己的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>
}

实现效果如下图: 精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification) 精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification) 精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification)

2.3 通知框位置placement

通知框的位置根据业务场景来看因该是全局配置,所以我们放在config方法里设置,关于如何根据用户传入的位置信息来控制Notification显示的位置,我们也可以先定义一个枚举类:

const adapterPos = {
    topLeft: {
      top: '24px',
      left: '24px'
    },
    topRight: {
      top: '24px',
      right: '24px'
    },
    bottomLeft: {
      bottom: '24px',
      left: '24px'
    },
    bottomRight: {
      bottom: '24px',
      right: '24px'
    }
}

从上面代码可以看到我们会定义四个基础位置,默认偏移都是24px,然后我们就可以根据用处传入的placement来匹配自己的位置信息了:

Notification.newInstance({
  style: {...adapterPos[placement] },
  // ...

上面代码可以知道位置信息我们是通过style来设置的。具体效果如下: 精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification) 精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification) 精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification) 精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification)

2.4 实现通知框动画效果

动画我们实现一个类似与antd的从右往左入场的动画,我们来改写样式如下:

.rc-notification-fade-enter {
    animation: moveLeft .3s;
}
.rc-notification-fade-leave {
    animation: moveOutLeft .3s;
}
.rc-notification-fade-enter.rc-notification-fade-enter-active {
    animation: moveLeft .3s;
}
.rc-notification-fade-leave.rc-notification-fade-leave-active {
    animation-name: moveOutLeft .3s;
}

@keyframes moveOutLeft {
    0% {

    }
    100% {
        right: -200%;
    }
}
@keyframes moveLeft {
    0% {
        right: -200%;
    }
    100% {
        right: 0;
    }
}

通过以上步骤, 一个功能强大的通知提醒框(Notification)就完成了.Notification组件算是组件库中中等复杂的组件,如果不懂的可以在评论区提问,笔者看到后会第一时间解答.

2.5 使用Notification组件

我们可以通过如下方式使用它:

<Button type="primary" onClick={
    () => {
        xNotification.pop({
            type: 'success',
            message: '趣谈前端学习打卡',
            description: '前端基础,中级进阶,高级打卡,一起玩转前端,996远离你'
        })
    }
}>点我显示通知</Button>

配置全局属性:

import { xNotification } from '@alex_xu/xui'

xNotification.config({
    placement: 'topRight'
})

笔者已经将实现过的组件发布到npm上了,大家如果感兴趣可以直接用npm安装后使用,方式如下:

npm i @alex_xu/xui

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

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

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

npm库截图如下: 精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification)

最后

后续笔者将会继续实现

  • table(表格),
  • tooltip(工具提示条),
  • Skeleton(骨架屏),
  • Message(全局提示),
  • form(form表单),
  • switch(开关),
  • 日期/日历,
  • 二维码识别器组件

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

如果对于react/vue组件设计原理不熟悉的,可以参考我的之前写的组件设计系列文章:

笔者已经将组件库发布到npm上了, 大家可以通过npm安装的方式体验组件.

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

精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification)

更多推荐

收藏
评论区

相关推荐

教你用200行代码写一个爱豆拼拼乐H5小游戏(附源码)
前言 本文将带大家一步步实现一个H5拼图小游戏,考虑到H5游戏的轻量级和代码体积,我没有使用react或vue这些框架,而采用我自己写的dom库和原生javascript来实现业务功能,具体库代码可见我的文章如何用不到200行代码写一款属于自己的js类库(https://juejin.im/post/6844903880707293198),构建工具我采
vue-toy: 200行代码模拟Vue实现
vuetoy 200行左右代码模拟vue实现,视图渲染部分使用React来代替Sanbbdom,欢迎Star。 项目地址:https://github.com/bplok20010/vuetoy(https://github.com/bplok20010/vuetoy) codesandbox示例(https://codes
前端组件/库打包利器rollup使用与配置实战
目前主流的前端框架vue和react都采用rollup来打包,为了探索rollup的奥妙,接下来就让我们一步步来探索,并基于rollup搭建一个库打包脚手架,来发布自己的库和组件。 (https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/16cb1c297071015523fb08d9e0f
基于jsoneditor二次封装一个可实时预览的json编辑器组件(react版)
前言 做为一名前端开发人员,掌握vue/react/angular等框架已经是必不可少的技能了,我们都知道,vue或react等MVVM框架提倡组件化开发,这样一方面可以提高组件复用性和可扩展性,另一方面也带来了项目开发的灵活性和可维护,方便多人开发协作.接下来文章将介绍如何使用react,开发一个自定义json编辑器组件.我们这里使用了jsoneditor
《精通react/vue组件设计》之用纯css打造类materialUI的按钮点击动画并封装成react组件
前言 作为一个前端框架的重度使用者,在技术选型上也会非常注意其生态和完整性.笔者先后开发过基于vue,react,angular等框架的项目,碧如vue生态的elementUI, antdesignvue, iView等成熟的UI框架, react生态的antdesign, materialUI等,这些第三方UI框架极大的降低了我们开发一个项目的成本和
《精通react/vue组件设计》之配合React Portals实现一个功能强大的抽屉(Drawer)组件
前言 本文是笔者写组件设计的第六篇文章,内容依次从易到难,今天会用到react的高级API React Portals,它也是很多复杂组件必用的方法之一. 通过组件的设计过程,大家会接触到一个完成健壮的组件设计思路和方法,也能在实现组件的过程逐渐对react/vue的高级知识和技巧有更深的理解和掌握,并且在企业实际工作做游刃有余. 之所以会写组件设计相关
精通React/Vue系列之手把手带你实现一个功能强大的通知提醒框(Notification)
前言 本文是笔者写组件设计的第十篇文章, 今天带大家实现一个比较特殊的组件——通知提醒框(Notification)。 该组件在诸如Antd或者elementUI等第三方
Create React App
Create React App Create React App Facebook开源了React前端框架(MIT Licence),也同时提供了React脚手架 createreactapp(ht
Taro 入门教程
简介 Taro 是一个遵循 React 语法规范的开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发微信/京东/百度/
MobX 上手指南
之前用 Redux 比较多,一直听说 Mobx 能让你体验到在 React 里面写 Vue 的感觉,今天打算尝试下 Mobx 是不是真的有写 Vue 的感觉。 题外话 在介绍 MobX 的用法之前,先说点题外话,我们可以看一下 MobX 的中文简介。在 MobX 的中文网站上写着: MobX 是一个经过战火洗礼的库,它通过透明的函数响应式编程
有关CSS预编译、管理工具和网络安全的相关面试题
我经常会听到一些想入行前端的人问,前端开发需要学习哪些技术?其实,除了核心的HTML、CSS、JavaScript技术外,像CSS预编译、前端框架,如Vue,React、Node、Angular;代码管理工具,如git,代码编辑器,sublime text,还有测试,网络安全等相关技术也是需要会一些。当然要求可能不会那么高,但至少要懂一些。所有今天准备了一些
【Flutter实战】状态管理
3.2 状态管理响应式的编程框架中都会有一个永恒的主题——“状态(State)管理”,无论是在React/Vue(两者都是支持响应式编程的Web开发框架)还是Flutter中,他们讨论的问题和解决的思想都是一致的。所以,如果你对React/Vue的状态管理有了解,可以跳过本节。言归正传,我们想一个问题,StatefulWidget的状态应该被谁管理?
新手学习 React 迷惑的点
网上各种言论说 React 上手比 Vue 难,可能难就难不能深刻理解 JSX,或者对 ES6 的一些特性理解得不够深刻,导致觉得有些点难以理解,然后说 React 比较难上手,还反人类啥的,所以我打算写两篇文章来讲新手学习 React 的时候容易迷惑的点写出来,如果你还以其他的对于学习 React 很迷惑的点,可以在留言区里给我留言。为什么要引入 Reac
2021前端技术面试必备Vue:(四)Vuex状态管理
前三章陆续已经更新了Vue基本使用 和Vue Router的基本使用,如果你读了前三篇文章的话,并且掌握差不多,那么你现在可以开发简单的应用了,例如Blog,电商网站........唯一不足的是,随着你的业务需求不断增加,页面中的状态数据也不断庞大,维护起来就特别困难了,Vue 提供了一种状态管理 解决办法 Vuex,它的思想和React 的R
项目中的富文本编辑器该如何选择?
项目中经常需要用到富文本编辑器的时候,而常见的富文本编辑器都有哪些?该如何选择?先看看市面上都有哪些可用的富文本编辑器: (插件式的,支持 Vue,React,Angular 框架) (Typescript 开发的 Web 富文本编辑器, 轻量、简洁、易用、开源免费,支持 JS 直接引入使用,或者 Vue2/3,React) (开源,插件多,功能齐全,支持