API

WARNING

dumi 提供的所有 API 均依赖 dumi 框架提供的编译时及运行时环境,所以这些 API 只能用于主题包开发或本地主题定制,请勿在项目源码(例如组件)中使用这些 API,将会导致项目发布后功能异常!

dumi 2 基于 Umi 4,除了自身特有的 API 以外,同样也支持 Umi 4 提供的基础 API,两者均从 dumi 包名中引入。

import { useLocale, useAppData } from 'dumi';
// 其他逻辑

重点配置项

openCodeSandbox

  • 作用:在 CodeSandbox 中打开传入的 demo
  • 场景:自定义 demo 预览器按钮 PreviewerActions 时可能需要用到
  • 用法:

假设在项目中创建 .dumi/theme/slots/PreviewerActions.tsx 用于覆盖默认的预览器按钮:

import { openCodeSandbox } from 'dumi';
import DumiPreviewerActions from 'dumi/theme-default/slots/PreviewerActions';
import React from 'react';
const PreviewerActions: typeof DumiPreviewerActions = (props) => (
<button type="button" onClick={() => openCodeSandbox(props)}>
点我在 CodeSandbox 里打开 demo
</button>
);

useAtomAssets

  • 作用:获取所有的原子资产元数据
  • 场景:定制 API 表格、创建资产索引页等场景可能需要用到
  • 用法:
import { useAtomAssets } from 'dumi';
const Example = () => {
const assets = useAtomAssets();
// 返回值:Record<string, AtomComponentAsset>
// 类型定义:https://github.com/umijs/dumi/tree/master/assets-types/typings/atom/index.d.ts#L37
// 其他逻辑
};

useFullSidebarData

  • 作用:获取所有路径下的侧边栏数据,如果希望获取当前路径的侧边栏数据,请使用 useSidebarData API
  • 场景:定制侧边栏时可能需要用到
  • 用法:
import { useFullSidebarData } from 'dumi';
const Example = () => {
const sidebar = useFullSidebarData();
// 返回值:Record<string, ISidebarGroup[]>
// 类型定义:https://github.com/umijs/dumi/blob/master/src/client/theme-api/types.ts#L171
// 其他逻辑
};

useLocale

  • 作用:获取当前的国际化语言数据
  • 场景:实现与国际化相关的功能时可能需要用到
  • 用法:
import { useLocale } from 'dumi';
const Example = () => {
const locale = useLocale();
// 返回值:{ id: string; name: string; base: string } | { id: string; name: string; suffix: string }
// 类型定义:https://github.com/umijs/dumi/blob/master/src/client/theme-api/types.ts#L152
// 其他逻辑
};

useNavData

  • 作用:获取导航栏数据
  • 场景:定制导航栏时需要用到
  • 用法:

INFO

如果你是 dumi v2.2 发布之前的主题包开发者,建议更新导航栏组件对二级导航数据的支持,并将主题包 peerDependencies 中的 dumi 版本设置为 ^2.2.0,以便主题包用户使用约定式二级导航特性

import { useNavData } from 'dumi';
const Example = () => {
const nav = useNavData();
// 返回值:INavItem[]
// 类型定义:https://github.com/umijs/dumi/blob/master/src/client/theme-api/types.ts#L157
// 其他逻辑
};

usePrefersColor

  • 作用:获取/设置站点的主题色
  • 场景:实现主题切换或全局控制组件 demo 主题色时可能需要用到
  • 用法:

例如通过 GlobalLayout 对组件 demo 的主题色进行全局控制,此处假定组件库已经实现了类似 antd 的 ConfigProvider 来控制全局主题色:

// .dumi/theme/layouts/GlobalLayout.tsx
import ConfigProvider from '@/config-provider';
import { useOutlet, usePrefersColor } from 'dumi';
const GlobalLayout: React.FC = ({ children }) => {
const outlet = useOutlet();
// color 为当前应用的主题色,dark or light
const [color] = usePrefersColor();
return <ConfigProvider theme={color}>{outlet}</ConfigProvider>;
};

在制作主题包时也可以通过该 API 实现切换主题色的按钮,用法如下:

