prettify代码高亮实战:三文件接入、样式覆盖与动态内容处理
发布时间:2026/10/6 10:00:27 锦皓数字建站

简介Prettify 是一套轻量级的网页代码高亮方案面向需要在博客、文档或技术站点中展示源码的前端开发者与内容维护者帮助解决代码块缺乏视觉层次、阅读困难的问题。压缩包共 3 个文件以 2 个 JavaScript 脚本和 1 个 CSS 样式表为主整体仅 14KB样式表负责定义关键字、注释、字符串等元素的配色与字体规则脚本负责识别 HTML、CSS、JavaScript、Python、Java、C 等常见语言并自动套用高亮压缩版脚本则在保留功能的前提下减小体积、加快加载。目前已有 841 人学习下载。引入样式表与脚本后只需为代码块添加约定类名即可生效无需逐语言手写规则适合快速集成到现有页面也便于按需调整主题配色与展示效果。1. 三行代码让代码块“活”过来prettify 这套老牌高亮方案到底值不值得用如果你维护过技术博客、后台管理系统的文档页或者给客户交付过带代码示例的静态站点大概率遇到过这种尴尬代码贴上去就是一片黑白缩进全靠空格硬撑读者复制前得先眯着眼找边界。更别提团队里有人用 Tab、有人用空格粘到页面上直接错位。prettify 就是冲着这类场景来的——它是一套纯前端代码高亮方案核心文件只有三个prettify.css负责配色prettify.js负责词法解析和着色run_prettify.min.js则是压缩后的自动加载入口。它不依赖任何构建工具不挑后端语言扔进 HTML 里就能跑。适合谁适合那些不想引入重型高亮库、不想配 Node 环境、只想在静态页面或老项目里快速把代码块变好看的人。但它的边界也很清楚它是“够用”而不是“全能”语言支持靠内置规则冷门语法可能识别不准。下面我按实际拆包和复现的顺序把选型理由、接入步骤、参数调法和踩坑记录一次讲透。2. prettify 三个文件各管什么从加载顺序到词法着色链路2.1 prettify.css、prettify.js、run_prettify.min.js 的职责边界先把这三个文件的关系理清楚不然后面调样式会一头雾水。prettify.css是纯样式表它定义的是 token 级别的颜色比如.pln普通文本、.kwd关键字、.str字符串、.com注释、.typ类型名。这些类名是 prettify.js 在解析时动态加到span上的所以样式表必须和 JS 配套使用单独引 CSS 没有任何效果。prettify.js是完整未压缩的解析器内部维护了一套基于正则的词法规则表按语言扩展名或lang-*类名去匹配。它的工作流程是页面加载后扫描所有pre标签判断是否带prettyprint类然后逐行做 token 切分最后把每个 token 包进带类名的 span。run_prettify.min.js是压缩版同时它内置了一个自动加载逻辑——如果你在 script 标签上加了autoload(function(){...})这种参数它会按需去拉对应语言的扩展规则而不是一次性加载全部语言定义。常见做法是小项目直接引prettify.js加prettify.css页面底部调一次PR.prettyPrint()如果页面代码块语言杂、又不想手动引一堆扩展就用run_prettify.min.js配 autoload。我一般会先看项目里代码块的语言种类三种以内就手动引超过五种再考虑 autoload因为自动加载会多几次请求内网环境反而慢。2.2 最小可运行接入一个 HTML 文件跑通高亮下面这段是能直接存成.html双击打开的最小示例不依赖任何 CDN 之外的资源。注意 script 的加载位置和prettyprint类的写法。!DOCTYPE html html langzh-CN head meta charsetUTF-8 !-- 1. 先引样式保证解析出的 span 有颜色可上 -- link relstylesheet href./prettify.css /head body !-- 2. pre 必须带 prettyprint 类否则扫描时会被跳过 -- pre classprettyprint lang-js function add(a, b) { // 这是注释应显示为灰色 return a b; } /pre !-- 3. 引解析脚本放在 body 末尾避免阻塞 -- script src./prettify.js/script script // 4. 手动触发一次DOM 已就绪 window.addEventListener(load, function () { PR.prettyPrint(); }); /script /body /html逻辑说明prettify.js加载后会在全局挂一个PR对象PR.prettyPrint()是入口函数它遍历document.getElementsByTagName(pre)只处理带prettyprint类的元素。参数说明lang-js是语言提示类告诉解析器按 JavaScript 规则切词如果不写它会走默认的通用规则关键字识别会弱很多。window load是为了确保样式和脚本都到位再解析避免闪一下无样式。跑通后你会看到关键字变蓝、字符串变绿、注释变灰这就是最基础的着色链路。2.3 语言类名与 autoload 参数怎么配prettify 支持的语言通过lang-*类名声明常见的有lang-js、lang-css、lang-html、lang-python、lang-sql、lang-java、lang-bash。如果你用run_prettify.min.js可以在 script 标签上写autoload参数让它按页面实际出现的语言去加载扩展。写法如下script src./run_prettify.min.js?autoload(function(){var q[];return function(n){q.push(n)}})()langjs,css,html/script这段参数的含义是autoload传入一个收集函数lang列出本页需要的语言脚本会去同目录下找lang-js.js这类扩展文件。注意扩展文件必须和run_prettify.min.js放在同一目录否则 404。我一般会先手动引prettify.js跑通确认样式没问题后再换 autoload这样出问题能快速定位是加载逻辑还是解析规则的问题。3. 把 prettify 接进真实项目样式覆盖、行号与动态内容处理3.1 覆盖 prettify.css 配色的正确姿势prettify.css默认配色偏浅放在深色主题站点里会刺眼。直接改源文件不是好习惯因为升级时会丢。常见做法是新建一个prettify-override.css在prettify.css之后引入用更高优先级的选择器覆盖。比如把关键字改成橙色、背景改成深灰/* 覆盖默认 token 颜色选择器加 pre 提升优先级 */ pre.prettyprint { background: #1e1e1e; color: #d4d4d4; padding: 12px; border-radius: 6px; } pre.prettyprint .kwd { color: #ff9d00; } /* 关键字 */ pre.prettyprint .str { color: #7ec699; } /* 字符串 */ pre.prettyprint .com { color: #6a737d; } /* 注释 */ pre.prettyprint .typ { color: #66d9ef; } /* 类型 */逻辑说明prettify 生成的 span 类名是固定的覆盖时只要保证选择器权重不低于原样式即可。参数说明.kwd对应关键字.str对应字符串.com对应注释.typ对应类型名.lit对应字面量。注意不要用!important满天飞靠pre.prettyprint前缀就能压过默认的单类选择器。改完刷新页面如果颜色没变先看覆盖文件是不是在prettify.css之后引入再看浏览器里 span 的类名是不是你预期的那个。3.2 加行号用 CSS 计数器还是插件prettify 本身不带行号这是很多人第一次用就发现的缺口。两种补法一是纯 CSS 计数器二是用linenums类让 prettify 自己生成行号。后者更省事写法是在pre上加linenumspre classprettyprint linenums lang-python def greet(name): return fhello {name} /pre逻辑说明linenums会让 prettify 在每行前插入一个带nocode类的 span里面是行号。参数说明默认从 1 开始如果想从指定数字起可以写linenums:10。注意行号是靠 CSS 的list-style或浮动实现的如果站点全局样式重置了ol或li行号可能错位。我一般会在覆盖样式里补一句pre.prettyprint ol.linenums { margin-left: 2.5em; }把缩进拉回来。纯 CSS 计数器方案适合不想让 prettify 改 DOM 的场景但复制代码时会连行号一起复制体验差所以更推荐linenums。3.3 动态插入的代码块怎么重新高亮后台系统里代码块常常是 AJAX 拉回来再塞进 DOM 的这时候页面初始的PR.prettyPrint()已经跑完了新内容不会被处理。血泪经验是每次插入新节点后手动对那个节点再调一次解析。prettify 提供了PR.prettyPrintOne方法可以只处理一段字符串// 假设从接口拿到一段代码文本 fetch(/api/snippet).then(r r.text()).then(code { const pre document.createElement(pre); pre.className prettyprint lang-js; // prettyPrintOne 返回高亮后的 HTML 字符串 pre.innerHTML PR.prettyPrintOne(code, js, true); document.getElementById(code-box).appendChild(pre); });逻辑说明prettyPrintOne(sourceCode, language, showLineNumbers)三个参数分别是源码字符串、语言标识、是否显示行号。它不依赖 DOM 里已有的 pre直接返回处理好的 HTML。参数说明语言标识写js而不是lang-js这是方法内部约定。注意如果代码里有 HTML 特殊字符prettyPrintOne会做转义不用自己再转一遍否则会显示成实体。动态场景下别反复对整个 document 调prettyPrint()那样会重复解析已有节点性能差还可能把已生成的 span 再包一层。4. 避坑与排查prettify 接入后不生效的五个真实原因4.1 代码块没变色控制台也没报错现象页面正常加载pre里的代码还是黑白Network 里prettify.js状态 200。原因最常见的是pre标签漏了prettyprint类或者类名拼成了pretty-print。prettify 的扫描条件是className里包含prettyprint少一个字母就跳过。解决打开 Elements 面板确认pre的 class 列表补上prettyprint如果用的是模板引擎检查它有没有把 class 属性过滤掉。4.2 样式加载了但颜色全一样现象span 已经生成但所有 token 都是同一个颜色。原因prettify.css没引成功或者被站点全局样式覆盖了。有些 CSS 框架会给span设color: inherit权重比 prettify 的单类选择器高。解决在 Network 里确认prettify.css返回 200 且内容非空在 Elements 里选中一个 span看 Computed 面板里 color 的来源如果是框架样式就在覆盖文件里用pre.prettyprint .kwd这种带前缀的选择器提权。4.3 autoload 模式下语言扩展 404现象控制台报lang-python.js404代码块只有部分着色。原因run_prettify.min.js的 autoload 会按lang参数去同目录找扩展文件但很多下载包里只给了主文件没带lang-*.js。解决要么把需要的语言扩展文件一起放到同目录要么放弃 autoload改回手动引prettify.js它内置了常见语言的规则不需要额外文件。我一般在内网项目里直接用手动模式少一次请求少一个故障点。4.4 行号与代码错位、复制带行号现象加了linenums后行号和代码不在同一行或者用户复制时把行号也复制走了。原因行号是插入到pre内部的 span复制时自然会被选中错位通常是站点全局line-height和 prettify 默认值不一致。解决在覆盖样式里统一pre.prettyprint, pre.prettyprint li { line-height: 1.5; }如果复制体验要求高就改用纯 CSS 计数器方案把行号放在::before伪元素里伪元素内容不会被复制。4.5 代码里有 HTML 标签导致解析中断现象代码块里写了div这类标签页面上直接渲染成了真实元素后面的代码全乱。原因pre里的没转义浏览器当成标签解析了。解决在服务端或模板里对代码内容做 HTML 实体转义把转成lt;、转成gt;、转成amp;。如果用的是prettyPrintOne它内部会转义但直接写在 HTML 里的pre不会。这是最容易被忽略的一条尤其是文档里贴 HTML 示例的时候。5. 进阶技巧用 prettyPrintOne 做按需高亮与主题切换把 prettify 用顺之后可以再往前走一步不依赖页面里预置的pre而是把它当成一个“代码转 HTML”的工具函数来用。这个思路在需要频繁切换代码示例、或者做在线编辑器预览的场景里特别省事。核心就是PR.prettyPrintOne它接收原始代码字符串返回带 span 的 HTML你可以塞进任何容器。先看一个按语言切换的完整例子。假设页面上有一个下拉框选语言一个按钮触发高亮// 缓存原始代码避免多次高亮后 span 嵌套 const rawCode document.getElementById(raw).textContent; document.getElementById(render).addEventListener(click, function () { const lang document.getElementById(lang-select).value; // js / css / html const box document.getElementById(output); // 第三个参数 true 表示显示行号 box.innerHTML PR.prettyPrintOne(rawCode, lang, true); // 切换后重新应用覆盖样式里的 pre 类 box.querySelector(pre).classList.add(prettyprint); });逻辑说明rawCode只取一次因为一旦高亮过DOM 里就是带 span 的结构再取textContent虽然能拿到纯文本但多次操作容易把转义字符搞乱。参数说明lang直接传js、css、html不要带lang-前缀第三个参数控制行号如果不需要就传false。注意prettyPrintOne返回的是完整pre结构所以容器里不要再套一层pre否则会出现嵌套的代码块。再进一步可以做主题切换。因为颜色全在 CSS 里只要准备两套覆盖样式切换时换link的href或者切换根元素上的类名即可。我一般会在html标签上挂>/* 浅色主题 */ html[data-themelight] pre.prettyprint { background: #f6f8fa; color: #24292e; } html[data-themelight] pre.prettyprint .kwd { color: #d73a49; } /* 深色主题 */ html[data-themedark] pre.prettyprint { background: #1e1e1e; color: #d4d4d4; } html[data-themedark] pre.prettyprint .kwd { color: #ff9d00; }逻辑说明主题切换只动 CSS不动 JS因为 span 的类名是稳定的。参数说明style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。