在浏览器页面中,通用的消息提示组件一般可以分为静态局部提示和动态全局提示,用于反馈用户需要关注的信息,使用频率较高。
虽然这两种提示从视觉交互上来看比较相似,但使用场景不同,组件对外暴露的接口也有一点区别,所以一般拆分成两个 UI 组件提供给业务方使用。
代码未动,设计先行,先考虑接口,再考虑实现。本文介绍如何设计这两个组件(展示、交互、API、实现思路),满足业务方的定制化和组件的通用化,提供简单易用、视觉交互友好的消息提示组件。
以下的设计思路整理总结自 People Design、 Ant Design 、Material-UI 的消息提示组件 ,主要针对 PC 端设计展示和交互,使用 React 语法 ;本文主要关注组件基础设计思路,不涉及:具体代码实现、无障碍设计 WAI-ARIA、主题切换、移动端适配等。
Alert & Toast ↓↓
Alert 警告提示
静态局部提示组件在 Antd 和 Material-UI 中命名为 Alert 警告提示
使用场景
页面局部展示一段简短且重要的信息,在不影响用户操作的同时能够吸引用户的注意力。
展示交互
消息框宽度默认水平撑满当前容器,以非浮层的静态展现形式,始终展现,不会自动消失,用户可以点击关闭,可以提供自定义的操作按钮。
标题和描述
Alert 提示可以指定标题,表示提示的类型或主题
长文本提示建议增加标题,用户可快速理解主要内容,内容建议不超过 4 行。
Material-UI Alert 带标题和内容
AntD Alert 带标题和内容
API 设计
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
message | 警告提示内容,一般是文本 | ReactNode | - |
title | 在内容上方展示的消息标题 | ReactNode | - |
|
|
提示类型
Alert 按照功能默认提供四种类型的提示,分别是:
- 普通提示 info:用于展示背景条件、政策信息、规范要求、当前状态等客观内容;
- 成功提示 success:用于展示已完成操作的成功状态;
- 警告提示 warning:用于展示可能会导致某种后果的警示性文本;
- 错误提示 error:用于展示当前操作或者整体状态有错误,提示用户修正或展示错误相关信息。
AntD Alert 四种提示类型
Material-UI Alert 四种提示类型
每种提示类型都提供对应的图标展示在消息开头,合适的图标让信息类型更加醒目,不带图标适合单调简单的提示。
API 设计
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
type | 提示类型,共有四种选择 success 、info 、warning 、error |
string | info |
icon | 辅助图标,可以指定 false 不展示图标,或者提供自定义图标 |
ReactNode | boolean | type 对应的 icon |
|
|
变体
提示框有描边(outlined)和填充(filled)这两种变体可以使用,以便匹配不同的设计风格。
标准(默认)是浅色填充无边框,填充(filled)是深色填充,描边(outlined)只有边框无填充。
Material-UI Alert 变体示例
API 设计
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
variant | 变体类型,共有三种选择 standard 、filled 、outlined |
string | standard |
|
|
可关闭
当用户接收到 Alert 提供的信息后,可能不希望再被 Alert 吸引注意力,影响对其它信息的处理;此时可以配置关闭按钮,允许用户主动关闭 Alert。
关闭后添加 leave 的过渡动效,平滑自然地收起 Alert。
AntD Alert 自定义关闭示例
API 设计
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
closeText | 自定义关闭按钮,替换默认的 x 图标 | ReactNode | - |
closable | 默认不可关闭 | boolean | false |
afterClose | 关闭后触发的回调函数 | () => void | - |
|
|
操作
提示中可以配置操作按钮,在尾部展示(关闭按钮之前)。文本消息不超过右侧操作按钮区域,间隔一定间距换行。
AntD Alert 操作示例
消息内容中也可以自定义文本链接,点击会跳转到其它页面。
People Design 常驻提示操作示例
API 设计
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
action | 自定义操作项 | ReactNode | - |
|
|
Toast 全局提示
有些组件库命名为 Message、Snackbar 等
全局提示是操作后的轻量级短时反馈提示,不会打断用户操作。
使用场景
用于展示操作后系统对该行为作出的反馈,如成功、警示、错误、提示信息等。
展示交互
位于页面顶部中央,距离顶部有固定间距。消息框宽度随内容自适应,超过最大宽度后文本换行。不随页面滚动,有浮层阴影效果。
展示一段时间后自动消息(可以设置不自动关闭),对内容干扰降到最低。
当不同操作触发了多条全局提示,按照时间先后顺序在页面上方中央依次堆叠出现,将未消失提示推至下方。
消息出现(从上至下掉落或从小放大)和消失(相反动作)都会有过渡。
当指针悬浮在 Toast 上时,认为用户此时正在关注此提示,所以清除 Toast 自动关闭的定时器,避免 Toast 突然在用户关注焦点中消失;当指针离开 Toast 后,重新创建 Toast 的关闭定时器。
和 Alert 的对比
- Alert 作为局部组件嵌入;Toast 页面最上层浮动展示,暗示全局性;
- Alert 跟随容器一起静态出现; Toast 在某个操作后动态浮现;
- Alert 默认常驻;Toast 出现后默认自动消失;
- Alert 在模板中声明式引用;Toast 在全局 JavaScript 作用域内命令式静态方法调用,可以看作是工具方法,在事件中触发;
- Alert 每个实例独立,配置不共享;Toast 实例间共享全局配置,可以统一管理,配置默认的样式和行为;
- Alert 提供 action prop;Toast 为轻交互,不提供额外操作,但可以在 content 中自行嵌入(基本约束,灵活定制);
- Alert 提供 title prop;Toast 为轻提示,一般不需要标题,但同样可以在 content 中自行嵌入。
提示类型
Toast 除了 Alert 的四种提示类型外,还有一种 loading 类型:
- 加载中 loading:用于展示当前操作正在处理中或者内容正在加载中
AntD message 示例
变体
同 Alert
静态方法 API
API 基本参照 AntD message
Toast 作为特殊的工具组件,提供每个提示类型的静态方法调用:
Toast.success(content, [duration], onClose)
Toast.error(content, [duration], onClose)
Toast.info(content, [duration], onClose)
Toast.warning(content, [duration], onClose)
Toast.loading(content, [duration], onClose)
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
content | 提示内容 | ReactNode | config | - |
duration | 自动关闭的延时,单位秒。设为 0 时不自动关闭 | number | 3 |
onClose | 关闭时触发的回调函数 | function | - |
返回 promise
静态方法调用后返回 promise,可以使用 .then()
、 await
语法,以同步的写法在之后执行 Toast 关闭后的逻辑:
|
|
|
|
实现思路
|
|
配置对象传参
除了上面的简略参数外,还可以使用对象的形式传递参数,用来定制更具体的 Toast:
Toast.success(config)
Toast.error(config)
Toast.info(config)
Toast.warning(config)
Toast.loading(config)
config
对象属性如下:
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
className | 自定义 CSS class | string | - |
content | 提示内容 | ReactNode | - |
duration | 自动关闭的延时,单位秒。设为 0 时不自动关闭,会展示关闭图标,也可以通过 Toast.destroy(key) 关闭 |
number | 3 |
icon | 自定义图标 | ReactNode | - |
key | 当前提示的唯一标志 | string | number | - |
style | 自定义内联样式 | CSSProperties | - |
onClose | 关闭时触发的回调函数 | function | - |
全局配置
提供 Toast.config(options)
配置 Toast 的默认样式和行为,在引入 Toast 的入口文件中配置。options
属性如下:
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
duration | 默认自动关闭延时,单位秒 | number | 3 |
getContainer | 配置渲染节点的输出位置 | () => HTMLElement | () => document.body |
maxCount | 最大显示数, 超过限制时,最早的消息会被自动关闭 | number | - |
top | 消息距离顶部的位置 | number | 24 |
关闭提示
Toast.destroy()
关闭当前所有的 Toast;Toast.destroy(key)
如果在config
对象中配置了一个 Toast 的唯一key
,可以传递key
参数关闭指定的 Toast。
通过 key 更新 Toast
可以通过唯一的 key
来动态更新 Toast 内容
|
|
通过 Hooks 获取上下文
当需要 context 信息(例如父级组件向下传递的共享状态)时,可以通过 Toast.useToast()
创建支持读取 context 的 contextHolder
,方法会返回 api
实体以及 contextHolder
节点。将其插入到需要获取 context 的位置即可:
|
|
实现思路
直接调用 Toast 方法,会动态创建新的 Toast 实例,默认渲染在 body 底部的容器中,此时在 Toast 中访问的是容器的 context。
要在 Toast content 内访问父级组件的 context,需要把 content 挂载到 <Context.Provider>
的 React 子树中,这样 content 中的 <Context.Consumer>
才能接收到绑定的 value
。但是如果直接把 Toast 渲染到父级组件下,即使是 fixed
定位,多少也会受到父组件的干扰(样式、生命周期等),所以为了避免父组件的影响,同时自动应用过渡动画,需要在一个专用的 Toast 容器环境中让 Toast 和普通调用一样渲染,即默认的 body 底部容器。
跨组件树异地渲染正是 React.createPortal()
的用武之地。
通过 Toast.useToast()
返回的 toast
api,在事件中触发提示后,会动态创建 Toast 实例(作为 contextHolder 插入到父组件子树),Toast render 中判断是 hook 触发则利用 React.createPortal(<ToastContent />, bodyContainer)
把 ToastContent 渲染到默认容器中,这样 ToastContent 即成为了 <Context.Provider>
子树中的组件,实现访问父级 context,又能在默认容器中正常渲染。
使用代码简略说明:
|
|
总结
通过设计 Alert 和 Toast 的展示交互和 API,能够从普通用户、组件使用方、组件提供方的角度考虑如何做好一个通用组件;不同的组件库有不同的风格,但沉淀出来的优秀实践都大同小异。
有了设计方案后才考虑怎么优雅地实现这些功能,关联组件库、解决兼容问题、处理各种细节等,逐步打磨成熟。