import { usePrefersColor } from 'dumi';
const Example = () => {
const [
// 当前生效的主题色,dark or light
color,
// 当前的偏好主题色,dark or light or auto
prefersColor,
// 设置偏好主题色,如果设置为 auto,则 color 的值会根据系统设置自动改变
setPrefersColor,
] = usePrefersColor();
// 可参考 dumi 默认主题的主题切换按钮实现:https://github.com/umijs/dumi/tree/master/src/client/theme-default/slots/ColorSwitch/index.tsx
// 其他逻辑
};

useRouteMeta

  • 作用:获取当前路由的元数据
  • 场景:定制页面功能(例如渲染 toc、定制 Tabs 实现等)时可能需要用到
  • 用法:
import { useRouteMeta } from 'dumi';
const Example = () => {
const {
// Markdown/React 的 frontmatter
frontmatter,
// 页面标题数据
toc,
// 页面文本数据
texts,
// 页面 Tab 数据
tabs,
} = useRouteMeta();
// 返回值:IRouteMeta
// 类型定义:https://github.com/umijs/dumi/blob/master/src/client/theme-api/types.ts#L56
// 其他逻辑
};

useSiteData

  • 作用:获取站点配置数据
  • 场景:定制全局功能(例如获取用户的主题配置、获取全局的 demo 数据、定制页面编辑链接)时可能需要用到
  • 用法:
import { useSiteData } from 'dumi';
const Example = () => {
const {
// 项目的 package.json 数据
pkg,
// 项目全量的 demo 数据
demos,
// 项目全量的组件数据(如果只需要单独获取这一份数据,请使用 useAtomAssets)
components,
// 项目的 locales 配置
locales,
// 用户从 .dumirc.ts 传入的 themeConfig
themeConfig,
// 当前页面的加载状态,由于默认启用路由按需加载,所以切换路由时会有 loading 的过程
loading,
} = useSiteData();
// 返回值:ISiteContext
// 类型定义:https://github.com/umijs/dumi/tree/master/src/client/theme-api/context.ts#L6
// 其他逻辑
};

useSidebarData

  • 作用:获取当前路径下的侧边栏数据
  • 场景:定制侧边栏时可能需要用到
  • 用法:
import { useSidebarData } from 'dumi';
const Example = () => {
const sidebar = useSidebarData();
// 返回值:ISidebarGroup[]
// 类型定义:https://github.com/umijs/dumi/blob/master/src/client/theme-api/types.ts#L171
// 其他逻辑
};

useSiteSearch

  • 作用:获取全文搜索的数据及方法
  • 场景:定制搜索框时可能需要用到
  • 用法:
import { useSiteSearch } from 'dumi';
const Example = () => {
const {
// 当前关键词(用于创建受控输入框)
keywords,
// 设置搜索关键词
setKeywords,
// 搜索结果
// 类型定义:https://github.com/umijs/dumi/tree/master/src/client/theme-api/useSiteSearch.ts#L25
result,
// 是否在搜索中
loading,
} = useSiteSearch();
// 其他逻辑
};

useTabMeta

  • 作用:获取当前 Tab 的元数据(仅页面存在 Tab 时且切换到某个 Tab 时才有返回值)
  • 场景:定制 Tab 实现的时候可能需要用到
  • 用法:
import { useTabMeta } from 'dumi';
const Example = () => {
const {
// Markdown/React 的 frontmatter
frontmatter,
// 页面标题数据
toc,
// 页面文本数据
texts,
} = useTabMeta();
// 返回值:IRouteTabMeta
// 类型定义:https://github.com/umijs/dumi/blob/master/src/client/theme-api/types.ts#L135
// 其他逻辑
};

基础配置项

createBrowserHistory

创建使用浏览器内置 history 来跟踪应用的 BrowserHistory。推荐在支持 HTML5 history 接口的 现代 Web 浏览器中使用。

类型定义如下:

function createBrowserHistory(options?: { window?: Window }) => BrowserHistory;

使用范例:

// create a BrowserHistory
import { createBrowserHistory } from 'dumi';
const history = createBrowserHistory();
// or a iframe BrowserHistory
import { createBrowserHistory } from 'dumi';
const history = createBrowserHistory({
window: iframe.contentWindow,
});

