首页
/ Slidev Code Groups 详解:基于 Comark 语法的标签式代码分组与图标自动匹配

Slidev Code Groups 详解:基于 Comark 语法的标签式代码分组与图标自动匹配

2026-09-07 16:53:36作者:郜逊炳

本文围绕 Slidev 的 Code Groups(代码分组)功能展开:如何在一张幻灯片中用 ::code-group 指令把多个代码块组织成可点击切换的标签页(例如 npm / yarn / pnpm 三种安装方式),以及如何通过标题自动匹配图标、使用 ~icon~ 语法自定义图标。读完后你可以直接在 Slidev 演示中编写带标签栏与品牌图标的多版本代码示例,并从 CodeGroup.vueTitleIcon.vue 等源码理解其标签渲染与图标匹配的底层机制。

code-groups-demo

前提条件:启用 Comark 语法

Code Groups 依赖 Slidev 的 Comark 语法——一种基于指令(Directive)的增强 Markdown 扩展,允许在文档中书写块级组件指令(::block-component / :::)。因此使用 Code Groups 之前,必须先在幻灯片文件的 frontmatter 中开启该选项:

---
comark: true
---

未开启 comark 时,::code-group 指令不会被解析为组件,也就无法渲染标签栏。该开关是文件级的,只作用于当前启用了它的幻灯片入口(详见 Comark 文档)。

基本用法:用 ::code-group 包裹多个代码块

开启 Comark 后,就可以用 ::code-group 开、::: 闭的形式把任意数量的代码块分组。每个代码块的标准标题(即信息字符串中的 [标题])会成为对应的标签页名称:

::code-group

```sh [npm]
npm i @slidev/cli
```

```sh [yarn]
yarn add @slidev/cli
```

```sh [pnpm]
pnpm add @slidev/cli
```

::

渲染效果是:顶部生成一排标签(npm、yarn、pnpm),点击某个标签即切换下方展示的代码块;默认激活第一个带标题的代码块。这一交互由 CodeGroup.vue 实现,其源码逻辑非常直观:

  • onMounted 时通过 querySelectorAll('.slidev-code-wrapper') 收集分组内所有代码块包装元素;
  • 读取每个元素的 data-title 属性(该属性由代码块包装组件 CodeBlockWrapper.vue 在渲染时写入,值即代码块的 [标题]);
  • 第一个带标题的块成为初始激活项(activeTitle),所有标题依次填入 tabs 数组;
  • 标签文案在展示前会执行 tab.replace(/~([^~]+)~/g, '').trim(),即自动剥除 ~icon~ 部分(后文详述),保证标签上只显示干净的名称。

配合 code.css.slidev-code-group-tabs / .slidev-code-wrapper.active 等样式,激活的标签会显示主题色边框,未激活的块则被隐藏,从而实现"标签页"式的切换体验。

一个值得注意的细节:当代码块处于 code-group 内部时,单个代码块自身不会再重复渲染标题栏。从 CodeBlockWrapper.vue 源码可以看到,它通过 inject('activeTitle') 注入父级 CodeGroup 提供的激活标题引用;只有当 activeTitlenull(即不在 code-group 内)且自身有标题时,才会渲染 slidev-code-block-title 标题条。这保证了标题只在标签栏出现一次,视觉上更整洁。

标题自动匹配图标

除了纯文字标签,Code Groups 还支持根据标题名称自动匹配图标。例如标题为 pnpm 时,标签左侧会自动出现 pnpm 的 logo。文档中说明,同样的图标匹配能力也适用于单个带标题的 code block 以及 Shiki Magic Move,因为三者的图标解析都收敛到同一个 TitleIcon.vue 组件。

TitleIcon 的匹配算法(见 matchIcon 函数)分两步:

  1. 显式图标优先:若标题中匹配到 ~icon~ 语法(title.match(/~([^~]+)~/g)),直接取第一个匹配项作为图标类名使用,不再查内置表;
  2. 内置表最长匹配:否则将内置图标表的所有键按长度降序排序,遍历查找第一个被标题"包含"(大小写不敏感)的键并返回对应图标。长度降序保证了像 package.jsontsconfig.json 这类完整文件名的键优先于 .json 这类扩展名键命中,避免误匹配。

内置图标表完整定义在 TitleIcon.vuebuiltinIcons 中,覆盖包管理器、框架、打包器、常见配置文件与文件扩展名:

