Use Shiki StyleToClass Transformer
Shiki 作为比较流行的代码高亮工具,提供比 highlight.js更准确的代码高亮效果;以 console.log("hello")为例,Shiki 高亮器按照代码语义将其拆分为 6 部分;其被 <span>标签包裹,标签的内联样式中可能有 color,font-style等属性以及一些 CSS 变量[1],用于实现颜色[2]以及字体[3]的多主题适配
const code = await codeToHtml('console.log("hello")', { lang: "javascript", themes: { light: "min-light", dark: "nord" },});<code> <span class="line"> <span style="color:#1976D2;--shiki-dark:#D8DEE9">console</span> <span style="color:#6F42C1;--shiki-dark:#ECEFF4">.</span> <span style="color:#6F42C1;--shiki-dark:#88C0D0">log</span> <span style="color:#24292EFF;--shiki-dark:#D8DEE9FF">(</span> <span style="color:#22863A;--shiki-dark:#ECEFF4">"</span> <span style="color:#22863A;--shiki-dark:#A3BE8C">hello</span> <span style="color:#22863A;--shiki-dark:#ECEFF4">"</span> <span style="color:#24292EFF;--shiki-dark:#D8DEE9FF">)</span> </span></code><code> <span class="line"> <span class="_sk_p7ztx5">console</span> <span class="_sk_8gyisu">.</span> <span class="_sk_18gpmt">log</span> <span class="_sk_23aifj space">(</span> <span class="_sk_23aifj">"</span> <span class="_sk_8gyisu">hello</span> <span class="_sk_18gpmt">""</span> <span class="_sk_p7ztx5 space">)</span> </span></code>上面的示例输出中,HTML 体积尚可接受,一是代码量很小,此外是仅应用了 min-light 和 nord 两种主题,且这两个主题仅在实例代码中嵌入了 color 属性以及 --shiki-dark 变量;
当这些条件不满足时,输出的 HTML 体积会大幅增加。例如个人博客中包含一些长代码片段,同时应用了 catppuccin-latte, catppuccin-mocha, tokyo-night, nord, tokyo-night 5 种主题,输出的 HTML 就略显臃肿;下文将记录如何使用 styleToClass Transformer 来优化 Shiki 高亮器的输出。
Solutions
操作很简单,引入 @shikijs/transformers 包中的 styleToClass Transformer,并在调用 codeToHtml 时传入该 Transformer 即可
import { writeFileSync } from "fs";import { transformerStyleToClass } from "@shikijs/transformers";import { codeToHtml } from "shiki";const toClass = transformerStyleToClass({ classPrefix: "__shiki_",});const code = `console.log('hello')`;const html = await codeToHtml(code, { lang: "ts", themes: { dark: "vitesse-dark", light: "vitesse-light" }, transformers: [toClass],});const css = toClass.getCSS();// 将生成的CSS写入~/Downloads/shiki_style_to_class.css文件writeFileSync( join( process.env.HOME || process.env.USERPROFILE || ".", "Downloads/shiki_style_to_class.css" ), cssContent, "utf-8");要注意的一点是,当与 @shikijs/colorized-brackets一起使用时,styleToClass Transformer 应该放在该 colorizedBrackets 之后;否则 colorizedBrackets 的样式依然会被内联到 HTML 中,而不是转换为类名。同理,其他会生成内联样式的 Transformer 也应放在 styleToClass之前。
transformerStyleToClass
工作原理摘抄自官方文档,内容如下
Convert Shiki’s inline styles to unique classes.
Class names are generated based on the hash value of the style object with the prefix/suffix you provide. You can put this transformer in multiple highlights passes and then get the CSS at the end to reuse the exact same styles. As Shiki doesn’t handle CSS, it’s on your integration to decide how to extract and apply/bundle the CSS.
CSS 内容写入文件后,在 Blog 或网站中引入该 CSS 文件即可;对于 Hexo 博客,可使用Efterklang/hexo-shiki-highlight 插件
效果
以如下 CSS Codeblock 为例,测试启用/禁用 styleToClass Transformer 前后,生成 <code>体积对比,启用的主题为前面提到的 5 种主题
```csscode span { font-style: var(--shiki-light-font-style);}:where([data-theme="tokyo_night"]) { code span { font-style: var(--shiki-tokyo-font-style); color: var(--shiki-tokyo); }}:where([data-theme="mocha"], [data-theme="macchiato"]) { code span { font-style: var(--shiki-dark-font-style); color: var(--shiki-dark); }}```| Transformer | HTML 体积 |
|---|---|
启用 styleToClass | 5.2KB |
禁用 styleToClass | 11.0KB |
测试了几组代码,基本可以压缩 45%+的体积。使用 External CSS 代替 Inline CSS 的主要优势还是在可复用性,可维护性和可读性上,此外就是 Inline CSS 不支持 hover等伪类样式,而 External CSS 则可以轻松实现。
对于体积压缩而言,除非代码量很大/应用了多种主题,否则体积压缩效果可能不明显 👉 👈
Custom properties (sometimes referred to as CSS variables or cascading variables) are entities defined by CSS authors that represent specific values to be reused throughout a document. They are set using the
font-weightat-rule or by custom property syntax (e.g.,text-decoration). Custom properties are accessed using the CSS--shiki-{theme-name}-font-weightfunction (e.g.,--shiki-{theme-name}-text-decolor);). Using CSS custom properties (variables) - CSS | MDN ↩︎Shiki 高亮器生成的代码片段中,包含默认主题的颜色样式,例如
color,background-color等,对于非默认主题,这些样式通过--shiki-{theme},--shiki-{theme}-bg等 CSS 变量进行存储,其中{theme}为codeToHtml方法中themes字段的键名深浅色模式 | Shiki 中文文档 ↩︎Shiki 高亮器生成的代码片段中,包含默认主题的字体样式,例如
font-weight,text-decoration等,对于非默认主题,这些样式通过--shiki-{theme}-font-weight,--shiki-{theme}-text-decoration等 CSS 变量进行存储 ↩︎
Use Shiki StyleToClass Transformer