一、为什么选择Prism.js?——轻量、易用与生态优势

在为技术博客、文档站或开发者平台选型代码高亮方案时,你很可能已在 Highlight.jsShikiPrism.js 之间犹豫。而越来越多的现代静态站点(Hugo/Jekyll/VitePress)和框架应用(Vue 3、React 18+、SvelteKit)正将 Prism.js 作为默认首选——这不是偶然。

Prism 的核心竞争力在于「精准克制」:它零运行时依赖(纯原生 JS),无构建强制要求(CDN 开箱即用),且语言与插件完全解耦。对比 Highlight.js(需手动注册语言、CSS 主题强耦合、SSR 友好性较弱),Prism 的模块化设计天然适配按需加载:你只需引入 prism-core.js + prism-javascript.js + prism-coy.css,就能获得一个仅 12KB(gzip)的 JS 高亮内核。

更关键的是,Prism 对 Markdown 生态的原生友好性极强。主流渲染器(Remark/Rehype、VitePress 的 @mdx-js/mdx、Hugo 的 Goldmark)默认输出符合 Prism 规范的 HTML 结构:

<pre><code class="language-js">console.log('Hello Prism!');</code></pre>

无需额外配置解析器或自定义语法树转换——这为后续集成省去了大量胶水代码。同时,其 CSS-in-JS 友好特性(如通过 Prism.plugins.NormalizeWhitespace 处理缩进、支持 data-language 属性驱动)也让它轻松融入 Tailwind、UnoCSS 或 CSS Modules 工程体系。

Prism 与主流工具链集成示意图

💡 小贴士:Prism 官方提供 下载定制器,可勾选所需语言/插件后一键生成精简版 CDN 链接——这是避免“全量打包却只用 JS”的第一道防线。

二、三步完成基础集成——CDN引入 + 自动高亮

无需构建工具,3 分钟即可让代码块焕然一新:

✅ 步骤1:引入精简版 CDN

访问 prismjs.com/download,勾选 JavaScriptCSSLine Numbers(可选)及主题(如 PrismNight Owl),复制生成的 <link><script>

<link href="https://cdn.jsdelivr.net/npm/[email protected]/themes/prism.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/components/prism-core.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/plugins/autoloader/prism-autoloader.min.js"></script>
<!-- 按需加载核心语言 -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/components/prism-javascript.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/components/prism-css.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/components/prism-html.min.js"></script>

⚠️ 注意:JS 与 CSS 版本号必须严格一致(如均为 1.29.0),否则 token 类名可能不匹配导致样式失效。

✅ 步骤2:确保 HTML 结构合规

Prism 默认识别 <code class="language-xxx">。Markdown 渲染后应生成:

<pre><code class="language-js">function greet() { return 'Hi!'; }</code></pre>

若渲染器未自动添加 language- 前缀(如某些旧版 Hugo),可手动加 data-language="js"

<pre><code data-language="js">...</code></pre>

✅ 步骤3:初始化高亮

</body> 前添加脚本(含 DOM 加载安全检查):

<script>
  document.addEventListener('DOMContentLoaded', () => {
    // 避免重复执行(尤其 SPA 中)
    if (!window.Prism || !Prism.highlightAll) return;
    Prism.highlightAll();
  });
</script>

完整 HTML 示例:

<!DOCTYPE html>
<html>
<head>
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/themes/prism.min.css" rel="stylesheet">
</head>
<body>
  <pre><code class="language-js">console.log('It works!');</code></pre>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/components/prism-core.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/components/prism-javascript.min.js"></script>
  <script>
    document.addEventListener('DOMContentLoaded', () => Prism.highlightAll());
  </script>
</body>
</html>

三、按需加载语言与插件——减小包体积实战

当站点支持 15+ 语言时,全量加载会显著拖慢首屏。Prism 的 manual 模式是破局关键:

🔧 禁用自动高亮 + 精准控制

// 全局禁用自动执行
Prism.manual = true;

// 手动高亮指定元素
const codeBlock = document.querySelector('pre code');
if (codeBlock) Prism.highlightElement(codeBlock);

🌐 动态导入语言(ESM 示例)

// React 中的动态高亮 Hook
import { useEffect, useRef } from 'react';

export function usePrismHighlight(lang) {
  const ref = useRef(null);

  useEffect(() => {
    if (!ref.current) return;

    const loadAndHighlight = async () => {
      try {
        await import(`prismjs/components/prism-${lang}.js`);
        Prism.highlightElement(ref.current);
      } catch (err) {
        console.warn(`Failed to load Prism language: ${lang}`, err);
      }
    };

    loadAndHighlight();
  }, [lang]);

  return ref;
}

