摘要:通過將實例傳入回調函數。添加再回過頭來看回調函數的內容。其中的作用是一次調用傳入的各函數,其中方法是移除中相應的節點,是傳入的關閉標簽后的回調函數。
notification簡介
notification就是通知提醒框,在系統四個角顯示通知提醒信息。經常用于以下情況:
較為復雜的通知內容。
帶有交互的通知,給出用戶下一步的行動點。
系統主動推送。
先來看一下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的組件中可以說是非常另類的,看著是不是有點眼熟,很像經常使用的Console的API,調用起來十分簡單。
console.log()
console.error()
console.info()
console.warn()
config的配置也比較簡單,主要是標題,內容,關閉時的延時和回調等。詳見ANTD的官網。
notification的結構在分析代碼之前,我們先來看下notification的結構,通知組件主要分為三層,由高到低是
NotificationApi => Notification => n*Notice。
NotificationApi是一個封裝的接口,提供統一調用的API,如info(),warn()等。
Notification是一個Notice容器,就是用來容納Notice列表的父組件,提供了添加,刪除等操作Notice的方法。
Notice就是我們所看到的通知標簽了。
源碼分析先從入口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函數實現的,open函數的具體實現是notice,那么看下這個notice函數。
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}
這段代碼主要的部分就是調用了getNotificationInstance函數,看名字應該是得到Notification的實例,命名方式是典型的單例模式,作為列表的容器組件,使用單例模式不僅節省了內存空間,而且單例延遲執行的特性也保證了在沒有通知的情況下不會生成notification組件,提升了頁面的性能。
function getNotificationInstance(prefixCls: string, placement: NotificationPlacement, callback: (n: any) => void)
查看定義,第一個參數是css前綴,第二個參數是notification的彈出位置,分為topLeft topRight bottomLeft bottomRight,第三個參數是一個回調,回調的參數是notification實例,可以看到,在回調中調用了notification的notice方法,notice方法的參數是一個對象,content看名字應該是通知標簽的內容,其他的參數也是調用notification中傳入的config參數。
接下來看下getNotificationInstance的實現
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]數組里,cacheKey是css前綴和彈出位置的組合,用以區分每個實例。接下來進入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回調函數。
可以看到傳入callback的參數對notification又做了一層封裝,目的是為了封裝destroy函數,其中
+ notice():添加一個notice組件到notification + removeNotice():刪除指定notice組件。 + destroy():銷毀notification組件。添加Notice
再回過頭來看回調函數的內容。
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}
調用了notification的notice方法,由前面的代碼可知notice其實是調用了Notification組件的add方法,記下來看下add方法是怎樣將標簽添加進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函數動態添加,key是該notice的唯一標識,通過filter將已存在的標簽過濾掉??梢韵胍?,Notification就是將state中的notices通過map渲染出要顯示的標簽列表,直接進入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}
根據state的notices生成Notice組件列表noticeNodes,然后將noticeNodes插入到一個Animate的動畫組件中。其中createChainedFunction的作用是一次調用傳入的各函數,其中remove方法是移除state中相應的節點,onClose是傳入的關閉標簽后的回調函數。
看到這里Notification的結構已經比較清晰了,最后再來看下Notice組件的實現。
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 ( ); } }
這個組件比較簡單,主要是實現標簽顯示一段時間后自動消失,通過setTimeout設置一段時間后調用close方法,也就是上一段代碼中實現的移除state中的相應節點以及調用相應的回調函數。
總結看到這里antd的通知組件的實現已經比較清晰了,代碼并沒有特別復雜的部分,但是這種使用單例模式動態添加組件的設計十分值得借鑒,在實現類似通知組件或者需要動態添加的組件的時候可以參考這種設計模式,antd的Message組件也采用了同樣的設計。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93885.html
摘要:返回刪除的節點。組件運行邏輯此時,組件的代碼與邏輯已經全部分析完了,整個組件的運行邏輯可以通過下方這張圖來概括本篇完 Wave組件效果預覽 ???????在上一篇文章Button組件的源碼分析中遇到了一個Wave組件, Wave組件在Ant design中提供了通用的表單控件點擊效果,在自己閱讀源碼之前,也并沒有過更多留心過在這些表單控件的動畫效果是如何實現的,甚至可能有時都沒注意到這...
摘要:基本開發環境創建的項目,作為代碼編寫工具插件推薦插件配置文章目錄項目目錄結構介紹框架選擇處理請求二次封裝項目目錄結構簡介業務相關靜態文件全局組件基礎樣式布局樣式及工具引入請求配置路由全局狀態管理工具文件入口文件主要配置文件頁面檢查配置測試 基本開發環境 vue-cli3 創建的項目,vscode 作為代碼編寫工具vscode插件推薦:vscode 插件配置 文章目錄 項目目錄結構介紹...
摘要:初學,擼一個熟悉熟悉基本語法,只有最簡單最簡單的功能。引入這個之后,我們可以直接使用一些簡單的組件,比如等,我們可以更加注重業務邏輯的實現。 初學React,擼一個TodoList熟悉熟悉基本語法,只有最簡單最簡單的功能。 showImg(https://segmentfault.com/img/remote/1460000010376536); 如上圖所示,是一個最簡單的TodoLi...
摘要:擅長網站建設微信公眾號開發微信小程序開發小游戲制作企業微信制作建設,專注于前端框架服務端渲染技術交互設計圖像繪制數據分析等研究。 Ant Design of React @3.10.9 拉取項目 luwei.web.study-ant-design-pro, 切換至 query 分支,可看到 Form 表單實現效果 實現一個查詢表單 showImg(https://segmentfau...
閱讀 1210·2019-08-30 15:55
閱讀 957·2019-08-30 15:55
閱讀 2155·2019-08-30 15:44
閱讀 2889·2019-08-29 14:17
閱讀 1136·2019-08-29 12:45
閱讀 3311·2019-08-26 10:48
閱讀 3136·2019-08-23 18:18
閱讀 2604·2019-08-23 16:47