createHashHistory

createHashHistory 返回一个 HashHistory 实例。window 默认为当前 documentdefaultView

HashHistoryBrowserHistory 的主要区别在于,HashHistory 将当前位置存储在 URL 的哈希部分中,这意味着它在路由切换时不会发送请求到服务器。如果您将站点托管在您无法完全控制服务器上,或者在只提供同单页面的 Electron 应用程序中,推荐使用 HashHistory

使用范例:

// create a HashHistory
import { createHashHistory } from 'dumi';
const history = createHashHistory();

createMemoryHistory

MemoryHistory 不会在地址栏被操作或读取。它也非常适合测试和其他的渲染环境。

const history = createMemoryHistory(location)

createSearchParams

包装 new URLSearchParams(init) 的工具函数,支持使用数组和对象创建

import { createSearchParams } from 'dumi';
// 假设路径 http://a.com?foo=1&bar=2
createSearchParams(location.search);
createSearchParams("foo=1&bar=2");
createSearchParams("?foo=1&bar=2");
// 键值对对象
createSearchParams({ foo: 'bar', qux: 'qoo'}).toString()
// foo=bar&qux=qoo
// 键值元组数组
createSearchParams([["foo", "1"], ["bar", "2"]]).toString()
// foo=1&bar=2

URLSearchParams 文档

generatePath

使用给定的带参数的 path 和对应的 params 生成实际要访问的路由。

import { generatePath } from 'dumi';
generatePath("/users/:id", { id: "42" }); // "/users/42"
generatePath("/files/:type/*", {
type: "img",
"*": "cat.jpg",
}); // "/files/img/cat.jpg"

Helmet

react-helmet-async 提供的 Helmet 组件,用于在页面中动态配置 head 中的标签,例如 title

注意:为了确保 SSR 时 Helmet 仍能正常工作,请务必使用 dumi 提供的 Helmet 而不是单独安装 react-helmet

import { Helmet } from 'dumi';
export default function Page() {
return (
<Helmet>
<title>Hello World</title>
</Helmet>
);
}

history

和 history 相关的操作,用于获取当前路由信息、执行路由跳转、监听路由变更。

// 建议组件或 hooks 里用 useLocation 取
import { useLocation } from 'dumi';
export default function Page() {
let location = useLocation();
return (
<div>
{ location.pathname }
{ location.search }
{ location.hash }
</div>
);
}

如果在 React 组件和 Hooks 之外获取当前路由信息。

// location 对象,包含 pathname、search 和 hash
window.location.pathname;
window.location.search;
window.location.hash;

命令式路由跳转。

import { history } from 'dumi';
// 跳转到指定路由
history.push('/list');
// 带参数跳转到指定路由
history.push('/list?a=b&c=d#anchor', state);
history.push({
pathname: '/list',
search: '?a=b&c=d',
hash: 'anchor',
},
{
some: 'state-data',
}
);
// 跳转当前路径,并刷新 state
history.push({}, state)
// 跳转到上一个路由
history.back();
history.go(-1);

🚨

注意:history.push 和 history.replace 需要使用 state 需将 state 作为这两个 API 的第二个参数传递

路由监听。

import { history } from 'dumi';
const unlisten = history.listen(({ location, action }) => {
console.log(location.pathname);
});
unlisten();

<Link> 是 React 组件,是带路由跳转功能的 <a> 元素。

类型定义如下:

declare function Link(props: {
prefetch?: boolean;
to: string | Partial<{ pathname: string; search: string; hash: string }>;
replace?: boolean;
state?: any;
reloadDocument?: boolean;
}): React.ReactElement;

示例:

import { Link } from 'dumi';
function IndexPage({ user }) {
return <Link to={user.id}>{user.name}</Link>;
}

<Link to> 支持相对路径跳转;<Link reloadDocument> 不做路由跳转,等同于 <a href> 的跳转行为。

若开启了 prefetch 则当用户将鼠标放到该组件上方时,Umi 就会自动开始进行跳转路由的组件 js 文件和数据预加载。

matchPath

matchPath 可以将给定的路径以及一个已知的路由格式进行匹配,并且返回匹配结果。

