搜索全站
插件 15 分钟阅读

WordPress 代码插入与展示最佳实践:避免主题样式破坏代码块

代码块被主题排版搞乱、引号被替换成全角、手机上撑破布局?本文讲清 WordPress 内容过滤链对代码的影响,给出转义、CSS 规范、按需加载与一键复制的完整做法。

U
UFO_PF 2016-09-24 · 阅读 15 分钟
Wo 插件
阅读提示
本文约 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;
  • 代码里若包含 < > &,要以实体形式写入(&lt; &gt; &amp;),否则会被当成 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;
}

常见踩坑

  1. 把密钥、真实 IP、账号写进示例。 公开文章里的任何凭据都应替换为 YOUR_API_KEY、192.0.2.1 这类占位值。
  2. 在 PHP 双引号字符串里写代码示例。 "$db_host" 会被解析成变量,输出为空。写代码示例一律用单引号或 nowdoc。
  3. white-space: pre 在移动端撑破布局。 用 overflow-x: auto 配合 max-height,而不是让页面出现整体横向滚动。
  4. 复制时带上了行号或 $ 命令提示符。 行号用 CSS 计数器生成,命令行示例不要在每行前写 $。
  5. 代码里的方括号被当成短代码。 用区块或 [code] 短代码包裹;短代码内的方括号要用转义写法或改用区块。
  6. 切换主题后代码块样式全丢。 样式写在子主题里,换主题就没了;更稳的做法是用自定义 CSS 插件或主题自带的”额外 CSS”(后台「外观 → 自定义 → 额外 CSS」)。
  7. 案例: 某站读者留言”照着文章复制的 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 表格插件教程。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。