likes
comments
collection
share

umi 路由支持 mdx - 模拟在 umi 项目中集成 dumi

作者站长头像
站长
· 阅读数 14

背景

dumi@1 官网上写着与 umi 项目的集成方式如下:

除了独立的组件库以外,我们大多数的项目还会有自己的内部组件,这些内部的组件库管理通常是一个很头疼的问题,既不需要发布单独的 npm 包,又需要进行迭代、更新、说明、交接;为了让项目内部组件库管理这件事变得更加轻松,dumi 推出了 Umi 项目集成模式:

  • 自动探测:当 dependenciesdevDependencies 中包含 umi@umijs/preset-dumi 时,进入集成模式(不再需要单独安装 dumi 这个包)
  • 相互隔离:所有 dumi 文档都会集中在 /~docs 路由下,与原项目相互隔离、互不干扰,可以理解为标准 dumi 文档都加了一个特定路由前缀,也包括用户的导航和菜单路由配置
  • 不影响生产:仅在 NODE_ENVdevelopment 时集成,不影响项目的生产构建
  • 可单独构建:如果需要单独构建文档做部署,可执行 umi build --dumi,即可得到一份非集成模式的 dumi 站点产物,--dumiumi dev 命令下也是可用的

使用方式很简单:在已有 Umi 项目中安装 @umijs/preset-dumidevDependencies 中,再根据需要配置 resolve.includes 即可(比如约定 src/components 目录下为业务组件库和组件库对应的文档)。

可惜 dumi 1.x 暂不支持与 Umi 4 项目集成。然后等了好久 dumi@2 与 umi 的集成功能一直没出。

umi 路由支持 mdx - 模拟在 umi 项目中集成 dumi

实现

看了一下要实现也不难,umi@4 本身就支持 mdx 的配置,只需要按照 dumi@2 的 mdx 配置应该可以很简单的写一个 umi 插件支持。

mdx 解析

简单的翻一下 dumi 的文档,发现这部分的逻辑在 src/loaders/markdown/transformer/index.ts

umi 路由支持 mdx - 模拟在 umi 项目中集成 dumi

依样画葫芦,我们在 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 文件被正确解析了。

umi 路由支持 mdx - 模拟在 umi 项目中集成 dumi

但是我们添加的代码块并没有被正确解析。

通过审查元素,我们可以发现 dom 已经正确解析了,只是少了 css。

umi 路由支持 mdx - 模拟在 umi 项目中集成 dumi

这时候,我们只需要安装 prism-themes,然后在项目的 global.ts 中全局引入样式即可。

import "prism-themes/themes/prism-one-light.css";

这个主题也是 dumi@2 的选择

重启项目,你将看到代码高亮显示。

umi 路由支持 mdx - 模拟在 umi 项目中集成 dumi

暗黑主题

通过观察 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 ,以后我会写一篇文章讲解。

在线体验

你可以通过这个链接,在线体验,暗黑主题和运行时换肤等功能。

umisse

umisse 项目的灵感来自 antfu 大神的 vitesse,欢迎 Star。

源码归档

umi-plugin-route-mdx

umisse