类型定义如下:

declare function matchPath<ParamKey extends string = string>(
pattern: PathPattern | string,
pathname: string
): PathMatch<ParamKey> | null;
interface PathMatch<ParamKey extends string = string> {
params: Params<ParamKey>;
pathname: string;
pattern: PathPattern;
}
interface PathPattern {
path: string;
caseSensitive?: boolean;
end?: boolean;
}

示例:

import { matchPath } from 'dumi';
const match = matchPath(
{ path: "/users/:id" },
"/users/123",
);
// {
// "params": { "id": "123" },
// "pathname": "/users/123",
// "pathnameBase": "/users/123",
// "pattern": { "path": "/users/:id" }
// }

matchRoutes

matchRoutes 可以将给定的路径以及多个可能的路由选择进行匹配,并且返回匹配结果。

类型定义如下:

declare function matchRoutes(
routes: RouteObject[],
location: Partial<Location> | string,
basename?: string
): RouteMatch[] | null;
interface RouteMatch<ParamKey extends string = string> {
params: Params<ParamKey>;
pathname: string;
route: RouteObject;
}

示例:

import { matchRoutes } from 'dumi';
const match = matchRoutes(
[
{
path: "/users/:id",
},
{
path: "/users/:id/posts/:postId",
},
],
"/users/123/posts/456",
);
// [
// {
// "params": {
// "id": "123",
// "postId": "456"
// },
// "pathname": "/users/123/posts/456",
// "pathnameBase": "/users/123/posts/456",
// "route": {
// "path": "/users/:id/posts/:postId"
// }
// }
// ]

<NavLink><Link> 的特殊形态,他知道当前是否为路由激活状态。通常在导航菜单、面包屑、Tabs 中会使用,用于显示当前的选中状态。

类型定义如下:

declare function NavLink(props: LinkProps & {
caseSensitive?: boolean;
children?: React.ReactNode | ((props: { isActive: boolean }) => React.ReactNode);
className?: string | ((props: { isActive: boolean }) => string | undefined);
end?: boolean;
style?: React.CSSProperties | ((props: { isActive: boolean }) => string | React.CSSProperties);
}): React.ReactElement;

下方示例分别用了 style、className 和 children 来渲染 active 状态。

import { NavLink } from 'dumi';
function Navs() {
return <ul>
<li><NavLink to="message" style={({ isActive }) => isActive ? { color: 'red' } : undefined}>Messages</NavLink></li>
<li><NavLink to="tasks" className={({ isActive }) => isActive ? 'active' : undefined}>Tasks</NavLink></li>
<li><NavLink to="blog">{({ isActive }) => <span className={isActive ? 'active' : undefined}>Blog</span>}</NavLink></li>
</ul>;
}

Outlet

<Outlet> 用于渲染父路由中渲染子路由。如果父路由被严格匹配,会渲染子路由中的 index 路由(如有)。

类型定义如下:

interface OutletProps {
context?: unknown;
}
declare function Outlet(
props: OutletProps
): React.ReactElement | null;

示例:

import { Outlet } from 'dumi';
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<Outlet />
</div>
);
}
function DashboardWithContext() {
return (
<div>
<h1>Dashboard</h1>
<Outlet context={{ prop: 'a' }}/>
</div>
);
}

Outlet 组件的 context 可以使用 API useOutletContext 在子组件中获取。

resolvePath

用于在客户端解析前端路由跳转路径。

类型定义如下:

declare function resolvePath(
to: Partial<Location> | string,
fromPathname?: string
): {
pathname: string;
search: string;
hash: string;
};

示例:

// 同级相对跳转,返回 { pathname: '/parent/child', search: '', hash: '' }
resolvePath('child', '/parent');
resolvePath('./child', '/parent');
resolvePath('', '/parent/child');
resolvePath('.', '/parent/child');
// 祖先层级相对跳转,返回 { pathname: '/parent/sibling', search: '', hash: '' }
resolvePath('../sibling', '/parent/child');
resolvePath('../../parent/sibling', '/other/child');
// 绝对跳转,返回 { pathname: '/target', search: '', hash: '' }
resolvePath('/target', '/parent');
resolvePath('/target', '/parent/child');
// 携带 search 和 hash 跳转,返回 { pathname: '/params', search: '?a=b', hash: '#c' }
resolvePath('/params?a=b#c', '/prev');

