
本文介绍在 React 中将含 的长代码字符串渲染为带换行和语法高亮的 HTML 内容,重点解决动态插入 <br/> 与按词性(如 export、const、变量名等)添加 <span> 样式标签的协同实现方案。
本文介绍在 react 中将含 `
` 的长代码字符串渲染为带换行和语法高亮的 html 内容,重点解决动态插入 `
` 与按词性(如 `export`、`const`、变量名等)添加 `` 样式标签的协同实现方案。
在 React 中直接使用 dangerouslySetInnerHTML 渲染带 HTML 标签的字符串虽快,但存在 XSS 风险且无法享受 JSX 类型检查与样式作用域优势。更健壮的做法是:将原始字符串结构化解析 → 按行拆分 → 每行内再按关键词正则匹配 → 分段包裹 <span> 并保留 <br/> 语义。以下为推荐的生产级实现方案:
✅ 推荐方案:逐行解析 + 行内关键词高亮
// 定义关键词匹配规则(支持类 CSS 选择器语义)
const TOKEN_RULES = [
{ pattern: /(export|import|default)/g, className: 'token-keyword' },
{ pattern: /(const|let|var|function|return)/g, className: 'token-declaration' },
{ pattern: /([a-zA-Z_$][w$]*)s*:/g, className: 'token-property' }, // 键名(如 "initial:")
{ pattern: /(fadeAnimation|initial|animate|exit)/g, className: 'token-identifier' },
{ pattern: /{[^}]*}/g, className: 'token-object' }, // 简单对象字面量高亮(可扩展)
];
// 主格式化函数:支持换行 + 关键词高亮 + 缩进
const formatCodeString = (text: string): React.ReactNode[] => {
return text.split('
').map((line, index) => {
// 第一行无缩进,其余行缩进 20px(对应原逻辑)
const style = index === 0
? { color: '#a69a9a' }
: { marginLeft: '20px', color: '#a69a9a' };
// 对每行内容进行关键词替换:用 <span> 包裹匹配项,其余为纯文本
let processedLine: React.ReactNode[] = [];
let lastIndex = 0;
TOKEN_RULES.forEach(({ pattern, className }) => {
let match;
// 注意:需全局重置 lastIndex,避免多规则交叉干扰(此处简化为顺序处理,实际建议合并正则)
const tempLine = line.slice(lastIndex);
while ((match = pattern.exec(tempLine)) !== null) {
const start = match.index;
const end = start + match[0].length;
// 添加前置普通文本
if (start > 0 && lastIndex + start > lastIndex) {
processedLine.push(line.slice(lastIndex, lastIndex + start));
}
// 添加高亮 span
processedLine.push(
<span key={`${index}-${className}-${processedLine.length}`} className={className}>
{match[0]}
</span>
);
lastIndex = lastIndex + end;
}
});
// 添加行尾剩余文本
if (lastIndex < line.length) {
processedLine.push(line.slice(lastIndex));
}
return (
<div key={index} style={style}>
{processedLine}
<br />
</div>
);
});
};
// 在组件中使用
function CodePreview() {
const codeString = `export const fadeAnimation = {\n initial: {opacity: 0, transition: { duration: 0.3, delay: 0.5 }, },\n animate: { opacity: 1, transition: { duration: 0.3, delay: 0.3 }, },\n exit: {opacity: 0, transition: { duration: 0.3, delay: 0 },}\n}`;
return <div className="code-block">{formatCodeString(codeString)}</div>;
}⚠️ 注意事项与最佳实践
- 安全性优先:全程未使用 dangerouslySetInnerHTML,所有文本均作为 React 子节点渲染,天然防御 XSS。
- 性能考量:对短代码块(<100 行)性能无压力;若需处理超长代码,建议配合 useMemo 缓存结果或使用 Web Worker 解析。
-
样式隔离:推荐通过 CSS Modules 或 className 配合 CSS-in-JS 实现主题化,例如:
.token-keyword { color: #007acc; font-weight: bold; } .token-identifier { color: #22863a; } .token-object { background: #f6f8fa; padding: 0 2px; border-radius: 3px; } - 扩展性设计:TOKEN_RULES 易于增删规则,可对接 Prism.js 词法定义或自定义 Babel 插件生成 token 配置。
- 换行兼容性: 已被显式 .split(' ') 处理,确保跨平台(Windows/Linux/macOS)一致;若原始字符串含 ,建议先统一替换为 。
✅ 总结
与其手动拼接 JSX 数组或硬编码标记,不如构建一个可复用的 formatCodeString 工具函数——它将「换行布局」与「语法高亮」解耦,既保持 React 的声明式特性,又具备良好的可维护性与安全性。对于复杂代码展示场景(如文档站点、IDE 风格预览),还可进一步集成 react-syntax-highlighter 等成熟库,但本方案提供了轻量、可控、零依赖的落地路径。










