精通React/Vue系列之帶你實現一個功能強大的通知提醒框

2020-12-22 酷扯兒

本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫

前言

本文是筆者寫組件設計的第十篇文章, 今天帶大家實現一個比較特殊的組件——通知提醒框(Notification)。 該組件在諸如Antd或者elementUI等第三方組件庫中也都會出現,主要用來為用戶提供系統通知信息的.我們在調用它時並不像其他組件一樣,通過引入組件標籤來調用。比如Modal組件,我們一般這樣來調用:

<Modal title="xui基礎彈窗" centered mask={false} visible={false}>

<p>我是彈窗內容</p>

<p>我是彈窗內容</p>

<p>我是彈窗內容</p>

<p>我是彈窗內容</p>

</Modal>

但是通知提醒框(Notification),大多數場景下是使用js API的方式調用:

notification.open({

message: '趣談前端React',

description: '學前端,學React/vue/Node,快快加入我們吧'

});

我們看到的組件效果可能是這樣的:

那麼我們如何實現這樣的調用方式呢?不用急,接下來筆者會一步步教你實現。

先來鞏固以下組件的分類法

通用型組件: 比如Button, Icon等.布局型組件: 比如Grid, Layout布局等.導航型組件: 比如麵包屑Breadcrumb, 下拉菜單Dropdown, 菜單Menu等.數據錄入型組件: 比如form表單, Switch開關, Upload文件上傳等.數據展示型組件: 比如Avator頭像, Table表格, List列表等.反饋型組件: 比如Progress進度條, Drawer抽屜, Modal對話框等.其他業務類型熟悉以上分類法是設計任何組件系統的前提,不管你是從零到一開發前端團隊的UI庫,還是基於已有組件庫二次開發業務組件,以上分類法則同樣適用。

本文將會使用React來開發該組件,也會使用到Javascript中常用的一些設計模式,比如單例模式,但是不管你使用什麼框架來實現,原理都是通用的,如果感興趣的朋友可以用vue也實現以一下。如果對設計模式不是很了解,可以移步:

15分鐘帶你了解前端工程師必知的javascript設計模式(附詳細思維導圖和源碼).

正文

在開始組件設計之前希望大家對css3和js有一定的基礎,並了解基本的react/vue語法.我們先來解構一下Notification組件, 一個Notification分為以下幾個部分:

每一個區塊都可以自定義配置, 也可以組合其他組件.並且我們可以配置提醒框出現的位置,就像antd的組件一樣,我們有左上,左下,右上,右下這幾個位置可以配置,也可以配置基於這幾個位置的偏移量。並且我們都知道,antd或者element這種組件庫,會自帶一些主題狀態,來提高用戶的使用效率,比如會有success(成功狀態),warning(警告狀態),error(錯誤狀態),info(通知狀態)等,那麼我們自己實現的組件也因該具備這些功能。

以下是筆者使用React實現後的Notification組件效果:

接下來我們來看看通知提醒框(Notification)的具體設計思路。

1. Notification組件設計思路

按照之前筆者總結的組件設計原則,我們第一步是要確認需求. 通知提醒框(Notification)組件一般會有如下需求點:

能控制Notification自動關閉的時間能配置Notification渲染節點的輸出位置能控制Notification的彈出位置能自定義關閉圖標可以手動選擇通知窗類型能自定義通知框的偏移量能設置通知框的信息和提示文本能自定義通知框的Icon通知框點擊時提供回調函數通知框關閉時提供回調函數能手動銷毀通知框需求收集好之後,作為一個有追求的程式設計師, 會得出如下線框圖:

其實通知提醒框要考慮的東西挺多的,所以在設計組件之前,一定要想理清需求和功能劃分,這樣才能有條不絮的去實現它,和我們實現一個複雜系統是一樣的,一個組件就是一個小系統。

2. 基於react實現一個通知提醒框(Notification)

通知框的API調用實現思路其實就是通過jsx動態渲染約定好的標籤,然後通過ReactDom的Render API將dom渲染到指定容器內掛載到頁面,其中要想實現Notification.info這樣的方式還需要考慮到創建實例的問題,我們應該使用單例模式來控制實例的創建個數。偽代碼如下:

const xNotification = (function() {

let notification = null;

if(notification) {

return {

render(dom){},

config(config){},

info(config){},

error(config){}

// ...

}

}else {

notification = new Notification({})

return {

render(dom){},

config(config){},

info(config){},

error(config){}

// ...

}

}

})()

// 使用

xNotification.info({...})

xNotification.error({...})

但是真正要實現以上需求討論的那些通知框的功能,實際上我們還是要寫很多代碼來處理不同的情況的,所以為了方便大家理解,我們這裡使用React Notification這個第三方庫來幫我們處理基本的邏輯,筆者會基於它,來實現上面我們討論的那些功能。

2.1 搭建通知提醒框(Notification)的基本骨架

首先按照筆者的代碼風格,一般會考慮組件設計的框架,然後再一步步往裡面填充內容和邏輯。通過這種漸進式的設計思路,能讓我們邏輯更嚴謹,更清晰。具體代碼如下:

import Notification from 'rc-notification'

import './index.less'

const xNotification = (function() {

let notification = null

/**

* notice類型彈窗

* @param {config} object 通知框配置屬性

* @param {type} string 通知窗類型

* @param {btn} ReactNode 自定義關閉按鈕

* @param {bottom} number 消息從底部彈出時,距離底部的位置,單位像素

* @param {className} string 自定義 CSS class

* @param {description} string|ReactNode 通知提醒內容,必選

* @param {duration} number 默認 4.5 秒後自動關閉,配置為 null 則不自動關閉

* @param {getContainer} HTMLNode 配置渲染節點的輸出位置

* @param {icon} ReactNode 自定義圖標

* @param {key} string 當前通知唯一標誌

* @param {message} string|ReactNode 通知提醒標題,必選

* @param {onClose} func 點擊默認關閉按鈕時觸發的回調函數

* @param {onClick} func 點擊通知時觸發的回調函數

* @param {top} number 消息從頂部彈出時,距離頂部的位置,單位像素

* @param {closeIcon} ReactNode 自定義關閉圖標

*/

const pop = (config) => {

const {

type, bottom, className, description, duration = 4.5,

getContainer = () => document.body, icon,

key, message, onClose, onClick, top, closable = true, closeIcon

} = config

notification.notice({

content: <div className={classnames('xNotice', className )}>

<div className={classnames('iconWrap', type)}>

<Icon type={iconType[type]} />

</div>

<div>

<div className="xNoticeTit">

{ message }

</div>

<div className="xNoticeDesc">

{ description }

</div>

</div>

</div>

})

}

/**

* 通知提示組件, 全局參數

* @param {bottom} number 消息從底部彈出時,距離底部的位置,單位像素, 默認24

* @param {duration} number 默認自動關閉延時,單位秒

* @param {getContainer} HTMLNode 配置渲染節點的輸出位置,默認document.body

* @param {placement} string 彈出位置,可選 topLeft topRight bottomLeft bottomRight

* @param {top} number 消息從頂部彈出時,距離頂部的位置,單位像素

* @param {closeIcon} HTMLNode 自定義關閉圖標

*/

const config = (config) => {

const { duration, getContainer, placement, closeIcon } = config

Notification.newInstance({

getContainer: getContainer,

duration: duration || 4.5,

closeIcon

}, (notice) => notification = notice)

}

if(notification) {

return {

config,

pop

}

}

// 如果為創建實例,則創建默認實例

Notification.newInstance({}, (notice) => notification = notice)

return {

config,

pop

}

})()

export default xNotification

首先我們根據需求把一項項的屬性都羅列出來。我們在全局使用的配置方法是xNotification.config(config), 在通知框實例中我們使用xNotification.pop(config)。這點和antd的使用方式有點不同,筆者是把通知框類型放到pop的config來處理了,比如說要渲染一個成功的通知框,我們可以這麼做:

xNotification.pop({type: 'success'})

antd同樣的方式會這麼調用:

// antd

Notification.info({//...})

筆者之所以會這麼做是因為info,success,warning這樣的狀態其實dom結構完全可以復用,所以通過配置方式可以極大的減少冗餘代碼。

2.2 實現通知框類型type和自定義icon

筆者其實在搭建組件框架的時候已經完成了部分屬性的配置,所以這裡就不一一介紹了,筆者將會介紹一些比較重要的方法的實現。

通過觀察我們可以知道要想實現不同的通知框類型,只需要根據類型來動態替換icon就行了。icon圖標部分採用筆者已經實現的Icon組件,具體用法和antd的Icon組件類似,如果想學習如何封裝屬於自己的Icon組件可以參考筆者源碼。

首先我們先定義一個類型和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>

}

實現效果如下圖:

2.3 通知框位置placement

通知框的位置根據業務場景來看因該是全局配置,所以我們放在config方法裡設置,關於如何根據用戶傳入的位置信息來控制Notification顯示的位置,我們也可以先定義一個枚舉類:

const adapterPos = {

topLeft: {

top: '24px',

left: '24px'

},

topRight: {

top: '24px',

right: '24px'

},

bottomLeft: {

bottom: '24px',

left: '24px'

},

bottomRight: {

bottom: '24px',

right: '24px'

}

}

從上面代碼可以看到我們會定義四個基礎位置,默認偏移都是24px,然後我們就可以根據用處傳入的placement來匹配自己的位置信息了:

Notification.newInstance({

style: {...adapterPos[placement] },

// ...

上面代碼可以知道位置信息我們是通過style來設置的。具體效果如下:

2.4 實現通知框動畫效果

動畫我們實現一個類似於antd的從右往左入場的動畫,我們來改寫樣式如下:

.rc-notification-fade-enter {

animation: moveLeft .3s;

}

.rc-notification-fade-leave {

animation: moveOutLeft .3s;

}

.rc-notification-fade-enter.rc-notification-fade-enter-active {

animation: moveLeft .3s;

}

.rc-notification-fade-leave.rc-notification-fade-leave-active {

animation-name: moveOutLeft .3s;

}

@keyframes moveOutLeft {

0% {

}

100% {

right: -200%;

}

}

@keyframes moveLeft {

0% {

right: -200%;

}

100% {

right: 0;

}

}

通過以上步驟, 一個功能強大的通知提醒框(Notification)就完成了.Notification組件算是組件庫中中等複雜的組件,如果不懂的可以在評論區提問,筆者看到後會第一時間解答.

2.5 使用Notification組件

我們可以通過如下方式使用它:

<Button type="primary" onClick={

() => {

xNotification.pop({

type: 'success',

message: '趣談前端學習打卡',

description: '前端基礎,中級進階,高級打卡,一起玩轉前端,996遠離你'

})

}

}>點我顯示通知</Button>

配置全局屬性:

import { xNotification } from '@alex_xu/xui'

xNotification.config({

placement: 'topRight'

})

筆者已經將實現過的組件發布到npm上了,大家如果感興趣可以直接用npm安裝後使用,方式如下:

npm i @alex_xu/xui

// 導入xui

import {

Button,

Skeleton,

Empty,

Progress,

Tag,

Switch,

Drawer,

Badge,

Alert

} from '@alex_xu/xui'

該組件庫支持按需導入,我們只需要在項目裡配置babel-plugin-import即可,具體配置如下:

// .babelrc

"plugins": [

["import", { "libraryName": "@alex_xu/xui", "style": true }]

]

npm庫截圖如下:

最後

後續筆者將會繼續實現

table(表格),tooltip(工具提示條),Skeleton(骨架屏),Message(全局提示),form(form表單),switch(開關),日期/日曆,二維碼識別器組件等組件, 來復盤筆者多年的組件化之旅.

如果對於react/vue組件設計原理不熟悉的,可以參考我的之前寫的組件設計系列文章:

手摸手實現一個輕量級可擴展的模態框(Modal)組件配合React Portals實現一個功能強大的抽屜(Drawer)組件5分鐘實現一個Tag(標籤)組件和Empty(空狀態)組件用純css打造類materialUI的按鈕點擊動畫並封裝成react組件快速實現一個可定製的進度條組件基於jsoneditor二次封裝一個可實時預覽的json編輯器組件(react版)筆者已經將組件庫發布到npm上了, 大家可以通過npm安裝的方式體驗組件.

相關焦點

  • 精通react/vue組件設計之實現一個輕量級可擴展的模態框組件
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言本文是筆者寫組件設計的第九篇文章, 今天帶大家實現一個輕量級且可靈活配置組合的模態框(Modal)組件, 該組件在諸如Antd或者elementUI等第三方組件庫中都會出現,主要用來提供系統的用戶反饋.
  • 精通react/vue組件設計之配合React Portals實現一個(Drawer)組件
    通過組件的設計過程,大家會接觸到一個完成健壯的組件設計思路和方法,也能在實現組件的過程逐漸對react/vue的高級知識和技巧有更深的理解和掌握,並且在企業實際工作做遊刃有餘.作為數據驅動的領導者react/vue等MVVM框架的出現,幫我們減少了工作中大量的冗餘代碼, 一切皆組件的思想深得人心.
  • 《精通react/vue組件設計》之實現一個健壯的警告提示(Alert)組件
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言本文是筆者寫組件設計的第七篇文章, 今天帶大家實現一個自帶主題且可關閉的Alert組件, 該組件在諸如Antd或者elementUI等第三方組件庫中都會出現,主要用來提供系統的用戶反饋
  • 《精通react/vue組件設計》之快速實現一個可定製的進度條組件
    每一個組件只負責某一特定的表現或者功能)正文在開始組件設計之前希望大家對css3和js有一定的基礎.我們先看看實現後的組件效果:上圖可以知道封裝後的進度條組件通過對外暴露的接口(react/vue裡面可以看做
  • 精通react/vue組件設計之實現一個Tag(標籤)和Empty(空狀態)組件
    今天主要帶大家一起實現一個Tag組件和Empty(空狀態)組件,在介紹組件設計之前,先給大家介紹一個免費開源的圖標庫icomoon,可以在線導入SVG格式字體,並進行編輯,然後下載來使用,在組件設計中有具體的使用介紹.
  • 精通react/vue組件設計教你實現一個極具創意的加載(Loading)組件
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言本文是筆者寫組件設計的第八篇文章, 今天帶大家用5分鐘實現一個極具創意的加載(loading)組件.涉及的核心知識點主要是css3相關特性, 如果大家非常熟悉,可直接跳過介紹直接看正文
  • 快速在你的vue/react應用中實現ssr(服務端渲染)
    對於服務端渲染的頁面,服務端可以直接將帶數據的內容通過 HTML 文本的形式返回,搜尋引擎爬蟲可以輕易的獲取頁面內容,而對於客戶端渲染的應用,客戶端必須執行伺服器返回的 Javascript才能得到正確的網頁內容。
  • 手把手教你用JS/Vue/React實現幸運水果機(80後情懷之作)
    項目體驗地址免費視頻教程分別使用原生JS,Vue和React,手把手教你開發一個H5小遊戲,快速上手Vue和React框架的使用。項目截圖在線體驗在線體驗遊戲介紹幸運水果機是一款街機遊戲,遊戲界面由24個方格拼接成一個正方形,每個方格中都有一個不同的水果圖形,方格下都有一個小燈。
  • React系列(一) -邂逅React開發
    哪一個是你最想要學習的框架國內外很多知名網站使用React開發:原生案例實現為了演練React,我們可以提出一個小的需求:點擊下方的一個按鈕,點擊後文本改變為Hello React點擊下方的一個按鈕,點擊後文本改變為Hello React
  • 基於jsoneditor二次封裝一個可實時預覽的json編輯器組件react版
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言做為一名前端開發人員,掌握vue/react/angular等框架已經是必不可少的技能了,我們都知道,vue或react等MVVM框架提倡組件化開發
  • 基於Vue實現一個有點意思的拼拼樂小遊戲
    cd pinpinle && yarn start複製代碼關於vue-cli3配置實戰,可以移步 一張圖教你快速玩轉vue-cli3H5遊戲核心功能介紹目前筆者主要整理樂如下核心功能,接下來筆者會一一帶大家實現
  • React、Angular和Vue三種最流行的前端框架哪一個最好?
    因此,在深入比較之前,我們首先需要確定哪一個是必需的 - 一個庫或一個框架?實際上,庫被設計來執行一些特定的任務,而且通常並不複雜。因此,如果我們使用庫來構建我們的應用程式,那麼我們需要為每個任務選擇一個庫,以及設置任務運行者。庫的主要優點是我們可以完全控制應用程式。但問題是建立該項目需要更多的時間。另一方面,框架被設計用於執行更複雜的事情。
  • Vue、React 和 Angular:該選擇哪個框架?
    本文對三個最流行的 JavaScript 框架進行了全面的比較:Vue、React 和 Angular,如果你是正在開發或者目前正在考慮使用這些流行框架之一來啟動項目的開發人員,我們希望本文對你選擇正確的解決方案能有所幫助。
  • 尤雨溪介紹 Vue 3:語法不變、TS 支持很好、2.0 系列還會發一個版本
    首先,beta 階段意味著:已合併所有計劃內的 RFC已實現所有被合併的 RFCVue CLI 現在通過 vue-cli-plugin-vue-next 提供了實驗性支持此外還提供了一個用於 Vue 3 的最小化 webpack 配置,支持單文件組件其中有一個主要的 RFC 是關於新引入的
  • Vue.js布局
    (但假設有一個結帳流程,您不想顯示導航。或者您可能有帶側邊欄的產品頁面和沒有側邊欄的其他頁面等等)面對這種多樣性要求,我們要怎麼做來滿足業務需求的同時,保持代碼的可維護、易擴展呢?下面一起來探討。/layouts/LayoutDefault.vue';export default { name: 'Home', components: { LayoutDefault, },};</script>說明:Home.vue組件實現LayoutDefault
  • Vue.js 教程:構建一個特斯拉汽車餘電計算器
    作為本教程的起點,請克隆這個 Github 存儲庫:https://github.com/petereijgermans11/workshop-reactjs-vuejs然後轉至 vuejs-app 目錄。
  • 如何用純css打造類materialUI的按鈕點擊動畫並封裝成react組件
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言作為一個前端框架的重度使用者,在技術選型上也會非常注意其生態和完整性.筆者先後開發過基於vue,react,angular等框架的項目,碧如vue生態的elementUI, ant-design-vue, iView等成熟的UI框架
  • vue-devtools調試工具安裝與使用的簡單教程
    使用vue開發項目時,如果習慣vue當前項目一些操作後相關數據的變化的log,可以安裝一個vue-devtools調試工具,如何安裝呢?請看下邊:一:下載與安裝:1.下載好vue-devtools壓縮包(crx類型的壓縮包),直接解壓到你自己選擇的本地目錄中:2.打開谷歌瀏覽器,打開設置,並找到擴展程序:3.
  • Vue.js 很難學?看看這個由 DCloud 與 Vue 官方合作推出的免費入門...
    這對於微小項目可能沒什麼感覺,甚至還很方便,但對於中大型但項目,更適合工程化開發模式的 Vue/react ,只要學會了就真的不會再回去了。這套在線觀看的免費入門視頻教程就是給你們準備的。Vue.js 是目前國內很火的前端框架,前端工程師招聘幾乎都要求掌握。
  • 一個超級棒的VUE流程設計器
    大家好,我是為廣大程式設計師兄弟操碎了心的小編,每天推薦一個小工具/源碼,裝滿你的收藏夾,每天分享一個小技巧,讓你輕鬆節省開發效率,實現不加班不熬夜不掉頭髮,是我的目標!  今天小編推薦一款流程設計器easy-flow, easy-flow基於VUE+ElementUI+JsPlumb的流程設計器,通過 vuedraggable 插件來實現節點拖拽。