terminal

terminal 用于在开发阶段在浏览器向 node 终端输出日志的工具。

示例:

import {terminal} from 'dumi';
// 下面三条命令会在 dumi 启动终端上打出用不同颜色代表的日志
terminal.log('i am log level');
terminal.warn('i am warn level');
terminal.error('i am error level');

注意 terminal 只在环境变量 NODE_ENVproduction 时生效;在 dumi 的构建产物中对应的日志调用函数不会有任何作用,所以可以不必删除调用 terminal 的代码。

useAppData

useAppData 返回全局的应用数据。

类型定义如下:

declare function useAppData(): {
routes: Record<id, Route>;
routeComponents: Record<id, Promise<React.ReactComponent>>;
clientRoutes: ClientRoute[];
pluginManager: any;
rootElement: string;
basename: string;
clientLoaderData: { [routeKey: string]: any };
preloadRoute: (to: string) => void;
};

注意:此处 API 可能还会调整。

useLocation

useLocation 返回当前 location 对象。

类型定义如下:

declare function useLocation(): {
pathname: string;
search: string;
state: unknown;
key: Key;
};

一个场景是在 location change 时做一些 side effect 操作,比如 page view 统计。

import { useLocation } from 'dumi';
function App() {
const location = useLocation();
React.useEffect(() => {
ga('send', 'pageview');
}, [location]);
// ...
}

useMatch

useMatch 返回传入 path 的匹配信息;如果匹配失败将返回 null

类型定义如下:

declare function useMatch(pattern: {
path: string;
caseSensitive?: boolean;
end?: boolean;
} | string): {
params: Record<string, string>;
pathname: string;
pattern: {
path: string;
caseSensitive?: boolean;
end?: boolean;
};
};

示例:

import { useMatch } from 'dumi';
// when url = '/events/12'
const match = useMatch('/events/:eventId');
console.log(match?.pathname, match?.params.eventId);
// '/events/12 12'

useNavigate

useNavigate 钩子函数返回一个可以控制跳转的函数;比如可以用在提交完表单后跳转到其他页面。

declare function useNavigate(): NavigateFunction;
interface NavigateFunction {
(
to: To,
options?: { replace?: boolean; state?: any }
): void;
(delta: number): void;
}

示例:

  • 跳转路径
import { useNavigate } from 'dumi';
let navigate = useNavigate();
navigate("../success", { replace: true });
  • 返回上一页
import { useNavigate } from 'dumi';
let navigate = useNavigate();
navigate(-1);

useOutlet

useOutlet 返回当前匹配的子路由元素,<Outlet> 内部使用的就是此 hook 。

类型定义如下:

declare function useOutlet(): React.ReactElement | null;

示例:

import { useOutlet } from 'dumi';
const Layout = ()=>{
const outlet = useOutlet()
return <div className="fancyLayout">
{outlet}
</div>
}

useOutletContext

useOutletContext 用于返回 Outlet 组件上挂载的 context

类型定义如下:

declare function useOutletContext<Context = unknown>(): Context;

示例:

import { useOutletContext, Outlet } from 'dumi';
const Layout = () => {
return <div className="fancyLayout">
<Outlet context={{ prop: 'from Layout'}} />
</div>
}
const SomeRouteComponentUnderLayout = () => {
const layoutContext = useOutletContext();
return JSON.stringify(layoutContext) // {"prop":"from Layout"}
}

useParams

useParams 钩子函数返回动态路由的匹配参数键值对对象;子路由中会集成父路由的动态参数。

类型定义如下:

declare function useParams<
K extends string = string
>(): Readonly<Params<K>>;

示例:

import { useParams } from 'dumi';
// 假设有路由配置 user/:uId/repo/:rId
// 当前路径 user/abc/repo/def
const params = useParams()
/* params
{ uId: 'abc', rId: 'def'}
*/

useResolvedPath

useResolvedPath 根据当前路径将目标地址解析出完整的路由信息。

类型定义如下:

declare function useResolvedPath(to: To): Path;

