
本文介绍如何在 React 中将含换行符(\n)的长代码字符串安全转换为带 <br/> 换行、且支持关键词(如 export/const/变量名)语法高亮的 HTML 内容,兼顾可维护性与 XSS 防护。
本文介绍如何在 react 中将含换行符(`\n`)的长代码字符串安全转换为带 `
` 换行、且支持关键词(如 `export`/`const`/变量名)语法高亮的 html 内容,兼顾可维护性与 xss 防护。
在 React 中直接用 dangerouslySetInnerHTML 渲染带 HTML 标签的字符串虽快,但存在 XSS 风险,且难以动态控制关键词样式;而纯字符串 .split('\n') 后映射 <div>+<br/> 的方式又无法嵌套高亮。理想方案是结构化文本语义——将原始字符串解析为带类型标记的词元(token)序列,再逐项渲染为受控的 JSX 元素。
✅ 推荐方案:Token 化 + 类型驱动渲染
我们不手动拼接 HTML 字符串,而是将代码字符串拆解为「语义化词元」(tokens),每个词元包含 type(如 'keyword'、'identifier'、'punctuation')和 text 属性,再通过 map 渲染为带样式的 <span>:
// Step 1: 定义 tokenized 常量(推荐:静态、可读、可复用)
export const fadeAnimationTokens = [
{ type: 'keyword', text: 'export' },
{ type: 'whitespace', text: ' ' },
{ type: 'keyword', text: 'const' },
{ type: 'whitespace', text: ' ' },
{ type: 'identifier', text: 'fadeAnimation' },
{ type: 'punctuation', text: ' =' },
{ type: 'whitespace', text: ' ' },
{ type: 'punctuation', text: '{' },
{ type: 'newline', text: '\n' },
{ type: 'whitespace', text: ' ' },
{ type: 'identifier', text: 'initial' },
{ type: 'punctuation', text: ':' },
// ... 更多 tokens(可自动生成,见下方说明)
];// Step 2: 创建高亮渲染组件
const CodeHighlighter = ({ tokens }) => {
const renderToken = (token, index) => {
const baseStyle = {
fontFamily: 'SFMono-Regular, Consolas, "Liberation Mono", monospace',
fontSize: '0.9em',
lineHeight: '1.5'
};
switch (token.type) {
case 'keyword':
return (
<span key={index} style={{ ...baseStyle, color: '#d73a49' }}>
{token.text}
</span>
);
case 'identifier':
return (
<span key={index} style={{ ...baseStyle, color: '#24292e', fontWeight: 'bold' }}>
{token.text}
</span>
);
case 'punctuation':
return (
<span key={index} style={{ ...baseStyle, color: '#586069' }}>
{token.text}
</span>
);
case 'whitespace':
return <span key={index}> </span>;
case 'newline':
return <br key={index} />;
default:
return <span key={index} style={baseStyle}>{token.text}</span>;
}
};
return <div className="code-block">{tokens.map(renderToken)}</div>;
};
// 使用示例
function AnimationDemo() {
return <CodeHighlighter tokens={fadeAnimationTokens} />;
}⚙️ 进阶技巧:自动 Token 化(可选)
对长代码块,可借助简易正则实现自动化 token 提取(适用于非生产级快速原型):
const tokenizeCodeString = (str) => {
const regex = /(\bexport\b|\bconst\b|\bfunction\b|\breturn\b|\b\{|\b\}|\b=|\b:|\n|\s+)/g;
return str.split(regex)
.filter(Boolean)
.map((text) => {
if (/^\s+$/.test(text)) return { type: 'whitespace', text };
if (text === '\n') return { type: 'newline', text };
if (['export', 'const', 'function', 'return'].includes(text))
return { type: 'keyword', text };
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(text))
return { type: 'identifier', text };
if (/[{}=:;()[\]]/.test(text))
return { type: 'punctuation', text };
return { type: 'plain', text };
});
};
// 动态生成(仅用于演示,生产环境建议预处理或使用 highlight.js)
const dynamicTokens = tokenizeCodeString(
`export const fadeAnimation = {\n initial: { opacity: 0 },\n animate: { opacity: 1 }\n}`
);⚠️ 关键注意事项
- 永远避免 dangerouslySetInnerHTML 处理用户输入:即使内容看似“可信”,也应统一走 token 渲染流;
- 保留空格与换行语义:whitespace 和 newline token 确保缩进与格式不失真;
- CSS 优先于内联样式:生产环境建议用 CSS Modules 或 className 替代 style,提升可维护性与性能;
- 性能优化:若 tokens 数量极大(>1000),可考虑 React.memo 包裹 CodeHighlighter,或使用虚拟滚动;
- 无障碍友好:为代码块添加 role="code" 和 tabIndex="0",便于键盘导航。
通过将文本语义化为 tokens,你不仅实现了灵活的语法高亮与换行控制,更构建了可扩展、可测试、安全可靠的代码展示基础设施——这是专业 React 应用处理富文本内容的推荐范式。










