umi 路由支持 mdx - 模拟在 umi 项目中集成 dumi
背景
dumi@1 官网上写着与 umi 项目的集成方式如下:
除了独立的组件库以外,我们大多数的项目还会有自己的内部组件,这些内部的组件库管理通常是一个很头疼的问题,既不需要发布单独的 npm 包,又需要进行迭代、更新、说明、交接;为了让项目内部组件库管理这件事变得更加轻松,dumi 推出了 Umi 项目集成模式:
- 自动探测:当
dependencies
或devDependencies
中包含umi
和@umijs/preset-dumi
时,进入集成模式(不再需要单独安装dumi
这个包) - 相互隔离:所有 dumi 文档都会集中在
/~docs
路由下,与原项目相互隔离、互不干扰,可以理解为标准 dumi 文档都加了一个特定路由前缀,也包括用户的导航和菜单路由配置 - 不影响生产:仅在
NODE_ENV
是development
时集成,不影响项目的生产构建 - 可单独构建:如果需要单独构建文档做部署,可执行
umi build --dumi
,即可得到一份非集成模式的 dumi 站点产物,--dumi
在umi dev
命令下也是可用的
使用方式很简单:在已有 Umi 项目中安装 @umijs/preset-dumi
到 devDependencies
中,再根据需要配置 resolve.includes
即可(比如约定 src/components
目录下为业务组件库和组件库对应的文档)。
可惜 dumi 1.x 暂不支持与 Umi 4 项目集成。然后等了好久 dumi@2 与 umi 的集成功能一直没出。
实现
看了一下要实现也不难,umi@4 本身就支持 mdx 的配置,只需要按照 dumi@2 的 mdx 配置应该可以很简单的写一个 umi 插件支持。
mdx 解析
简单的翻一下 dumi 的文档,发现这部分的逻辑在 src/loaders/markdown/transformer/index.ts
依样画葫芦,我们在 umi 的配置文件中,配置 mdx,会发现 umi 的配置文件中不让使用 impot 语法,但是关系不大,我们可以在“项目中的插件” - plugin.ts
文件中使用 api.modifyDefaultConfig
来配置 mdx
:
import { IApi } from "umi";
import type { Element } from "hast";
export default (api: IApi) => {
api.modifyDefaultConfig(async (memo) => {
const { default: remarkParse } = await import("remark-parse");
const { default: remarkFrontmatter } = await import("remark-frontmatter");
const { default: remarkDirective } = await import("remark-directive");
const { default: remarkBreaks } = await import("remark-breaks");
const { default: remarkGfm } = await import("remark-gfm");
const { default: rehypeAutolinkHeadings } = await import(
"rehype-autolink-headings"
);
const { default: rehypeRemoveComments } = await import(
"rehype-remove-comments"
);
const { default: rehypePrismPlus } = await import("rehype-prism-plus");
const { default: rehypeExternalLinks } = await import(
"rehype-external-links"
);
memo.mdx = {
loader: require.resolve("@mdx-js/loader"),
loaderOptions: {
providerImportSource: "@mdx-js/react",
remarkPlugins: [
remarkParse,
remarkFrontmatter,
remarkDirective,
remarkBreaks,
remarkGfm,
],
rehypePlugins: [
rehypeAutolinkHeadings,
rehypeRemoveComments,
[rehypePrismPlus, { ignoreMissing: true }],
[
rehypeExternalLinks,
{
target(element: Element) {
return element.properties &&
/^https?:\/\//.test(`${element.properties!.href}`)
? "_blank"
: undefined;
},
},
],
],
},
};
return memo;
});
};
尝试一下 mdx 文件
让我们尝试一下,在 umi 约定的路由目录下,新建一个 mdx 文件 src/pages/about.mdx
# About
<div>Hello</div>
```js
function func (){
return true;
}
/```
如果你使用配置路由,需要把增加的文件,写到你的
routes
配置中。
这时候启动项目,访问 about 页面,你将看到 mdx
文件被正确解析了。
但是我们添加的代码块并没有被正确解析。
通过审查元素,我们可以发现 dom 已经正确解析了,只是少了 css。
这时候,我们只需要安装 prism-themes
,然后在项目的 global.ts
中全局引入样式即可。
import "prism-themes/themes/prism-one-light.css";
这个主题也是 dumi@2 的选择
重启项目,你将看到代码高亮显示。
暗黑主题
通过观察 dumi 官网的暗黑主题,我们很容易发现,它主要是通过 dark-selector
的改变来进行样式覆盖的,看起来有点难,要写不少代码。不适合我这种 css 懒人,所以我们换一种简单的方式来实现。
打开 prism-themes/themes/prism-one-light.css
文件,你可以在文件中看到如下的配置。
/**
* One Light colours (accurate as of commit eb064bf on 19 Feb 2021)
* From colors.less
* --mono-1: hsl(230, 8%, 24%);
* --mono-2: hsl(230, 6%, 44%);
* --mono-3: hsl(230, 4%, 64%)
* --hue-1: hsl(198, 99%, 37%);
* --hue-2: hsl(221, 87%, 60%);
* --hue-3: hsl(301, 63%, 40%);
* --hue-4: hsl(119, 34%, 47%);
* --hue-5: hsl(5, 74%, 59%);
* --hue-5-2: hsl(344, 84%, 43%);
* --hue-6: hsl(35, 99%, 36%);
* --hue-6-2: hsl(35, 99%, 40%);
* --syntax-fg: hsl(230, 8%, 24%);
* --syntax-bg: hsl(230, 1%, 98%);
* --syntax-gutter: hsl(230, 1%, 62%);
* --syntax-guide: hsla(230, 8%, 24%, 0.2);
* --syntax-accent: hsl(230, 100%, 66%);
* From syntax-variables.less
* --syntax-selection-color: hsl(230, 1%, 90%);
* --syntax-gutter-background-color-selected: hsl(230, 1%, 90%);
* --syntax-cursor-line: hsla(230, 8%, 24%, 0.05);
*/
并且通过
import "prism-themes/themes/prism-one-light.css";
我们不难发现,它有一个对应的暗黑主题
import "prism-themes/themes/prism-one-dark.css";
对比两个 css 文件,除了颜色数值,他们几乎是一样的。
因此我们将这些变量还原成 css 变量。
html {
--mono-1: hsl(230, 8%, 24%);
}
code[class*="language-"],
pre[class*="language-"] {
color: var(--mono-1);
}
然后给暗黑主题也整理一套 css 变量
html.dark {
/* One Dark colours (accurate as of commit 8ae45ca on 6 Sep 2018)
From colors.less */
--mono-1: hsl(220, 14%, 71%);
}
把这些 css 变量和 css 引到项目中,然后我们就可以在运行时,通过简单的 js 语法,切换两个主题了。
document.documentElement.classList.toggle("dark", true);
是不是非常简单啊,当然了,为了方便网友尝鲜,我们可以将这整理成一个插件包。
umi-plugin-route-mdx
Umi@4 Plugin For Route Support mdx。
使用 umi mdx 插件
pnpm i umi-plugin-route-mdx
import { defineConfig } from "umi";
export default defineConfig({
plugins: [require.resolve("umi-plugin-route-mdx")],
});
切换 dark theme
document.documentElement.classList.toggle("dark", true);
其他
细心的你一定发现了,我们的 mdx 解析,暗黑主题部分我们只处理了,语法高亮相关的暗黑主题,其他的文字我们并没有处理。文字处理需求其实很简单,我们可以通过 antd@5 的 App 组件,或者 theme-ui
的主题变化,就能处理这些游离
的 dom。感兴趣的同学,可以自己尝试一下。因为这部分涉及到 antd@5 ,以后我会写一篇文章讲解。
在线体验
你可以通过这个链接,在线体验,暗黑主题和运行时换肤等功能。
源码归档
转载自:https://juejin.cn/post/7198496671113822266