示例:

import { useResolvedPath } from 'dumi';
const path = useResolvedPath('docs')
/* path
{ pathname: '/a/new/page/docs', search: '', hash: '' }
*/

useRouteData

useRouteData 返回当前匹配路由的数据的钩子函数。

类型定义如下:

declare function useRouteData(): {
route: Route;
};

注意:此处 API 可能还会调整。

示例:

import { useRouteData } from 'dumi';
const route = useRouteData();
/* route
{
route: {
path: 'a/page',
id: 'a/page/index',
parentId: '@@/global-layout',
file: 'a/page/index.tsx'
}
}
*/

useRoutes

useRoutes 渲染路由的钩子函数,传入路由配置和可选参数 location, 即可得到渲染结果;如果没有匹配的路由,结果为 null

类型定义如下:

declare function useRoutes(
routes: RouteObject[],
location?: Partial<Location> | string;
): React.ReactElement | null;

示例:

import * as React from "react";
import { useRoutes } from "dumi";
function App() {
let element = useRoutes([
{
path: "/",
element: <Dashboard />,
children: [
{
path: "messages",
element: <DashboardMessages />,
},
{ path: "tasks", element: <DashboardTasks /> },
],
},
{ path: "team", element: <AboutPage /> },
]);
return element;
}

useRouteProps

读取当前路由在路由配置里的 props 属性,你可以用此 hook 来获取路由配置中的额外信息。

// .umirc.ts
routes: [
{
path: '/',
custom_key: '1',
}
]
import { useRouteProps } from 'dumi'
export default function Page() {
const routeProps = useRouteProps()
// use `routeProps.custom_key`
}

注:同样适用于约定式路由。

useSelectedRoutes

用于读取当前路径命中的所有路由信息。比如在 layout 布局中可以获取到当前命中的所有子路由信息,同时可以获取到在 routes 配置中的参数,这格外有用。

实例:

// layouts/index.tsx
import { useSelectedRoutes } from 'dumi'
export default function Layout() {
const routes = useSelectedRoutes()
const lastRoute = routes.at(-1)
if (lastRoute?.pathname === '/some/path') {
return <div>1 : <Outlet /></div>
}
if (lastRoute?.extraProp) {
return <div>2 : <Outlet /></div>
}
return <Outlet />
}

useSearchParams

useSearchParams 用于读取和修改当前 URL 的 query string。类似 React 的 useState,其返回包含两个值的数组,当前 URL 的 search 参数和用于更新 search 参数的函数。

类型定义如下:

declare function useSearchParams(defaultInit?: URLSearchParamsInit): [
URLSearchParams,
(
nextInit?: URLSearchParamsInit,
navigateOpts?: : { replace?: boolean; state?: any }
) => void
];
type URLSearchParamsInit =
| string
| ParamKeyValuePair[]
| Record<string, string | string[]>
| URLSearchParams;

示例:

import React from 'react';
import { useSearchParams } from 'dumi';
function App() {
let [searchParams, setSearchParams] = useSearchParams();
function handleSubmit(event) {
event.preventDefault();
setSearchParams(serializeFormQuery(event.target));
}
return <form onSubmit={handleSubmit}>{/* ... */}</form>;
}

withRouter

withRouter 参考 react-router faq 实现的版本, 仅实现了部分能力, 请参考类型定义按需使用, 建议迁移到 React Hook API。

类型定义如下:

export interface RouteComponentProps<T = ReturnType<typeof useParams>> {
history: {
back: () => void;
goBack: () => void;
location: ReturnType<typeof useLocation>;
push: (url: string, state?: any) => void;
};
location: ReturnType<typeof useLocation>;
match: {
params: T;
};
params: T;
navigate: ReturnType<typeof useNavigate>;
}

示例:

import React from 'react';
import { withRouter } from 'dumi';
class HelloWorld extends React.Component<any> {
render() {
return (
<div>
Hello World {this.props.location.pathname}
<h2>params: {JSON.stringify(this.props.match.params)}</h2>
<button
onClick={() => {
this.props.history.push('/users');
}}
>
To Users
</button>
</div>
);
}
}
export default withRouter(HelloWorld);