const builtinIcons = {
  // package managers
  'pnpm': 'i-vscode-icons:file-type-light-pnpm',
  'npm': 'i-vscode-icons:file-type-npm',
  'yarn': 'i-vscode-icons:file-type-yarn',
  'bun': 'i-vscode-icons:file-type-bun',
  'deno': 'i-vscode-icons:file-type-deno',
  // frameworks
  'vue': 'i-vscode-icons:file-type-vue',
  'svelte': 'i-vscode-icons:file-type-svelte',
  'angular': 'i-vscode-icons:file-type-angular',
  'react': 'i-vscode-icons:file-type-reactjs',
  'next': 'i-vscode-icons:file-type-light-next',
  'nuxt': 'i-vscode-icons:file-type-nuxt',
  'solid': 'logos:solidjs-icon',
  'astro': 'i-vscode-icons:file-type-light-astro',
  // bundlers
  'rollup': 'i-vscode-icons:file-type-rollup',
  'webpack': 'i-vscode-icons:file-type-webpack',
  'vite': 'i-vscode-icons:file-type-vite',
  'esbuild': 'i-vscode-icons:file-type-esbuild',
  // configuration files
  'package.json': 'i-vscode-icons:file-type-node',
  'tsconfig.json': 'i-vscode-icons:file-type-tsconfig',
  '.npmrc': 'i-vscode-icons:file-type-npm',
  '.editorconfig': 'i-vscode-icons:file-type-editorconfig',
  '.eslintrc': 'i-vscode-icons:file-type-eslint',
  '.eslintignore': 'i-vscode-icons:file-type-eslint',
  'eslint.config': 'i-vscode-icons:file-type-eslint',
  '.gitignore': 'i-vscode-icons:file-type-git',
  '.gitattributes': 'i-vscode-icons:file-type-git',
  '.env': 'i-vscode-icons:file-type-dotenv',
  '.env.example': 'i-vscode-icons:file-type-dotenv',
  '.vscode': 'i-vscode-icons:file-type-vscode',
  'tailwind.config': 'vscode-icons:file-type-tailwind',
  'uno.config': 'i-vscode-icons:file-type-unocss',
  'unocss.config': 'i-vscode-icons:file-type-unocss',
  '.oxlintrc': 'i-vscode-icons:file-type-oxlint',
  'vue.config': 'i-vscode-icons:file-type-vueconfig',
  // filename extensions
  '.mts': 'i-vscode-icons:file-type-typescript',
  '.cts': 'i-vscode-icons:file-type-typescript',
  '.ts': 'i-vscode-icons:file-type-typescript',
  '.tsx': 'i-vscode-icons:file-type-typescript',
  '.mjs': 'i-vscode-icons:file-type-js',
  '.cjs': 'i-vscode-icons:file-type-js',
  '.json': 'i-vscode-icons:file-type-json',
  '.js': 'i-vscode-icons:file-type-js',
  '.jsx': 'i-vscode-icons:file-type-js',
  '.md': 'i-vscode-icons:file-type-markdown',
  '.py': 'i-vscode-icons:file-type-python',
  '.ico': 'i-vscode-icons:file-type-favicon',
  '.html': 'i-vscode-icons:file-type-html',
  '.css': 'i-vscode-icons:file-type-css',
  '.scss': 'i-vscode-icons:file-type-scss',
  '.yml': 'i-vscode-icons:file-type-light-yaml',
  '.yaml': 'i-vscode-icons:file-type-light-yaml',
  '.php': 'i-vscode-icons:file-type-php',
}

其中绝大多数键值来自 vscode-icons 图标集合。从源码结构看,该组件默认依赖 @iconify-json/vscode-icons 提供的图标数据(以及 logos 集合中的 Solid 图标),因此要实际看到这些内置图标,需要按官方说明在项目中安装 @iconify-json/vscode-icons(即执行 npm add @iconify-json/vscode-icons 等对应包管理器的安装命令)。与文档列表相比,当前仓库的 TitleIcon.vue 实现中还额外内置了 .svg 扩展名到 i-vscode-icons:file-type-svg 的映射,以仓库源码为准。

匹配结果最终通过 UnoCSS 的图标类名生效:TitleIcon 的模板仅渲染一个 <div>,其 :class 绑定为匹配到的图标类(如 i-vscode-icons:file-type-npm),未匹配到标题时不渲染任何内容。

自定义图标:~icon~ 语法

内置表覆盖不到的品牌或场景,可以使用任意 Iconify 集合中的图标,写法是在标题里用 ~图标类名~ 包裹,例如:

```js [npm ~i-uil:github~]
console.log('Hello, GitHub!')
```

此时标题为 npm,但标签上显示的图标是 i-uil:github 指定的图标,而不是内置表匹配到的 npm 图标——~icon~ 优先级高于内置匹配。

要让自定义图标正常工作,需要做两步:

  1. 安装图标所属的 Iconify 集合。例如使用 uil 集合时:

:::code-group

npm add @iconify-json/uil
yarn add @iconify-json/uil
pnpm add @iconify-json/uil
bun add @iconify-json/uil

:::

  1. 将图标类名加入 uno.config.tssafelist,确保该类名会被 UnoCSS 收集生成:
import { defineConfig } from 'unocss'

export default defineConfig({
  safelist: [
    'i-uil:github',
  ],
})

如果不写入 safelist,动态拼接的图标类名可能不会进入最终的样式产物,图标将不显示。参考 UnoCSS 配置文档可了解 uno.config.ts 的其余可选项。

小结

Code Groups 的核心脉络可以概括为三条线索:

  • 语法层:comark: true 开启 Comark 后,::code-group / ::: 指令把若干带 [标题] 的代码块组合成标签页;
  • 渲染层:CodeGroup.vue 读取各块的 data-title 生成标签并通过 provide('activeTitle')CodeBlockWrapper.vue 协作完成激活态切换与标题去重;
  • 图标层:TitleIcon.vue 先解析 ~icon~ 显式图标,再按"长度降序、大小写不敏感的包含匹配"命中内置图标表,从而让 npmpnpmvitepackage.json 等标题自动带上对应的品牌图标。

这一套机制同样服务于带标题的单个代码块与 Shiki Magic Move,因此在编写 Slidev 演示时,只要给代码块起一个规范的名字,就能获得一致的图标与视觉风格。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388