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