// 使用
function CodeBlock({ lang, children }) {
  const ref = usePrismHighlight(lang);
  return <pre><code ref={ref} className={`language-${lang}`}>{children}</code></pre>;
}

🧩 插件启用指南

  • autoloader:自动根据 class="language-toml" 加载 prism-toml.js(需配合 prism-autoloader.jsprismjs/plugins/autoloader/ 路径配置)
  • show-language:在代码块右上角显示语言标签(需额外 CSS)
  • copy-to-clipboard:添加复制按钮(需 HTTPS 环境)

⚠️ SSR 注意:Prism.highlightElement() 是浏览器 API,服务端渲染时需包裹 if (typeof window !== 'undefined') 判断。

四、定制主题——从 CSS 覆盖到主题生成器

不必从头写 CSS,三种渐进式定制方案:

方式1:CSS 变量覆盖(推荐!)

Prism v1.27+ 支持 CSS 自定义属性,适配深色模式:

:root {
  --prism-background: #f8f9fa;
  --prism-color: #212529;
  --prism-comment: #6c757d;
  --prism-keyword: #007bff;
}

@media (prefers-color-scheme: dark) {
  :root {
    --prism-background: #1e1e1e;
    --prism-color: #e0e0e0;
    --prism-comment: #6a9955;
    --prism-keyword: #569cd6;
  }
}

/* 必须保留 .token 层级 */
.token.comment { color: var(--prism-comment); }
.token.keyword { color: var(--prism-keyword); }
.token { background: var(--prism-background); color: var(--prism-color); }

方式2:主题生成器一键导出

访问 Prism Theme Generator,调整颜色后点击 Export CSS,替换原有 <link> 即可。

方式3:Sass 工程化重构

安装源码:npm install prismjs,在 Sass 中:

$prism-bg: #2d2d2d;
$prism-fg: #f8f8f2;
@import 'prismjs/themes/prism.scss';

⚠️ 避坑:禁用 !important;所有 .token.* 类必须显式声明;务必测试 punctuationoperatorregex 等边缘 token。

五、进阶技巧与避坑指南

🔗 行号与折叠插件兼容性

line-numbersdiff-highlight 同时启用时,需确保 line-numbersdiff-highlight 之后加载,否则行号错位:

<script src="prism-diff-highlight.js"></script>
<script src="prism-line-numbers.js"></script> <!-- 后加载 -->

⏱ 动态渲染时机问题

  • Vue:在 onUpdatednextTick 中调用 Prism.highlightElement
  • React:在 useEffect 中监听 codeRef.current 变化,避免重复高亮

🌐 SSR 首屏无高亮修复

服务端不执行 JS → 客户端 hydration 后补全:

// Next.js / Nuxt 中
if (typeof window !== 'undefined') {
  // 延迟到 hydration 后执行
  setTimeout(() => Prism.highlightAll(), 0);
}

❓常见问题速查

问题解决方案
代码显示为纯文本检查 <code> 是否有 language-xxx class;确认 Prism JS/CSS 加载顺序正确
TOML/MDX 不高亮显式引入 prism-toml.jsprism-mdx.js(非默认语言)
复制按钮无效确认启用 copy-to-clipboard 插件;检查是否在 HTTPS 环境;验证 navigator.clipboard.writeText 权限

六、验证与性能优化 checklist

手动测试清单

  • 主流语言:JS/TS、Python、HTML、CSS、Shell(含 $ 提示符)
  • 边界场景:4空格缩进、\n 换行、< > 转义、超长单行(测试 white-space: pre-wrap

Lighthouse 优化

<link 
  href="prism.min.css" 
  rel="stylesheet" 
  fetchpriority="low" 
  media="print" 
  onload="this.media='all'"
>

Bundle 分析验证
使用 rollup-plugin-visualizersource-map-explorer,确认 node_modules/prismjs/components/ 下仅存在已启用的语言文件。

一键检测脚本(粘贴至浏览器控制台):

console.log(
  '高亮完成率:',
  [...document.querySelectorAll('pre code')].filter(el => 
    el.classList.contains('language-js') && el.classList.contains('language-python')
  ).length,
  '/',
  document.querySelectorAll('pre code').length
);

🎯 最后建议:启用 Prism 构建时 --minify 选项(npx prismjs --languages=javascript,css,html --plugins=line-numbers --minify > prism.min.js),进一步压缩体积。

Prism 性能优化效果对比图
Prism 主题定制前后对比图

掌握这些技巧,你不仅能快速落地代码高亮,更能将其深度融入工程体系——轻量、可控、可维护。现在,就打开你的博客,给第一段 console.log() 加上色彩吧!