- 前端
- UI组件
- 设计系统
【免费下载链接】semi-design
🚀A modern, comprehensive, flexible design system and React UI library, AI-friendly built-in.🎨Provide 3000+ Design Tokens, easy to build your design system. Make Semi Design to Any Design.🧑🏻💻 Design to Code in one click
Toast 是 Semi Design(@douyinfe/semi-ui)反馈类组件中用于对用户操作给出及时反馈的轻量级提示:它由用户操作触发,反馈信息可以是操作的结果状态(成功、失败、出错、警告等)。本篇以官方文档 content/feedback/toast/index.md 为主体骨架,结合仓库内 packages/semi-ui/toast 与 packages/semi-foundation/toast 的源码实现,系统讲解 Toast 的引入方式、全部静态方法、Options/Config 参数、堆叠模式、Hook 消费 Context、自定义配置工厂,以及 ARIA 无障碍与文案规范,帮助你从“会调用”进阶到“懂原理”。
如何引入
Toast 采用命令式(imperative)调用方式,无需在 JSX 中挂载组件,直接从组件库导入即可:
import { Toast } from '@douyinfe/semi-ui';文档中所有演示代码均可直接运行:点击按钮后,Toast 会在屏幕顶部(默认位置)以浮动层形式出现,并在duration(默认 3 秒)后自动关闭。
普通提示:静态方法的基础用法
调用Toast的静态方法即可弹出提示。最基本的用法是传入一个字符串:
Toast.info('Hi, Bytedance dance dance');也可以传入一个options对象,配置内容、展示时长、堆叠行为等:
import React from 'react'; import { throttle } from 'lodash-es'; import { Toast, Button } from '@douyinfe/semi-ui'; function Demo() { const opts = { content: 'Hi, Bytedance dance dance', duration: 3, stack: true, }; const handleClose = () => { throttled.cancel(); }; const throttleOpts = { content: 'Hi, Bytedance dance dance', duration: 10, onClose: handleClose, stack: true, }; const throttled = throttle(() => Toast.info(throttleOpts), 10000, { trailing: false }); return ( <div> <Button onClick={() => Toast.info(opts)}>Display Toast</Button> <br /> <br /> <Button onClick={throttled}>Throttled Toast</Button> </div> ); }推荐使用 stack 堆叠模式
文档特别建议:推荐设置stack属性应用堆叠样式到同屏多个 Toast,Hover 可展开,这能有效防止一次性弹出多个并列 Toast 对用户造成干扰。该 API 在v2.42.0之后支持。
从源码 packages/semi-ui/toast/toast.tsx 可以看到堆叠模式的实现:当stack为true时,每个 Toast 外层会包裹一个semi-toast-zero-height-wrapper,其高度在未展开时为 0,通过 3D 变换transform: translate3d(0, 0, ${reservedIndex * -10}px)(见 toast.tsx)按索引在 Z 轴方向层叠;鼠标悬停(onMouseEnter)时调用clearCloseTimer暂停关闭计时,并展开堆叠区域展示全部内容。对应的层叠过渡动画定义在样式文件 packages/semi-foundation/toast/toast.scss 中,使用transition: all $animation_duration-toast-stack $animation_function-toast-stack完成展开/收起。
其他提示类型:Success / Warning / Error
Toast 共提供四种操作结果反馈类型,对应四个静态方法。下面的例子演示了成功、警告、错误三种提示(Toast.success可直接传入字符串):
import React from 'react'; import { Toast, Button } from '@douyinfe/semi-ui'; function Demo() { let opts = { content: 'Hi, Bytedance dance dance', duration: 3, }; return ( <> <Button style={{ color: `var(--semi-color-success)` }} onClick={() => Toast.success('Hi,Bytedance dance dance')}>Success</Button> <br /> <br /> <Button type="warning" onClick={() => Toast.warning(opts)}> Warning </Button> <br /> <br /> <Button type="danger" onClick={() => Toast.error(opts)}> Error </Button> </> ); }四种类型的图标映射在 packages/semi-ui/toast/toast.tsx 的renderIcon()中定义:warning对应IconAlertTriangle、success对应IconTickCircle、info对应IconInfoCircle、error对应IconAlertCircle。如果你传入自定义icon,Semi 图标会被自动放大为large尺寸,非 Semi 图标则原样渲染。
多色样式:theme 浅色填充
默认情况下 Toast 使用白色卡片(normal模式)。通过theme: 'light'可以启用浅色填充样式,为不同类型赋予语义背景色,提高与界面的对比度:
import React from 'react'; import { Toast, Button } from '@douyinfe/semi-ui'; function Demo() { let opts = { content: 'Hi, Bytedance dance dance', duration: 3, theme: 'light', }; return ( <> <Button onClick={() => Toast.info(opts)}>Info</Button> <br /> <br /> <Button style={{ color: `var(--semi-color-success)` }} onClick={() => Toast.success(opts)}>Success</Button> <br /> <br /> <Button type="warning" onClick={() => Toast.warning(opts)}> Warning </Button> <br /> <br /> <Button type="danger" onClick={() => Toast.error(opts)}> Error </Button> </> ); }theme的可选值在 packages/semi-foundation/toast/constants.ts 中定义为['normal', 'light'],默认值为normal(v2.54.0起支持)。从源码看,light主题通过 toast.scss 中的&-light样式为每种类型设置了语义背景色、边框与图标颜色,例如成功态使用$color-toast_success_light-bg背景与$color-toast_success_light-border边框。
链接文本:配合 Typography 自定义内容
Toast 的content接受任意ReactNode,因此可以配合Typography组件(如Text link)渲染带链接的文本,适用于“查看详情”“稍后处理”等复合操作场景:
import React from 'react'; import { Toast, Typography, Button } from '@douyinfe/semi-ui'; function Demo() { const { Text } = Typography; let opts = { content: ( <span> <Text>Hi, Bytedance dance dance</Text> <Text link style={{ marginLeft: 12 }}> 更多 </Text> </span> ), duration: 3, }; let multiLineOpts = { content: ( <> <div>Hi, Bytedance dance dance</div> <div style={{ marginTop: 8 }}> <Text link>查看详情</Text> <Text link style={{ marginLeft: 20 }}> 一会再看 </Text> </div> </> ), duration: 3, }; return ( <> <Button onClick={() => Toast.info(opts)}>Display Toast</Button> <br /> <br /> <Button onClick={() => Toast.info(multiLineOpts)}>Display Multi-line Toast</Button> </> ); }在渲染层面,content会被放入semi-toast-content-text容器(见 toast.tsx),该容器设置了textMaxWidth对应的maxWidth以及word-wrap: break-word自动换行(见 toast.scss),因此多行、长文本均能良好展示。
修改延时:duration 自动关闭
duration控制 Toast 自动关闭的延时,单位为秒,默认值为 3。下面的例子将其设置为 10 秒:
import React from 'react'; import { Toast, Button } from '@douyinfe/semi-ui'; function Demo() { let opts = { content: 'Hi, Bytedance dance dance', duration: 10, }; return <Button onClick={() => Toast.info(opts)}>Close After 10s</Button>; }底层计时逻辑位于 packages/semi-foundation/toast/toastFoundation.ts:startCloseTimer_()读取duration,当其为合法数字时通过setTimeout(..., duration * 1000)触发关闭。默认值3定义在 constants.ts 的numbers.duration中。值得注意的细节是:Toast 内容区域(.semi-toast-content)设置了pointer-events: all(toast.scss),并且 Toast 本身监听了onMouseEnter/onMouseLeave(toast.tsx),鼠标悬停时会暂停关闭计时、移出后恢复,避免用户还没读完内容就消失。
手动关闭:duration 为 0 与 Toast.close(toastId)
当duration设置为0时,Toast 不会自动关闭,此时必须通过手动方式关闭。每次调用Toast.info()等静态方法会返回一个toastId,用它即可精确关闭对应 Toast:
import React, { useState } from 'react'; import { Toast, Button } from '@douyinfe/semi-ui'; function Demo() { const [toastId, setToastId] = useState(); function show() { if (toastId) { return; } let id = Toast.info(opts); setToastId(id); } function hide() { Toast.close(toastId); destroy(); } function destroy() { setToastId(null); } let opts = { content: 'Not auto close', duration: 0, onClose: destroy, }; return ( <> <Button type="primary" onClick={show}> Show Toast </Button> <br /> <br /> <Button type="primary" onClick={hide}> Hide Toast </Button> </> ); }这里演示了“显示后记住toastId→ 点击按钮通过Toast.close(toastId)关闭”的完整闭环,同时通过onClose回调把本地 state 复位,防止重复弹出。源码层面,Toast.close(id)最终调用ToastListFoundation.removeToast(id)(见 packages/semi-foundation/toast/toastListFoundation.ts):它先把目标 Toast 从list中摘除并放入removedItems,随后由 React 侧的CSSAnimation播放离场动画,动画结束后才真正卸载节点并清理计时器(见 packages/semi-ui/toast/index.tsx),保证关闭过程平滑且无内存残留。
更新消息内容:通过唯一 id
当你需要改变一条已存在 Toast 的内容时(例如从“上传中”变为“上传成功”),无需关闭重建,直接传入相同的id即可触发更新:
import React, { useState } from 'react'; import { Toast, Button } from '@douyinfe/semi-ui'; function Demo() { function show() { const id = 'toastid'; Toast.info({ content: 'Update Content By Id', id }); setTimeout(() => { Toast.success({ content: 'Id By Content Update', id }); }, 1000); } return ( <Button type="primary" onClick={show}> Update Content By Id </Button> ); }更新流程在 packages/semi-ui/toast/index.tsx 中实现:静态create方法会先通过ToastList.ref.has(id)判断该 id 是否已存在,若存在则走update(id, opts)分支,调用ToastListFoundation.updateToast(toastListFoundation.ts)以浅合并方式替换该条 Toast 的配置,并记录到updatedItems;渲染阶段通过 ref 回调检测到更新项后会调用restartCloseTimer()重新计时(index.tsx),避免内容刚更新就被旧计时器关闭。
销毁所有:Toast.destroyAll()
需要一次性清空屏幕上所有 Toast 时,使用全局销毁方法:
Toast.destroyAll()从源码看,destroyAll()会先调用ToastListFoundation.destroyAll()把所有 Toast 移入removedItems播放离场动画,随后reactUnmount(wrapper)卸载整个 ToastList 实例并移除挂载容器、重置静态引用(packages/semi-ui/toast/index.tsx)。
消费 Context:Toast.useToast()
命令式 API 的 Toast 默认渲染在document.body下,无法读取组件树中的 Context。若希望 Toast 内容能消费到业务 Context,可以使用Toast.useToast()创建contextHolder,将其插入组件树的任意位置;此时通过 hooks 创建的 Toast 会渲染在contextHolder所在节点处,并拿到该位置的全部上下文:
import React from 'react'; import { Toast, Button } from '@douyinfe/semi-ui'; const ReachableContext = React.createContext(); function Demo(props = {}) { const [toast, contextHolder] = Toast.useToast(); const config = { duration: 0, title: 'This is a success message', content: <ReachableContext.Consumer>{name => `ReachableContext: ${name}`}</ReachableContext.Consumer>, }; return ( <ReachableContext.Provider value="Light"> <div> <Button onClick={() => { toast.success(config); }} > Hook Toast </Button> </div> {contextHolder} </ReachableContext.Provider> ); }hook 返回的 toast 对象拥有以下方法:info、success、warning、error、close。其实现位于 packages/semi-ui/toast/useToast/index.tsx:useToast内部通过usePatchElement维护一个 elements 数组,addToast为每次调用生成semi_toast_前缀的 uuid,并渲染一个 HookToast 到 contextHolder 中;close(id)则通过 ref Map 找到对应实例执行关闭。注意:由于 toast 渲染位置变化,hook 模式的 Toast 与命令式 Toast 在 DOM 挂载节点、以及能否读取 Context 上存在差异,需要根据场景二选一。
创建不同配置 Toast:ToastFactory.create(config)
如果应用的不同区域需要差异化的 Toast 默认配置(比如把 Toast 渲染到指定容器),可以使用ToastFactory.create(config)创建新的 Toast 实例(>= 1.23版本支持):
import React from 'react'; import { Button, ToastFactory } from '@douyinfe/semi-ui'; function Demo() { const ToastInCustomContainer = ToastFactory.create({ getPopupContainer: () => document.getElementById('custom-toast-container'), }); return ( <div> <Button onClick={() => Toast.info('Toast')}>Default Toast</Button> <br /> <br /> <Button onClick={() => ToastInCustomContainer.info('Toast in some container')}> Toast in custom container </Button> <div id="custom-toast-container">custom container</div> </div> ); }ToastFactory.create(config)的实现见 packages/semi-ui/toast/index.tsx:它调用createBaseToast()生成一个全新的 ToastList 类,并对其调用config(config)应用独立配置,因此两个实例互不干扰——Toast与ToastInCustomContainer拥有各自的默认配置、挂载容器与内部状态。文档中提示该模式常用于覆盖全局配置。
API 参考:静态方法总览
Toast 组件提供的静态方法,使用方式与参数如下。展示时可直接传入options对象或string:
全局配置(在调用前提前配置,全局一次生效)
Toast.config(config)
直接展示 Toast
Toast.info(options || string)Toast.error(options || string)Toast.warning(options || string)Toast.success(options || string)
info、error、warning、success的返回值均为toastId,可用于手动关闭:
const toastId = Toast.info({ /*...options*/ }); Toast.close(toastId); // 手动关闭config方法对全局默认值的写入逻辑在 packages/semi-ui/toast/index.tsx:top/left/bottom/right直接写入defaultOpts,theme需命中strings.themes白名单,zIndex、duration需为数字,getPopupContainer需为函数。此外四个展示方法在内部都会合并全局覆盖配置semiGlobal.config.overrideDefaultProps.Toast(优先级为opts > 全局配置 > defaultOpts,见 index.tsx),这也是ToastFactory.create之外的另一种全局定制入口。
Options 参数说明
Toast Options 支持以下 API,同时也支持 Config 中的全部 API:
| 属性 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| content | 提示内容 | ReactNode | '' | - |
| icon | 自定义图标 | ReactNode | - | - |
| showClose | 是否展示关闭按钮 | boolean | true | - |
| textMaxWidth | 内容的最大宽度 | number | string | 450 | - |
| onClose | toast 关闭的回调函数 | () => void | - | - |
| stack | 是否堆叠 Toast | boolean | false | 2.42.0 |
| id | 自定义 ToastId | number | - | - |
这些默认值在 packages/semi-ui/toast/toast.tsx 的defaultProps中均有对应定义:showClose默认为true、textMaxWidth默认为450、stack默认为false、theme默认为'normal'。showClose为true时会在内容右侧渲染一个borderless主题、small尺寸的关闭按钮(toast.tsx),点击后调用foundation.close(e)关闭并停止事件冒泡。
Config 全局配置参数说明
以下 API 支持全局配置,用于更改当前 Toast 的默认配置:
| 属性 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| bottom | 弹出位置 bottom | number | string | - | - |
| left | 弹出位置 left | number | string | - | - |
| right | 弹出位置 right | number | string | - | - |
| top | 弹出位置 top | number | string | - | - |
| zIndex | 弹层 z-index 值 | number | 1010 | - |
| theme | 填充样式,支持light、normal | string | normal | 2.54.0 |
| duration | 自动关闭的延时,单位 s,设为 0 时不自动关闭 | number | 3 | - |
| getPopupContainer | 指定父级 DOM,弹层将会渲染至该 DOM 中。自定义时需要设置 container 和内部的.semi-toast-wrapper为position: relative。这会改变浮层 DOM 树位置,但不会改变视图渲染位置。 | () => HTMLElement | null | () => document.body | - |
位置与 z-index 的应用逻辑位于 packages/semi-ui/toast/index.tsx:首次创建 Toast 时,系统会动态生成一个semi-toast-wrapper容器挂载到document.body(或getPopupContainer指定的节点),将top/left/bottom/right写入容器的内联样式(数字类型自动拼接px),并把zIndex作为容器的层级。容器本身是position: fixed的(见 toast.scss),默认位于屏幕顶部中央。
Accessibility 无障碍
ARIA
- Toast 的
role为alert。
实现上,Toast 根元素渲染了role="alert"以及aria-label="${type} type"(packages/semi-ui/toast/toast.tsx),使屏幕阅读器能够及时播报操作结果,符合反馈类组件的无障碍要求。
文案规范
Toast 作为高频出现的轻量反馈,文案质量直接影响产品体验。Semi 官方给出以下规范:
- 保持简洁
- 句尾不使用句号
- 使用「名词 + 动词」的格式进行说明
| ✅ 推荐用法 | ❌ 不推荐用法 |
|---|---|
| Language added | New language has been added successfully |
| Ticket transfer failed | Can't transfer ticket |
- 提供动作的提示消息
- 只提供一个动作
- 不使用类似于「已读」类的动作,例如 OK、Got it、Dismiss、Cancel
| ✅ 推荐用法 | ❌ 不推荐用法 |
|---|---|
| Ticket transfer failed +Retry(重试动作) | Ticket transfer failed +Dismiss(已读类动作) |
设计变量
Toast 组件的全部设计变量(Design Tokens)可在文档页下方通过<DesignToken/>查看,涵盖背景色、边框色、图标色、圆角、间距、动画时长与缓动函数等(对应样式变量定义见 packages/semi-foundation/toast/variables.scss 与 animation.scss),支持通过 Semi 的主题定制能力统一调整。这些变量同时服务于normal与light两种主题,是自定义品牌外观的入口。
小结
Toast 是 Semi Design 中最常用的轻量反馈组件之一,其核心要点可归纳为:
- 命令式 API:
Toast.info/success/warning/error四类静态方法,参数支持字符串或 options 对象,返回toastId供Toast.close手动关闭; - 全局配置:
Toast.config()一次性修改默认行为(位置、zIndex、theme、duration、挂载容器),ToastFactory.create()可创建互相独立的配置实例; - 进阶能力:
stack堆叠(v2.42.0+)、theme: 'light'彩色样式(v2.54.0+)、按id更新内容、useToast消费 Context、destroyAll全量销毁; - 工程细节:默认 3 秒自动关闭、鼠标悬停暂停计时、
role="alert"无障碍支持,以及源码中 Foundation/组件双层架构与removedItems + CSSAnimation的平滑离场机制。
掌握这些 API 与底层行为后,你可以在任何 React 应用中快速构建专业、可访问、体验统一的 Toast 反馈体系。
- 前端
- UI组件
- 设计系统
【免费下载链接】semi-design
🚀A modern, comprehensive, flexible design system and React UI library, AI-friendly built-in.🎨Provide 3000+ Design Tokens, easy to build your design system. Make Semi Design to Any Design.🧑🏻💻 Design to Code in one click
相关推荐
Semi Design Toast 轻提示组件完全指南:静态方法、Hook 与源码实现解析
Semi Design Toast 轻提示组件完全指南:静态方法、Hook 与源码实现解析 Toast 是 Semi Design 反馈类(Feedback)组
前端UI组件设计系统Semi Design Notification 通知组件完全指南:静态方法、Hooks 用法与源码级原理剖析
Semi Design Notification 通知组件完全指南:静态方法、Hooks 用法与源码级原理剖析 Notification 是 Semi Desi
前端UI组件设计系统Ant Design Message 组件完全指南:全局消息提示的静态方法、Hooks 用法与源码级原理剖析
Ant Design Message 组件完全指南:全局消息提示的静态方法、Hooks 用法与源码级原理剖析 Message 是 Ant Design 中面向全
前端UI组件设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考