1
0
Fork 0
blog/content/posts/逍遥游/Hello,Shiki.md

219 lines
8.6 KiB
Markdown
Raw Normal View History

---
slug: hello-shiki
summary: 这代码高亮,多是一件美事啊
tags:
- 博客
- 折腾
title: HelloShiki
date: 2024-08-18
description: 本文介绍了如何在Hugo中使用Shiki进行代码高亮Shiki在服务端执行提供了更好的性能和样式适合构建静态网站需满足特定前置条件和安装依赖步骤包括配置Hugo、创建rehyperc文件和修改CSS等。
categories: ["逍遥游"]
featuredImage: https://img.viento.cc/cover/hello-shiki-cover.webp
draft: false
showTableOfContents: true
---
各位老友们好,我是 Chlorine。继续高强度水文。
本期的主题是 Hugo 的代码高亮,主要参考了[蜗牛大神](https://www.eallion.com)的教程:[在 Hugo 中使用 Shiki](https://www.eallion.com/hugo-syntax-highlight-shiki/)。感谢前辈的付出。
## 前言
Hugo 的代码高亮基于 Chroma。这是个用 Go 语言写的库,好处是性能极高,坏处是不太聪明。比如我之前的文章 [HelloSearXNG]({{< relref "HelloSearXNG.md" >}}),里面有一段 PowerShell 脚本,结果:
![](https://img.viento.cc/IMG-20240818100304.avif)
嗯……这个效果不能说是尽善尽美吧,至少也可以说是聊胜于无。
其他的部分,像 `docker-compose``git` 之类的命令高亮不了,已经是一食堂的麻辣香锅——家常便饭了。
那怎么修复?无非两个招:自己写 Lexer[^1],或者引入第三方库。
不清楚哪位勇士有第一项所需的娴熟的专业技能、大把的闲暇时间和超人的折腾勇气,反正小氯自认为没有。
Hugo 的第三方代码高亮库很多,比方说 `Highlight.js``Prime.js`,其优缺点可谓各有千秋。但它们都有一个致命的缺点:**都需要在客户端执行大量的 JavaScript 代码**。这就势必会对性能造成可怕的影响。如果大家没有什么感知的话,请参考下面的 Twikoo 的加载需要多久。
那有没有办法在服务端执行代码,把压力放在网站构建过程中呢?
方法就是我们今天的主角——Shiki。
## Shiki 简介
Shiki 这个名字来自于日语的「式」しき意思就是「样式」。此外如果我记得没错Shiki 应该还有季节、四季的意思,当然这不重要。
[Shiki](https://shiki.style) 是一个基于 VS Code 语法高亮引擎(没错,就是那个 VS Code的代码高亮库。其基于 TextMate 语法定义文件和 WebAssembly 技术提供快速精确的代码高亮。
Shiki 很特别的一点在于:**它在执行时会扫描指定路径的 HTML 文件,并且将各种语法 token 附加上内联样式**。这使得 Shiki 非常适合用来为 Hugo 这样的静态网站构建工具添加高亮,无它,扫一遍 `public` 就行了。
顺便说一句Shiki 是 Pine Wu 和 Anthony Fu 等前端大神的力作 :)
## 前置要求
请注意,如果您的已有条件和需求不满足下面所述,那小氯推荐您还是先别看文章了,还是先去公寓的公共空间喝杯茶更能体现对您生命的尊重。~~没错我是直接从上一篇文章复制过来的~~
条件:
- 已经能用 Hugo 构建静态网站
- 对 GitHub Actions 和 Vercel 等自动构建的流程有一定了解
- 本地已经配置了一个 JavaScript 运行时及包管理器,例如 `Node.js` 或者 `Bun.js`。我们以下均使用 `Bun.js` 进行演示。
要求:
- 对 Chroma 的高亮表现不满
- 不希望使用 `Highlight.js` 等客户端高亮器
## 安装相关依赖
进入你的博客根目录,运行命令:
```bash
bun i shiki @shikijs/rehype rehype-cli
```
这应该会自动为你创建 `node_modules``bun.lockb``package.json`。记得把 `node_nodules` 文件夹加到 `.gitignore` 里面——如果没有自动添加的话。
## 配置 Hugo
修改 `hugo.toml` 或者你其他名字的配置文件,将 `codeFences` 改为 `false`。如果没有请自行创建,像这样:
```toml
[markup]
[markup.highlight]
codeFences = false
```
## 创建 `.rehyperc`
如果我没猜错的话,这应该是 rehype 的配置文件了。看起来跟个 `JSON` 似的。
```json
{
"plugins": [
[
"@shikijs/rehype",
{
"themes": {
"light": "你的日间主题",
"dark": "你的夜间主题"
}
}
]
]
}
```
主题列表在[这里](https://shiki.style/themes),挑个自己喜欢的吧。效果没必要像我一样每次都重新构建,在 VS Code 里面搜索同名主题看就行了。我比较选综,就用了 One Dark Pro 系列的。
rehype 这东西很神奇,可以对 HTML 各种爆改。当然,咱们这里用的就是其中一个插件,如果想进一步探索请看[这里](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md),如果有什么好的创意也可以和我分享,小氯洗耳恭听。
## 修改 Hugo CSS
找到你主题的 CSS。我们要做一下样式适配。我原本的样式大概是
```css
code {
font-family: "Fira Code Light", "LXGW WenKai Lite", monospace;
}
code[class*="language-"],
pre[class*="language-"] {
white-space: pre-wrap;
/* 或 pre-line */
word-break: break-all;
}
```
现在改成:
```css
code {
font-family: "Fira Code Light", "LXGW WenKai Lite", monospace;
}
html .shiki,
html .shiki span {
white-space: pre-wrap;
word-break: break-all;
overflow-wrap: break-word;
font-family: "Fira Code Light", "LXGW WenKai Lite", monospace;
}
html.dark .shiki,
html.dark .shiki span {
color: var(--shiki-dark) !important;
white-space: pre-wrap;
word-break: break-all;
overflow-wrap: break-word;
font-family: "Fira Code Light", "LXGW WenKai Lite", monospace;
}
```
大家照猫画虎即可。
## 配置脚本命令
在根目录的 `package.json` 下面添加这样的脚本命令:
```json
"scripts": {
"shiki": "bunx rehype-cli public -o"
}
```
大体而言改完之后你的文件应该看起来像是:
```json
{
"dependencies": {
"@shikijs/rehype": "^1.13.0",
"rehype-cli": "^12.0.0",
"shiki": "^1.13.0"
},
"scripts": {
"shiki": "bunx rehype-cli public -o"
}
}
```
当然shiki 这个名字你可以随便起,然后就可以在根目录下运行 `bun run shiki` 对构建产物进行替换了。如果报错,大概率是因为你使用了不支持的语言或者错误的语言代码(比方说,`html` 写成 `HTML`)。可以在[这里](https://shiki.style/languages)查看标准化的代号表。
## Vercel 构建命令
GitHub Actions 版本的可以看上面蜗牛大神的教程。
```bash
cd themes/efimero && bun install && bun run build && cd ../.. && hugo --gc --minify && bun install && (bun run shiki || true)
```
这大概就是完整的构建命令了。最后的 `|| true` 是为了防止报错导致的构建失败。
## 自动化
大家知道,小氯是个被 C++ ~~荼毒~~熏陶过的带学生,因此习惯使用 Makefile 整合所有构建过程。
不过 Makefile 有个致命的缺点:**只能一步一步执行命令**。而 `bun run shiki` 应该在 `hugo server -D` **构建 public 文件夹完成,但是未结束(只有我们手动停止这个命令才能结束)** 时执行。使用后台运行的指令的话,又没办法准确把控时机。唯一的方法可能就是使用 `fswatch` 了,但是这样又会造成不必要的性能开销。
对于不经常折腾主题,只是发布内容的老友来说,这其实不是问题,主要对于小氯这样的喜欢折腾主题的人比较麻烦。现在只能是每次手动执行下,或者干脆只 `make`,缺点就是高亮没了。
## 去掉行号
在本地测试的时候我发现 Hugo 代码块的行号变得非常抽象具体来说10 居然会换行变成 1 和 0。改 `hugo.toml` 不管用于是采取最简单粗暴的方法CSS 隐藏。
```css
.custom-md code span.line:before {
display: none;
}
```
## 结语
又水了篇文章,内心毫无~~波兰~~波澜。
Shiki 的效果确实出挑,虽然会显著加长构建时间,但是也算可以接受。这样代码高亮看起来就好看多了。
以及一个新消息:我准备为主域名重新备案啦!这期间网站评论可能会关闭(看侧边栏的公告即可),但是其他功能不受影响。可以给我发邮件来互动~
[^1]: Lexer即词法分析器或扫描器是编译器的第一阶段。其任务是读取源代码文本并将其分解成一系列的标记tokens。每个标记代表了源代码中的一个有意义的片段比如关键字、标识符、字面量、运算符等。Lexer 的输出是源代码的抽象表示它为后续的语法分析parser提供了基础。