本文是筆者寫組件設計的第十一篇文章, 今天帶你們實現一個一樣比較特殊的組件——全局提示(Message)組件。 咱們看到的組件效果多是這樣的: javascript
因爲全局提示組件的設計原理和筆者上一篇寫的 精通React/Vue系列之手把手帶你實現一個功能強大的通知提醒框(Notification)是相似的,區別主要是佈局和配置參數,因此說細節和實現原理部分就不在這篇文章介紹了,本文主要介紹設計思路和設計的方法。基礎組件庫主要按如下分類來劃分:css
熟悉以上分類法是設計任何組件系統的前提,無論你是從零到一開發前端團隊的UI庫,仍是基於已有組件庫二次開發業務組件,以上分類法則一樣適用。html
本文將會使用React來開發該組件,也會使用到Javascript中經常使用的一些設計模式,好比單例模式,可是無論你使用什麼框架來實現,原理都是通用的,若是感興趣的朋友能夠用vue也實現一下。若是對設計模式不是很瞭解,能夠移步:前端
15分鐘帶你瞭解前端工程師必知的javascript設計模式(附詳細思惟導圖和源碼).vue
在開始組件設計以前但願你們對css3和js有必定的基礎,並瞭解基本的react/vue語法.咱們先來解構一下Message組件, 一個Message分爲如下幾個部分:java
每個區塊均可以自定義配置, 也能夠組合其餘組件.咱們還能夠配置全局提示出如今頂部的偏移量,相似於antd的組件同樣。而且咱們都知道,antd或者element這種組件庫,會自帶一些主題狀態,來提升用戶的使用效率,好比會有 success(成功狀態), warning(警告狀態), error(錯誤狀態), info(通知狀態)等,那麼咱們本身實現的全局提示( Message)組件也因該具有這些功能。如下是筆者使用React實現後的Message組件效果: node
接下來咱們來看看通知提醒框(Message)的具體設計思路。react
按照以前筆者總結的組件設計原則,咱們第一步是要確認需求. 通知提醒框(Message)組件通常會有以下需求點:webpack
需求收集好以後,做爲一個有追求的程序員, 會得出以下線框圖: css3
具體的設計細節能夠參考個人上一篇 Notification組件設計的文章。組件的核心部分咱們仍是採用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一樣的方式會這麼調用:// 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> } 複製代碼
實現效果以下圖:
經過以上步驟, 全局提示(Message)組件就完成了.實現方式和Notification組件有不少類似點,若是不懂的能夠在評論區提問,筆者看到後會第一時間解答.咱們能夠經過以下方式使用它:
<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數據可視化等前端知識和實戰,歡迎在公號《趣談前端》加入咱們的技術羣一塊兒學習討論,共同探索前端的邊界。