☰
Google Code Prettify 代码高亮实战:三文件引入、动态调用与避坑指南
2026/10/6 17:18:36 网站建设 项目流程

简介:Prettify 代码高亮工具包面向需要在网页、博客或技术文档中展示源代码的前端开发者与内容创作者,解决代码块缺乏视觉层次、可读性差的问题。压缩包共 3 个文件,包含 2 个 JavaScript 脚本与 1 个 CSS 样式表,整体约 14KB,体积轻量、便于集成。其中样式表负责定义关键字、注释、字符串等语法元素的配色与字体规则,脚本文件则遍历页面中的预格式化与代码标签,自动识别 HTML、CSS、JavaScript、Python、Java、C++ 等常见语言并套用对应高亮样式,压缩版本在保留功能的同时减少带宽占用、加快页面加载。目前已有 841 人学习下载,适合希望低成本实现专业代码展示效果的读者参考使用。

1. 三个文件就能跑:prettify 代码高亮到底解决了什么问题

接手一个老后台项目时,产品经理丢来一句“代码展示区太丑了,能不能像 IDE 那样有颜色”。翻开源码一看,页面里<pre>标签包着一堆灰扑扑的代码,没有行号、没有关键字着色,连字符串和注释都分不清。这种场景下,引入 Google Code Prettify 是最省事的方案——它只需要三个文件:prettify.css负责配色,prettify.js负责词法解析和着色,run_prettify.min.js负责自动加载和初始化。整套东西不依赖构建工具,不挑框架,直接丢进静态目录就能用。

这篇文章面向的是需要在博客、文档站、后台管理系统的代码展示区快速加上高亮的开发者。不管你是用原生 HTML 还是 Vue、React 这类框架,只要页面最终渲染出<pre>或<code>标签,prettify 就能接管。我会把三个文件各自的职责、引入顺序、参数配置、自动加载机制以及实际踩过的坑讲清楚,让你拿到就能复现,不用再去翻零散的英文文档。

2. prettify 三件套的分工与最小引入路径

2.1 prettify.css、prettify.js、run_prettify.min.js 各自管什么

很多人第一次看到这三个文件会懵:为什么不能合并成一个?其实它们的分工非常明确,理解之后你就能判断什么时候该用哪个、什么时候可以省掉哪个。

prettify.css是纯样式表,里面定义了.pln(普通文本)、.kwd(关键字)、.str(字符串)、.com(注释)、.typ(类型)、.lit(字面量)、.pun(标点)、.tag(标签)、.atn(属性名)、.atv(属性值)等 token 类的颜色和字重。它不参与任何解析逻辑,只负责“长什么样”。你可以直接改这个文件来换配色,也可以自己写一份覆盖它。

prettify.js是核心。它内部维护了一套基于正则的词法规则,按语言扩展名(如lang-css、lang-js、lang-python)去匹配代码文本,把每个片段打上对应的 token 类名,然后替换 DOM 内容。它暴露了全局对象PR,提供PR.prettyPrint()方法用于手动触发着色。这个文件是必须引入的,没有它就没有解析能力。

run_prettify.min.js是自动加载器。它的作用是:扫描页面里所有<pre>和<code>标签,根据class属性判断语言,然后动态加载对应的语言扩展脚本(如果需要的话),最后调用PR.prettyPrint()。它适合“我不想写一行 JS 初始化代码”的场景。如果你已经在业务代码里手动调用了PR.prettyPrint(),这个文件可以不加。

三者的关系可以这样理解:prettify.js是发动机,prettify.css是车漆,run_prettify.min.js是自动点火装置。你可以手动点火,也可以让它自动点。

2.2 最小可运行页面的引入顺序与代码

