记一次迁移到Shiki的尝试

Apr 30, 2024

从写前端开始,我使用的语法高亮器一直是highlight.js,包括这个博客;但是,随着我将博客的CSS框架迁移到Tailwind CSS后,适配highlight.js时遇到了大大小小很多问题,之前写的代码也屎,就决定弃用了。看到了antfu老师的这篇博客了解到了Shiki,文档写的十分详细,支持很多现代新框架,便有了这次尝试。

此博客数据源是Markdown,加载MDX数据我使用的是next-mdx-remote,其中主要使用到<MDXRemote />组件:

function MDXComponent({ source }) {
  return (
    <MDXRemote
      {...source} // MDX Content
      components={components} // Custom Components
    />
  );
}

不像mdx-bundler那样可以在MDX中使用import导入组件,next-mdx-remote则需要我们自己解析:

const components = {
  pre: (props) => <Codeblock {...props} />,
  //...
};

Shiki提供了Rehype插件,可直接传入<MDXRemote />中:

function MDXComponent({ source }) {
  return (
    <MDXRemote
      {...source}
      components={components}
      options={{
        mdxOptions: {
          rehypePlugins: [
            [
              rehypeShiki,
              {
                themes: {
                  light: "github-light",
                  dark: "github-dark",
                },
                //添加'language-*'到class中,可以用来在代码块中显示语言种类
                addLanguageClass: true,
              },
            ],
          ],
        },
      }}
    />
  );
}

完成了这些,就可以去<Codeblock />打印出接收到的props看看:

Codeblock组件props打印出的内容

可以看到Shiki已经生效了,可以在组件内拿到'language-jsx',children是React Element可以直接丢到JSX中。

最后是对深色模式的支持:在前面的themes选项中我们已经指定了lightdark两个主题,相应的样式已经已经挂在了标签上了:

生成的HTML

在css中添加一些内容,使这些样式能响应深浅色模式:

html.dark .codeblock span {
  color: var(--shiki-dark) !important;
}