You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中用highlight.js实现带行号代码高亮,修复多行SQL高亮异常

问题分析与修复方案

看起来你遇到的核心问题是逐行调用highlightBlock破坏了代码的上下文关联——SQL这类语言的语法高亮依赖整个代码块的上下文(比如跨多行的语句、关键字的延续),单独处理每一行时,HLJS无法识别孤立行的语法角色,导致除第一行外的其他行无法正确高亮。

下面给你两个可行的修复思路,从修改现有代码到更优雅的重构都有:


方案一:先整体高亮,再拆分到行(适配现有表格结构)

这个思路是先让HLJS处理完整的代码块,生成带高亮标记的HTML,再把这些高亮后的内容拆分成行,放到你的表格里。这样每一行都会保留正确的高亮样式,同时不改变你现有的UI结构。

修改后的代码示例:

if (data.success === 1) {
  const targetLang = translateLanguage(data.code.language);
  setCodeLanguage(targetLang);
  
  // 第一步:整体高亮完整代码,获取带高亮标签的HTML
  const highlightedCode = hljs.highlight(data.code.source_code, { 
    language: targetLang 
  }).value;
  
  // 第二步:把高亮后的代码按换行拆分(注意保留所有HTML标签)
  const highlightedLines = highlightedCode.split('\n');
  
  // 第三步:生成表格行,用dangerouslySetInnerHTML插入高亮内容
  const rows = highlightedLines.map((line, index) => (
    <tr key={index} className="line">
      <td className="line-number">{index + 1}</td>
      <td id={"plus" + index} className="plus-square-line">
        <PlusSquareTwoTone className="plus-square" />
      </td>
      <td 
        id={"codeblock" + index} 
        className={'language-' + targetLang} 
        style={rowStyle}
        dangerouslySetInnerHTML={{ __html: line }}
      />
    </tr>
  ));
  
  setCodeRows(rows);
  // 这里不需要再循环调用highlightBlock了!
}

注意事项:

  • 使用dangerouslySetInnerHTML时,要确保你的代码来源是可信的(比如自己后端返回的代码,不是用户提交的未过滤内容),避免XSS攻击风险。如果需要处理用户提交的代码,建议先做HTML转义,再交给HLJS处理。

方案二:用CSS实现行号,避免逐行拆分(更优雅高效)

如果可以调整UI实现方式,推荐用CSS计数器生成行号,这样不需要把代码拆分成表格行,直接让HLJS处理整个代码块,既保证高亮正确,又简化代码结构。

实现步骤:

  1. 调整HTML结构:
if (data.success === 1) {
  const targetLang = translateLanguage(data.code.language);
  setCodeLanguage(targetLang);
  setFullCode(data.code.source_code); // 保存完整代码
}

// 渲染时的结构
<div className="code-with-lines">
  <pre>
    <code className={`language-${codeLanguage}`}>{fullCode}</code>
  </pre>
</div>
  1. 添加CSS样式实现行号:
.code-with-lines {
  counter-reset: line-counter;
  padding-left: 2.5em; /* 给行号留出空间 */
}

.code-with-lines pre code {
  display: block;
  counter-increment: line-counter;
}

.code-with-lines pre code::before {
  content: counter(line-counter);
  display: inline-block;
  width: 2em;
  margin-left: -2.5em; /* 把行号拉到预留空间里 */
  text-align: right;
  color: #888; /* 行号的灰色样式,和GitHub一致 */
  padding-right: 0.5em;
  border-right: 1px solid #eee;
  margin-right: 0.5em;
}
  1. 高亮整个代码块:
// 组件挂载或代码更新时调用
useEffect(() => {
  if (fullCode && codeLanguage) {
    hljs.highlightBlock(document.querySelector(`.language-${codeLanguage}`));
  }
}, [fullCode, codeLanguage]);

这个方案的优势:

  • HLJS处理完整代码块,上下文完整,高亮100%正确;
  • 代码结构更简洁,没有表格拆分的冗余;
  • 性能更好,不需要循环处理每一行;
  • 行号样式更容易调整,和GitHub的风格更接近。

为什么原来的方式不行?

HLJS的highlightBlock是针对完整代码块设计的,它会分析整个代码的语法结构(比如SQL中的SELECT ... FROM ... WHERE是一个完整的语句链)。当你把代码拆成单独的行后,每一行都是孤立的“片段”,HLJS无法识别这些片段属于完整SQL语句的一部分,自然无法正确高亮后续行的语法元素。

内容的提问来源于stack exchange,提问作者Alex Popa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 13:42:44