下面是一个不依赖任何框架的最小示例。注意引入顺序:CSS 放<head>,JS 放</body>前,run_prettify.min.js必须在prettify.js之后。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <!-- 1. 先引入样式,保证着色类名有对应颜色 --> <link rel="stylesheet" href="./prettify.css"> </head> <body> <!-- 2. 代码块用 pre 包裹,class 里写 prettyprint 触发自动着色 --> <pre class="prettyprint lang-js"> function greet(name) { // 这是一个注释 const msg = "Hello, " + name; return msg; } </pre> <!-- 3. 先加载核心解析库 --> <script src="./prettify.js"></script> <!-- 4. 再加载自动运行脚本,它会扫描页面并调用 PR.prettyPrint() --> <script src="./run_prettify.min.js"></script> </body> </html>

这段代码里最关键的是<pre class="prettyprint lang-js">。prettyprint这个类名是run_prettify.min.js识别代码块的标记,没有它就不会被处理。lang-js告诉解析器按 JavaScript 规则着色,如果不写,prettify 会尝试自动猜测,但猜测准确率有限,尤其是混合了 HTML 和 JS 的模板代码。

run_prettify.min.js在 DOMContentLoaded 之后执行,它会遍历所有带prettyprint类的元素,逐个调用PR.prettyPrint()。如果你在框架里动态插入代码块,自动脚本不会再次触发,需要手动调用。

2.3 手动调用 PR.prettyPrint 的时机与参数

在 Vue、React 这类框架里,代码块往往是异步渲染的。run_prettify.min.js只在页面首次加载时跑一次,后续新增的<pre>不会被着色。这时候应该去掉run_prettify.min.js,改为在组件挂载或数据更新后手动调用。

// 假设代码块已经插入 DOM // 手动触发 prettify 解析 if (window.PR && typeof window.PR.prettyPrint === 'function') { // 不传参数:扫描全文档 window.PR.prettyPrint(); // 也可以只处理某个容器,减少不必要的遍历 // window.PR.prettyPrint(null, document.getElementById('code-area')); }

PR.prettyPrint()接受两个可选参数:第一个是回调函数,每个代码块处理完后调用;第二个是根节点,限定扫描范围。在单页应用里,我一般会传根节点,避免每次更新都全文档扫描,代码块多的时候能明显减少卡顿。

还有一个细节:PR.prettyPrint()会给已经处理过的元素加上prettyprinted类,重复调用不会重复着色。但如果你替换了<pre>内部的 HTML 内容,需要先移除这个类,否则会被跳过。

3. 语言扩展、行号与主题定制的落地做法

3.1 lang- 类名与自动加载语言脚本的对应关系

prettify 默认只内置了少量常见语言的解析规则,比如 C、Java、Python、Bash、HTML、XML、CSS、JavaScript。对于 SQL、Go、Rust、TypeScript 这些,需要额外加载对应的扩展脚本。run_prettify.min.js支持通过lang-类名自动去加载扩展,但前提是你把扩展文件放在了它预期的路径下。

常见做法是:在prettify.js同级目录建一个lang-xxx.js文件,然后在<pre>的 class 里写lang-sql。run_prettify.min.js会检测到lang-sql,动态插入<script src="lang-sql.js">。如果路径不对,控制台会报 404,代码块保持灰色。

类名写法对应扩展文件适用场景
lang-js内置,无需额外文件JavaScript、JSON
lang-python内置Python
lang-sqllang-sql.jsSQL 查询语句
lang-golang-go.jsGo 代码
lang-tslang-ts.jsTypeScript
lang-rustlang-rust.jsRust 代码

如果你不想依赖自动加载,也可以手动在页面里引入扩展脚本,顺序放在prettify.js之后、run_prettify.min.js之前。这样即使自动加载路径配错了,扩展规则也已经注册到PR对象里了。

3.2 给代码块加行号的两种方案与取舍

prettify 本身不提供行号功能,这是很多人第一次用时的预期落差。常见做法有两种:CSS 计数器和 JavaScript 插入。

CSS 方案利用counter-increment和::before伪元素,不改 DOM,性能好,但复制代码时会把行号一起复制进去。

/* 给代码块加行号,纯 CSS 方案 */ pre.prettyprint { counter-reset: line; padding-left: 3.5em; position: relative; } pre.prettyprint code { display: block; white-space: pre; } /* 每一行用 span 包裹后才能计数,prettify 默认不包 span */ /* 所以纯 CSS 方案需要配合下面的 JS 分行 */

纯 CSS 方案有个硬伤:prettify 输出的 HTML 里,换行是文本节点,没有逐行包裹元素,counter-increment找不到计数对象。所以实际项目中,我一般用 JavaScript 在PR.prettyPrint()的回调里逐行包裹<span class="line">,再用 CSS 计数。

// 在 prettify 完成后,把代码块内容按行拆分并包裹 function addLineNumbers(preElement) { const code = preElement.querySelector('code') || preElement; const lines = code.innerHTML.split('\n'); // 最后一行如果是空的,去掉,避免多出一个行号 if (lines[lines.length - 1].trim() === '') lines.pop(); code.innerHTML = lines .map(line => '<span class="line">' + (line || ' ') + '</span>') .join('\n'); } // 手动调用时传入回调 window.PR.prettyPrint(function () { document.querySelectorAll('pre.prettyprint').forEach(addLineNumbers); });

这个方案的好处是行号在复制时不会被选中(可以用user-select: none控制),缺点是代码块特别长时,字符串拼接会有性能开销。超过 500 行的代码块,建议分页或折叠,不要一次性渲染。

3.3 换主题:改 CSS 变量还是直接覆盖 token 类

prettify 的默认主题偏浅色,关键字是深蓝,字符串是暗红。如果你想要暗色主题,最直接的方式是覆盖prettify.css里的 token 类颜色。但直接改原文件不利于升级,更好的做法是新建一个prettify-dark.css,在prettify.css之后引入。

/* prettify-dark.css 覆盖默认配色 */ pre.prettyprint { background: #1e1e1e; color: #d4d4d4; border: 1px solid #333; border-radius: 4px; padding: 12px; } pre.prettyprint .kwd { color: #569cd6; font-weight: normal; } pre.prettyprint .str { color: #ce9178; } pre.prettyprint .com { color: #6a9955; font-style: italic; } pre.prettyprint .typ { color: #4ec9b0; } pre.prettyprint .lit { color: #b5cea8; } pre.prettyprint .pun { color: #d4d4d4; } pre.prettyprint .tag { color: #569cd6; } pre.prettyprint .atn { color: #9cdcfe; } pre.prettyprint .atv { color: #ce9178; }

覆盖时要注意选择器权重。prettify 默认用的是.pln、.kwd这类单类选择器,你用pre.prettyprint .kwd权重更高,能稳定覆盖。不要用!important,否则后续想微调会很痛苦。

另外,暗色主题下行号的颜色也要单独设,否则默认黑色在深色背景上看不见。行号的 CSS 类名取决于你上面用的包裹方案,如果是.line,就加一条pre.prettyprint .line::before { color: #858585; }。

4. 避坑与排查:prettify 接入后最常见的五类问题

4.1 代码块有背景但没颜色,token 类名没生成

现象:<pre>有了背景色和内边距,但关键字、字符串全是同一种颜色,右键检查元素发现内部只有纯文本,没有<span class="kwd">这类标签。

原因:PR.prettyPrint()没有被调用,或者调用时prettify.js还没加载完。run_prettify.min.js依赖prettify.js先执行,如果两个脚本用了async或顺序颠倒,自动运行脚本会找不到PR对象。

解决:检查<script>标签顺序,确保prettify.js在前。如果用了模块打包工具,确认prettify.js是同步加载或在使用前已经完成初始化。手动调用时加一个window.PR存在性判断。

4.2 动态插入的代码块不生效,刷新页面才行

现象:在 Vue 或 React 里,通过接口获取代码内容后渲染到<pre>,页面显示的是灰色文本,但手动刷新后正常。

原因:run_prettify.min.js只在首次 DOMContentLoaded 时扫描一次,后续异步渲染的代码块不在它的处理范围内。

解决:去掉run_prettify.min.js,在数据更新后的nextTick或useEffect里手动调用PR.prettyPrint()。如果代码块在弹窗或折叠面板里,要等 DOM 可见后再调用,否则某些浏览器下计算样式会异常。

4.3 行号与代码错位,长行换行后行号对不上

现象:代码里有一行特别长,自动换行后占了视觉上的两行,但行号只加了一个,导致后续行号整体偏移。

原因:行号方案基于\n拆分,而 CSS 的white-space: pre-wrap会让长行在视觉上折行,但 DOM 里仍然是一个.line元素。

解决:给pre.prettyprint设置overflow-x: auto和white-space: pre,让长行横向滚动而不是折行。这样行号和视觉行严格一一对应。如果产品要求必须折行,那就放弃行号,或者改用white-space: pre-wrap配合word-break: break-all,但行号只能按逻辑行显示,需要跟产品说明这个限制。

4.4 lang- 类名写了但语言没高亮,控制台报 404

现象:<pre class="prettyprint lang-sql">里的 SQL 语句没有按 SQL 规则着色,控制台出现GET ./lang-sql.js 404。

原因:run_prettify.min.js自动加载扩展时,路径是相对于当前页面 URL 拼接的,不是相对于prettify.js所在目录。如果你的页面路由是/article/123,它会去/article/lang-sql.js找,而不是/static/js/lang-sql.js。

解决:手动在页面里引入扩展脚本,不要依赖自动加载。或者在使用run_prettify.min.js之前,设置window.PR_SHOULD_USE_CONTINUATION或相关路径变量(不同版本变量名有差异,建议直接手动引入更可控)。

4.5 代码里的 HTML 标签被浏览器解析,页面结构错乱

现象:代码块里写<div class="test">,渲染出来真的变成了一个 div 元素,而不是显示文本。

原因:<pre>里的<没有转义成&lt;,浏览器把它当成了 HTML 标签。

解决:后端返回代码内容时做 HTML 实体转义,或者前端在插入前用textContent赋值而不是innerHTML。如果代码是静态写在页面里的,手动把<写成&lt;、>写成&gt;、&写成&amp;。prettify 解析的是转义后的文本,不会二次转义。

5. 让 prettify 在真实项目里跑得更稳的几个习惯

用 prettify 这几年,我最大的体会是:它足够简单,但简单的东西更容易在边界场景翻车。下面这几个习惯帮我省了很多返工时间。

第一,永远不要依赖run_prettify.min.js的自动加载。在稍微正式一点的项目里,我都会去掉它,改为在业务代码里显式调用PR.prettyPrint()。这样时机可控,动态内容不会漏,扩展语言也直接手动引入,不担心路径问题。自动加载适合 demo 和静态页,不适合有路由和异步数据的应用。

第二,代码块的内容一定要转义。我见过太多因为代码里带<导致页面布局崩掉的案例。后端返回时转义是最省事的,前端拿到后直接innerHTML塞进去。如果后端不方便改,前端用document.createTextNode或者textContent赋值,再手动调 prettify。

第三,行号不是必须的。如果代码块普遍不超过 30 行,行号带来的视觉收益有限,反而增加复制时的干扰。我现在的做法是:超过 20 行的代码块才加行号,短代码块保持干净。行号的 CSS 里加user-select: none,避免用户复制时把行号带进剪贴板。

第四,暗色主题下记得检查对比度。prettify 默认的注释色#6a9955在深色背景上对比度偏低,长时间阅读容易累。我一般会把它调亮到#7ec699左右,字符串色也适当提亮。这个没有标准答案,用浏览器开发者工具实时调,调到眼睛舒服为止。

第五,升级 prettify 版本时先跑一遍回归。不同版本的 token 类名可能有增减,CSS 覆盖文件要跟着检查。我习惯在项目里保留一份prettify.css的原始副本,升级后 diff 一下,看看有没有新增的 token 类需要补颜色。

最后说一个我自己的习惯:每次接入 prettify,我都会先写一个包含 10 种语言代码块的测试页面,把所有lang-类名跑一遍,确认扩展加载正常、颜色正确、行号对齐。这个页面留在项目里,后续换主题或升级版本时直接打开看一眼,比在业务页面里逐个排查快得多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询