本文约 5,735 字,预计阅读 15 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:贴进去的代码,前台变了样
技术博客作者最常遇到的几个诡异现象:
- 代码里的双引号
"在前台变成了中文引号"",读者复制回去直接语法错误; - 三个连续的点
...变成了省略号…; <pre>里的换行被插入了一堆<br>和<p>,缩进全乱;- 手机上看代码块,一行撑破屏幕,横向滚动条把整个页面都带歪了;
- 贴了一段含
$_POST的 PHP,保存后再打开,变量消失了。
这些现象背后是 WordPress 的内容过滤链:内容从编辑器到前台,会依次经过 wptexturize、convert_smilies、wpautop、wp_filter_content_tags、do_shortcode 等一系列过滤器。它们对普通文本是好事(自动排版、表情、响应式图片),对代码却是灾难。
好消息是 WordPress 核心对 <pre>、<code>、<kbd>、<style>、<script> 这些标签有内置保护(这些标签内部的内容默认不参与 wptexturize)。坏消息是:一旦你用短代码、用旧编辑器、或主题自己加了过滤,保护就会失效。
原理分析:内容过滤链上有哪些”坑”
| 过滤函数 | 对代码的破坏 | 是否默认保护 <pre>/<code> |
|---|---|---|
wptexturize |
引号、省略号、破折号被替换 | 是(核心跳过这些标签内部) |
convert_smilies |
;) 8-) 变成表情图片 |
否(会扫到代码里) |
wpautop |
插入 <p>/<br> |
是(保护 <pre> 块) |
do_shortcode |
代码里的方括号被当短代码解析 | 否 |
wp_filter_content_tags |
给 <img> 加 srcset(一般无损) |
否,但影响不大 |
balanceTags |
未闭合标签被”修正” | 可能破坏示例 HTML |
除了过滤链,还有一层是主题样式。技术博客常见的样式破坏包括:
- 主题给
code设了background: transparent或圆角边框,导致行内代码和代码块看起来一样; - 主题给
pre设了white-space: normal,缩进被折叠成一行; - 主题的行高、字体系列不含等宽字体,中文注释与代码对不齐;
- 主题的
overflow: hidden让长行被截断,看不到后面的代码。
最后是安全与合规:代码里的真实密钥、服务器地址、账号信息一旦贴出来就是公开的。写教程时所有敏感值必须脱敏成占位符,这也是本站一贯的原则。
保姆级实操
第一步:优先用古腾堡的「代码」区块
区块编辑器的「代码」区块输出结构固定:
<pre class="wp-block-code"><code>echo 'hello';</code></pre>
它的优点是内容以纯文本存储、编辑时不会被富文本处理。写作时注意:
- 粘贴代码前先切成”以纯文本粘贴”(
Ctrl+Shift+V),避免带入外层 HTML; - 代码里若包含
<>&,要以实体形式写入(<>&),否则会被当成 HTML 解析; - 不要用「经典」区块写代码,那会走
wpautop。
第二步:统一代码块 CSS 规范
写进子主题的 style.css,用比主题更具体的选择器:
/* 代码块:等宽字体 + 可横向滚动 + 不换行 */
.wp-block-code,
pre.wp-block-code {
margin: 1.5em 0;
padding: 16px;
background: #1f2430;
color: #e6e6e6;
border-radius: 6px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
font-family: "JetBrains Mono", Consolas, "Courier New", monospace;
font-size: 14px;
line-height: 1.7;
tab-size: 4;
}
.wp-block-code code {
background: none; /* 覆盖主题给 code 加的背景 */
padding: 0;
border: 0;
color: inherit;
white-space: pre; /* 保留缩进,不自动换行 */
font-size: inherit;
}
/* 行内代码:与代码块区分开 */
.entry-content code {
background: #f1f3f5;
padding: 2px 6px;
border-radius: 3px;
font-size: 0.9em;
}
/* 移动端:限制最大高度,避免过长代码块占据整屏 */
@media (max-width: 600px) {
pre.wp-block-code { max-height: 60vh; font-size: 13px; }
}
第三步:处理引号被替换的问题
如果代码里的引号仍然被替换(常见于短代码方案或自定义字段输出的内容),用核心过滤器把相关标签彻底排除:
// 让这些标签内部的内容完全不参与 wptexturize
add_filter( 'no_texturize_tags', function ( $tags ) {
$tags[] = 'pre';
$tags[] = 'code';
$tags[] = 'kbd';
$tags[] = 'samp';
return $tags;
} );
// 关掉表情转换,避免代码里的 ;) 变成图片
add_filter( 'option_use_smilies', '__return_false' );
第四步:模板输出代码时必须转义
在自定义模板、短代码或区块里输出用户提供的代码,一律转义:
function mimangfei_render_code( $code, $lang = '' ) {
$class = $lang ? ' class="language-' . sanitize_html_class( $lang ) . '"' : '';
return sprintf(
'<pre class="wp-block-code"><code%s>%s</code></pre>',
$class,
esc_html( $code ) // 关键:把 < > & " 全部转成实体
);
}
// 用法
echo mimangfei_render_code( get_post_meta( get_the_ID(), 'snippet', true ), 'php' );
esc_html() 是这里的正确选择;不要用 wp_kses_post(),它会把代码示例当成真实 HTML 处理并剥掉一部分标签。
第五步:只给有代码的页面加载高亮脚本
add_action( 'wp_enqueue_scripts', function () {
if ( ! is_singular() ) {
return;
}
$post = get_queried_object();
// 文章里确实包含代码区块才加载
if ( $post && has_block( 'core/code', $post ) ) {
wp_enqueue_style( 'prism', get_theme_file_uri( '/assets/prism.min.css' ), array(), '1.29.0' );
wp_enqueue_script( 'prism', get_theme_file_uri( '/assets/prism.min.js' ), array(), '1.29.0', true );
}
} );
高亮库本身的选型与取舍见 代码高亮插件选型。
第六步:加一个一键复制按钮
document.addEventListener( 'DOMContentLoaded', function () {
document.querySelectorAll( 'pre.wp-block-code' ).forEach( function ( pre ) {
var btn = document.createElement( 'button' );
btn.type = 'button';
btn.className = 'code-copy';
btn.textContent = '复制';
pre.style.position = 'relative';
pre.appendChild( btn );
btn.addEventListener( 'click', function () {
var text = pre.querySelector( 'code' ).innerText;
navigator.clipboard.writeText( text ).then( function () {
btn.textContent = '已复制';
setTimeout( function () { btn.textContent = '复制'; }, 2000 );
} );
} );
} );
} );
行号建议用 CSS 计数器实现,这样复制时不会把行号一起带走:
pre.wp-block-code code {
counter-reset: line;
}
pre.wp-block-code code > span.line::before {
counter-increment: line;
content: counter(line);
display: inline-block;
width: 2.5em;
color: #6b7280;
user-select: none;
}
常见踩坑
- 把密钥、真实 IP、账号写进示例。 公开文章里的任何凭据都应替换为
YOUR_API_KEY、192.0.2.1这类占位值。 - 在 PHP 双引号字符串里写代码示例。
"$db_host"会被解析成变量,输出为空。写代码示例一律用单引号或nowdoc。 white-space: pre在移动端撑破布局。 用overflow-x: auto配合max-height,而不是让页面出现整体横向滚动。- 复制时带上了行号或
$命令提示符。 行号用 CSS 计数器生成,命令行示例不要在每行前写$。 - 代码里的方括号被当成短代码。 用区块或
[code]短代码包裹;短代码内的方括号要用转义写法或改用区块。 - 切换主题后代码块样式全丢。 样式写在子主题里,换主题就没了;更稳的做法是用自定义 CSS 插件或主题自带的”额外 CSS”(后台「外观 → 自定义 → 额外 CSS」)。
- 案例: 某站读者留言”照着文章复制的 Nginx 配置起不来”。排查发现文章里的
listen 80;正常,但server_name example.com;前后的引号在发布时被wptexturize换成了中文引号,读者复制后 Nginx 报directive "server_name" is not terminated by ";"。作者当时用的是一款高亮短代码插件,短代码内部的内容没被核心的<pre>保护覆盖到。解决办法是给no_texturize_tags加上pre/code,并把存量文章重新保存一次。教训:代码展示的正确性优先于美观,发布后一定要亲自复制一遍到真实环境验证。
总结
代码展示的三条底线:内容正确(引号、缩进、实体不被破坏)、视觉可控(等宽字体、横滚、与行内代码区分)、按需加载(只在有代码的页面加载高亮资源)。
行动清单:用「代码」区块写作并纯文本粘贴 → 写一套覆盖主题的代码块 CSS(放到”额外 CSS”里更稳)→ 给 no_texturize_tags 补上 pre/code → 模板输出一律 esc_html() → 用 has_block() 做按需加载 → 发布前复制代码到真实环境跑一遍。高亮库怎么选见 代码高亮插件选型,其余结构化内容(表格)的规范见 TablePress 表格插件教程。
