嵌套是语义与渲染的双重刚需:保留空白格式,传达代码语义并继承等宽字体,屏幕阅读器依赖此结构识别代码块;单独使用任一标签均无法同时满足需求。

pre + code 嵌套是语义与渲染的双重刚需
单独用 <pre class="brush:php;toolbar:false;"></pre> 只保留空白,但不传达“这是代码”的语义;单独用 <code> 会丢失换行和缩进。两者必须嵌套:<pre class="brush:php;toolbar:false;"><code>...</code></pre>
浏览器对 <code> 的默认等宽字体继承,依赖这个结构生效;屏幕阅读器也靠此识别代码块。若写成 <pre class="brush:php;toolbar:false;"><span>...</span></pre>,字体可能回退到非等宽字体,中文缩进直接塌陷。
- 服务端模板中不要拼接:
<pre class="brush:php;toolbar:false;"><code class="js">{{ raw_code }}</code></pre>—— 若raw_code含未转义或 <code>&,会被解析为标签或实体 - Vue 中禁用
{{ raw_code }},改用v-text或v-html(仅当内容可信且已净化) - React 中
{raw_code}默认走textContent,安全;需渲染 HTML 时才用dangerouslySetInnerHTML
动态注入必须用 textContent,禁用 innerHTML 拼接
常见错误是:preEl.innerHTML = "<code>" + apiResponse.code + ""。只要 apiResponse.code 含 、<code>>、&,就会触发 HTML 解析,轻则内容错位,重则执行脚本。
正确做法是:先确保 DOM 中已有 <pre class="brush:php;toolbar:false;"><code></code></pre> 结构,再赋值:preEl.querySelector("code").textContent = apiResponse.code。
- 若后端返回的是带转义的字符串(如
"<div>hello</div>"),前端要先DOMPurify.sanitize()或decodeURIComponent()还原,再塞进textContent - 绝对不要在 JS 中用正则替换
→ <code><—— 容易漏掉嵌套或属性中的尖括号,不可靠 - 服务端输出 PHP/Node.js 时,应调用
htmlspecialchars($str, ENT_NOQUOTES),且仅对用户输入字段做,避免双引号被二次解析
样式加固缺一不可:font-family + white-space + overflow-x
<pre class="brush:php;toolbar:false;"></pre> 默认用系统字体,中文环境下常 fallback 到“微软雅黑常规体”——它不是等宽字体,导致空格和 Tab 对不齐;默认 white-space: pre 禁止折行,长代码撑破容器;默认无滚动,移动端完全不可用。
必须显式加 CSS:
pre {
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, 'Microsoft YaHei Mono', monospace;
white-space: pre-wrap;
overflow-x: auto;
max-width: 100%;
padding-right: 1em; /* 防滚动条遮挡末字符 */
tab-size: 4;
}
- 别只写
monospace—— 它只是关键字,不同系统映射差异大,中文环境尤其危险 -
white-space: pre-line会合并连续空格,破坏 Python 缩进,必须避开 -
overflow-x: auto必须配max-width: 100%父容器,否则滚动条不触发
超长日志或代码块要切片,别硬塞进单个 pre
百万行日志或超长 SQL 直接扔进一个 <pre class="brush:php;toolbar:false;"></pre>,会导致主线程卡死、内存溢出、首屏白屏。这不是 CSS 能解决的问题,而是 DOM 构建瓶颈。
解法是语义切片:HTMLHeaderTextSplitter 按 <h2></h2>、<h3></h3> 分块,每块不超过 50KB,用 <template></template> 按需注入。
- 切片时启用
return_each_element=True,保留标题路径,方便生成侧边大纲 - 含
<table> 或 <code><pre class="brush:php;toolbar:false;"><code></code> 的块,必须换用 <code>HTMLSemanticPreservingSplitter</code>,否则表格跨行、代码缩进被截断</pre> <li> <code><template></template>内容必须cloneNode(true)后再 append,否则第二次调用返回空 Fragment,静默失败
真正容易被忽略的是:预格式化失效,往往不是标签没写对,而是内容在抵达 <pre class="brush:php;toolbar:false;"></pre> 前,已经被模板引擎、JS 赋值方式或服务端转义链悄悄“蒸干”了换行和 Tab。检查路径得从 network response 里的原始字节开始,而不是只看最终渲染结果。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











