是最稳妥的代码展示方式,浏览器在 html 解析阶段即对 启用预格式化路径,原样保留空格、tab 和换行符为独立文本节点,且必须嵌套 以保障语义与样式可控性。

直接在 <pre class="brush:php;toolbar:false;"><code></code> 标签里贴代码最稳妥</pre>
浏览器对 <pre class="brush:php;toolbar:false;"></pre> 的空白符保留机制是唯一可靠的方式,其他标签(比如 <p></p> 或 <div>)会把换行、缩进全吃掉,导致代码变形。必须嵌套 <code><code>,否则语义丢失,CSS 也没法针对性加样式。
实操建议:
- 先手动写好
<pre class="brush:php;toolbar:false;"><code class="js"></code> 开头,再粘贴代码,最后补上 <code></code></pre> - 别用编辑器“插入代码块”按钮自动生成的 HTML —— 很多编辑器(如 TinyMCE、CKEditor)默认只用
<div> 包裹,不带 <code><pre class="brush:php;toolbar:false;"></pre>,结果就是代码挤成一行 - 如果编辑器支持 HTML 源码模式,就切过去手写;不支持的话,得提前配置插件或自定义按钮,让它输出
<pre class="brush:php;toolbar:false;"><code></code> 结构</pre> - 别自己写正则替换 —— 容易漏边角情况;用现成函数更稳,比如
DOMPurify.sanitize()或简单封装:function escapeHtml(str) { return str.replace(/[&"']/g, c => ({'&': '&', '': '>', '"': '"', "'": '''}[c])); } - 转义后塞进
<pre class="brush:php;toolbar:false;"><code></code> 内容区,而不是直接赋给 <code>innerText</code> —— 后者会把所有空格/换行压成单空格</pre>
用 innerHTML 动态插入时,必须转义 和 <code>>
直接 element.innerHTML = "if (x 看似能跑,但一旦代码里有未闭合的标签(比如 <code><div>),浏览器会尝试解析它,导致 DOM 错乱甚至脚本中断。
<p>正确做法是先做 HTML 实体转义:</p>
<ul>
<li><code> → <code><,> → >,& → &
编辑器粘贴代码后缩进错乱?检查是否启用了「智能缩进」或「自动格式化」
很多富文本编辑器(如 Quill、Draft.js)在粘贴时会主动清理缩进、合并空行,尤其对 Python、YAML 这类依赖缩进的语言是灾难性的。
应对方式取决于你用的编辑器:
- Quill:禁用
clipboard.matchers中对text/plain的默认处理,或监听text-change事件,在插入前拦截并重写内容 - TinyMCE:设
paste_preprocess回调,对content字段做replace(/\s+$/gm, '')清尾空格,再用content = content.replace(/^/gm, ' ')把首空格转为(仅限简单场景) - 最省事的方案:粘贴前先切换到源码模式,或用快捷键
Ctrl+Shift+V(无格式粘贴),再手动补<pre class="brush:php;toolbar:false;"><code></code></pre>
语法高亮没生效?别只靠 <code> 标签名推断语言
<code> 本身不触发高亮,需要额外标记语言类型,且不同高亮库要求不同:
- Prism.js:依赖
class="python"或class="js",不是lang="python" - highlight.js:支持
class="python"或data-language="python",但若用后者,得手动调用hljs.highlightElement(el) - 如果不加语言类名,多数库会 fallback 到纯文本渲染 —— 看起来像没生效,其实是没告诉它该按什么规则解析
顺带一提:有些编辑器生成的代码块带 data-lang 属性,但 Prism 默认不读这个,得自己写适配逻辑。
真正麻烦的点不在怎么插,而在于编辑器和高亮库之间的约定是否对齐 —— 多数人卡在这一步,反复刷新页面以为是缓存问题,其实只是 class 名写错了。











