摘要:通過將實例傳入回調(diào)函數(shù)。添加再回過頭來看回調(diào)函數(shù)的內(nèi)容。其中的作用是一次調(diào)用傳入的各函數(shù),其中方法是移除中相應(yīng)的節(jié)點,是傳入的關(guān)閉標(biāo)簽后的回調(diào)函數(shù)。
notification簡介
notification就是通知提醒框,在系統(tǒng)四個角顯示通知提醒信息。經(jīng)常用于以下情況:
較為復(fù)雜的通知內(nèi)容。
帶有交互的通知,給出用戶下一步的行動點。
系統(tǒng)主動推送。
先來看一下notification的API。
APInotification.success(config)
notification.error(config)
notification.info(config)
notification.warning(config)
notification.warn(config)
notification.close(key: String)
notification.destroy()
可以看到,notification的API在antd的組件中可以說是非常另類的,看著是不是有點眼熟,很像經(jīng)常使用的Console的API,調(diào)用起來十分簡單。
console.log()
console.error()
console.info()
console.warn()
config的配置也比較簡單,主要是標(biāo)題,內(nèi)容,關(guān)閉時的延時和回調(diào)等。詳見ANTD的官網(wǎng)。
notification的結(jié)構(gòu)在分析代碼之前,我們先來看下notification的結(jié)構(gòu),通知組件主要分為三層,由高到低是
NotificationApi => Notification => n*Notice。
NotificationApi是一個封裝的接口,提供統(tǒng)一調(diào)用的API,如info(),warn()等。
Notification是一個Notice容器,就是用來容納Notice列表的父組件,提供了添加,刪除等操作Notice的方法。
Notice就是我們所看到的通知標(biāo)簽了。
源碼分析先從入口index.js入手,因為這是一個notification的API封裝,不是一個組件,所以沒有render方法。
//.......省略部分代碼........ const api: any = { open: notice,//入口 close(key: string) { Object.keys(notificationInstance) .forEach(cacheKey => notificationInstance[cacheKey].removeNotice(key)); }, config: setNotificationConfig, destroy() { Object.keys(notificationInstance).forEach(cacheKey => { notificationInstance[cacheKey].destroy(); delete notificationInstance[cacheKey]; }); }, }; //.......省略部分代碼........ ["success", "info", "warning", "error"].forEach((type) => { api[type] = (args: ArgsProps) => api.open({ ...args, type, }); }); api.warn = api.warning; export interface NotificationApi { success(args: ArgsProps): void; error(args: ArgsProps): void; info(args: ArgsProps): void; warn(args: ArgsProps): void; warning(args: ArgsProps): void; open(args: ArgsProps): void; close(key: string): void; config(options: ConfigProps): void; destroy(): void; } export default api as NotificationApi;
接口比較清晰,可以看出API提供的不同的方法實際是通過一個類似工廠方法的open函數(shù)實現(xiàn)的,open函數(shù)的具體實現(xiàn)是notice,那么看下這個notice函數(shù)。
function notice(args: ArgsProps) { const outerPrefixCls = args.prefixCls || "ant-notification"; const prefixCls = `${outerPrefixCls}-notice`; const duration = args.duration === undefined ? defaultDuration : args.duration; //生成icon組件 let iconNode: React.ReactNode = null; if (args.icon) { iconNode = ( {args.icon} ); } else if (args.type) { const iconType = typeToIcon[args.type]; iconNode = (); } const autoMarginTag = (!args.description && iconNode) ? : null; getNotificationInstance(outerPrefixCls, args.placement || defaultPlacement, (notification: any) => { notification.notice({ content: ( {iconNode}), duration, closable: true, onClose: args.onClose, key: args.key, style: args.style || {}, className: args.className, }); }); }{autoMarginTag} {args.message}{args.description}{args.btn ? {args.btn} : null}
這段代碼主要的部分就是調(diào)用了getNotificationInstance函數(shù),看名字應(yīng)該是得到Notification的實例,命名方式是典型的單例模式,作為列表的容器組件,使用單例模式不僅節(jié)省了內(nèi)存空間,而且單例延遲執(zhí)行的特性也保證了在沒有通知的情況下不會生成notification組件,提升了頁面的性能。
function getNotificationInstance(prefixCls: string, placement: NotificationPlacement, callback: (n: any) => void)
查看定義,第一個參數(shù)是css前綴,第二個參數(shù)是notification的彈出位置,分為topLeft topRight bottomLeft bottomRight,第三個參數(shù)是一個回調(diào),回調(diào)的參數(shù)是notification實例,可以看到,在回調(diào)中調(diào)用了notification的notice方法,notice方法的參數(shù)是一個對象,content看名字應(yīng)該是通知標(biāo)簽的內(nèi)容,其他的參數(shù)也是調(diào)用notification中傳入的config參數(shù)。
接下來看下getNotificationInstance的實現(xiàn)
function getNotificationInstance(prefixCls: string, placement: NotificationPlacement, callback: (n: any) => void) { const cacheKey = `${prefixCls}-${placement}`; if (notificationInstance[cacheKey]) { callback(notificationInstance[cacheKey]); return; } //---實例化Notification組件 (Notification as any).newInstance({ prefixCls, className: `${prefixCls}-${placement}`, style: getPlacementStyle(placement), getContainer: defaultGetContainer, }, (notification: any) => { notificationInstance[cacheKey] = notification; callback(notification); }); }
代碼很簡短,可以看到確實是使用了單例模式,因為存在4個彈出位置,所以將每個位置的notification實例存放在notificationInstance[cacheKey]數(shù)組里,cacheKey是css前綴和彈出位置的組合,用以區(qū)分每個實例。接下來進(jìn)入newInstance方法來看下是怎么使用單例模式生成notification實例的。
實例化NotificationNotification.newInstance = function newNotificationInstance(properties, callback) { const { getContainer, ...props } = properties || {}; const div = document.createElement("div"); if (getContainer) { const root = getContainer(); root.appendChild(div); } else { document.body.appendChild(div); } let called = false; function ref(notification) { if (called) { return; } called = true; callback({ notice(noticeProps) { notification.add(noticeProps); }, removeNotice(key) { notification.remove(key); }, component: notification, destroy() { ReactDOM.unmountComponentAtNode(div); div.parentNode.removeChild(div); }, }); } ReactDOM.render(, div); };
主要完成了兩件事
通過ReactDOM.render將Notification組件渲染到頁面上,可以選擇渲染到傳入的container或者body中。
通過ref將notification實例傳入callback回調(diào)函數(shù)。
可以看到傳入callback的參數(shù)對notification又做了一層封裝,目的是為了封裝destroy函數(shù),其中
+ notice():添加一個notice組件到notification + removeNotice():刪除指定notice組件。 + destroy():銷毀notification組件。添加Notice
再回過頭來看回調(diào)函數(shù)的內(nèi)容。
getNotificationInstance(outerPrefixCls, args.placement || defaultPlacement, (notification: any) => { notification.notice({ content: ({iconNode}), duration, closable: true, onClose: args.onClose, key: args.key, style: args.style || {}, className: args.className, }); });{autoMarginTag} {args.message}{args.description}{args.btn ? {args.btn} : null}
調(diào)用了notification的notice方法,由前面的代碼可知notice其實是調(diào)用了Notification組件的add方法,記下來看下add方法是怎樣將標(biāo)簽添加進(jìn)Notification的。
//省略部分代碼 state = { notices: [], }; //省略部分代碼 add = (notice) => { const key = notice.key = notice.key || getUuid(); this.setState(previousState => { const notices = previousState.notices; if (!notices.filter(v => v.key === key).length) { return { notices: notices.concat(notice), }; } }); }
Notification將要顯示的通知列表存在state的notices中,同通過add函數(shù)動態(tài)添加,key是該notice的唯一標(biāo)識,通過filter將已存在的標(biāo)簽過濾掉??梢韵胍姡琋otification就是將state中的notices通過map渲染出要顯示的標(biāo)簽列表,直接進(jìn)入Notification組件的render方法。
render() { const props = this.props; const noticeNodes = this.state.notices.map((notice) => { const onClose = createChainedFunction(this.remove.bind(this, notice.key), notice.onClose); return ({notice.content} ); }); const className = { [props.prefixCls]: 1, [props.className]: !!props.className, }; return (); } }{noticeNodes}
根據(jù)state的notices生成Notice組件列表noticeNodes,然后將noticeNodes插入到一個Animate的動畫組件中。其中createChainedFunction的作用是一次調(diào)用傳入的各函數(shù),其中remove方法是移除state中相應(yīng)的節(jié)點,onClose是傳入的關(guān)閉標(biāo)簽后的回調(diào)函數(shù)。
看到這里Notification的結(jié)構(gòu)已經(jīng)比較清晰了,最后再來看下Notice組件的實現(xiàn)。
export default class Notice extends Component { static propTypes = { duration: PropTypes.number, onClose: PropTypes.func, children: PropTypes.any, }; static defaultProps = { onEnd() { }, onClose() { }, duration: 1.5, style: { right: "50%", }, }; componentDidMount() { this.startCloseTimer(); } componentWillUnmount() { this.clearCloseTimer(); } close = () => { this.clearCloseTimer(); this.props.onClose(); } startCloseTimer = () => { if (this.props.duration) { this.closeTimer = setTimeout(() => { this.close(); }, this.props.duration * 1000); } } clearCloseTimer = () => { if (this.closeTimer) { clearTimeout(this.closeTimer); this.closeTimer = null; } } render() { const props = this.props; const componentClass = `${props.prefixCls}-notice`; const className = { [`${componentClass}`]: 1, [`${componentClass}-closable`]: props.closable, [props.className]: !!props.className, }; return ( ); } }
這個組件比較簡單,主要是實現(xiàn)標(biāo)簽顯示一段時間后自動消失,通過setTimeout設(shè)置一段時間后調(diào)用close方法,也就是上一段代碼中實現(xiàn)的移除state中的相應(yīng)節(jié)點以及調(diào)用相應(yīng)的回調(diào)函數(shù)。
總結(jié)看到這里antd的通知組件的實現(xiàn)已經(jīng)比較清晰了,代碼并沒有特別復(fù)雜的部分,但是這種使用單例模式動態(tài)添加組件的設(shè)計十分值得借鑒,在實現(xiàn)類似通知組件或者需要動態(tài)添加的組件的時候可以參考這種設(shè)計模式,antd的Message組件也采用了同樣的設(shè)計。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/93885.html
摘要:返回刪除的節(jié)點。組件運行邏輯此時,組件的代碼與邏輯已經(jīng)全部分析完了,整個組件的運行邏輯可以通過下方這張圖來概括本篇完 Wave組件效果預(yù)覽 ???????在上一篇文章Button組件的源碼分析中遇到了一個Wave組件, Wave組件在Ant design中提供了通用的表單控件點擊效果,在自己閱讀源碼之前,也并沒有過更多留心過在這些表單控件的動畫效果是如何實現(xiàn)的,甚至可能有時都沒注意到這...
摘要:基本開發(fā)環(huán)境創(chuàng)建的項目,作為代碼編寫工具插件推薦插件配置文章目錄項目目錄結(jié)構(gòu)介紹框架選擇處理請求二次封裝項目目錄結(jié)構(gòu)簡介業(yè)務(wù)相關(guān)靜態(tài)文件全局組件基礎(chǔ)樣式布局樣式及工具引入請求配置路由全局狀態(tài)管理工具文件入口文件主要配置文件頁面檢查配置測試 基本開發(fā)環(huán)境 vue-cli3 創(chuàng)建的項目,vscode 作為代碼編寫工具vscode插件推薦:vscode 插件配置 文章目錄 項目目錄結(jié)構(gòu)介紹...
摘要:初學(xué),擼一個熟悉熟悉基本語法,只有最簡單最簡單的功能。引入這個之后,我們可以直接使用一些簡單的組件,比如等,我們可以更加注重業(yè)務(wù)邏輯的實現(xiàn)。 初學(xué)React,擼一個TodoList熟悉熟悉基本語法,只有最簡單最簡單的功能。 showImg(https://segmentfault.com/img/remote/1460000010376536); 如上圖所示,是一個最簡單的TodoLi...
摘要:擅長網(wǎng)站建設(shè)微信公眾號開發(fā)微信小程序開發(fā)小游戲制作企業(yè)微信制作建設(shè),專注于前端框架服務(wù)端渲染技術(shù)交互設(shè)計圖像繪制數(shù)據(jù)分析等研究。 Ant Design of React @3.10.9 拉取項目 luwei.web.study-ant-design-pro, 切換至 query 分支,可看到 Form 表單實現(xiàn)效果 實現(xiàn)一個查詢表單 showImg(https://segmentfau...
閱讀 1269·2019-08-30 15:55
閱讀 1010·2019-08-30 15:55
閱讀 2220·2019-08-30 15:44
閱讀 2993·2019-08-29 14:17
閱讀 1196·2019-08-29 12:45
閱讀 3374·2019-08-26 10:48
閱讀 3190·2019-08-23 18:18
閱讀 2673·2019-08-23 16:47