本文是笔者写组件设计的第十一篇文章, 今天带你们实现一个一样比较特殊的组件——全局提示(Message)组件。 咱们看到的组件效果多是这样的: javascript
基础组件库主要按如下分类来划分:css
熟悉以上分类法是设计任何组件系统的前提,无论你是从零到一开发前端团队的UI库,仍是基于已有组件库二次开发业务组件,以上分类法则一样适用。html
本文将会使用React来开发该组件,也会使用到Javascript中经常使用的一些设计模式,好比单例模式,可是无论你使用什么框架来实现,原理都是通用的,若是感兴趣的朋友能够用vue也实现一下。若是对设计模式不是很了解,能够移步:前端
15分钟带你了解前端工程师必知的javascript设计模式(附详细思惟导图和源码).vue
在开始组件设计以前但愿你们对css3和js有必定的基础,并了解基本的react/vue语法.咱们先来解构一下Message组件, 一个Message分为如下几个部分:java
如下是笔者使用React实现后的Message组件效果: node
接下来咱们来看看通知提醒框(Message)的具体设计思路。react
按照以前笔者总结的组件设计原则,咱们第一步是要确认需求. 通知提醒框(Message)组件通常会有以下需求点:webpack
需求收集好以后,做为一个有追求的程序员, 会得出以下线框图: css3
组件的核心部分咱们仍是采用React 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:
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
Notification.info({//...})
复制代码
笔者之因此会这么作是由于info,success,warning这样的状态其实dom结构彻底能够复用,因此经过配置方式能够极大的减小冗余代码。
remove和destroy方法都比较简单,咱们直接上代码:
const remove = (key) => {
message.removeNotice(key)
}
const destroy = () => {
message.destroy()
}
复制代码
由上能够看出他们的实现都是基于message实例自带的API。
首先咱们先定义一个类型和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> } 复制代码
实现效果以下图:
咱们能够经过以下方式使用它:
<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
// 导入xui
import {
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库截图以下:
后续笔者将会继续实现
等组件, 来复盘笔者多年的组件化之旅.
若是对于react/vue组件设计原理不熟悉的,能够参考个人以前写的组件设计系列文章:
笔者已经将组件库发布到npm上了, 你们能够经过npm安装的方式体验组件.
若是想获取组件设计系列完整源码, 或者想学习更多H5游戏, webpack,node,gulp,css3,javascript,nodeJS,canvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》加入咱们的技术群一块儿学习讨论,共同探索前端的边界。