FumadocsZDecode
React

HTML 转义

React 默认对所有文本内容进行 HTML 转义,以防止 XSS 攻击。若要渲染真正的 HTML,可以使用 dangerouslySetInnerHTML 属性,但这需要谨慎处理。


一、基本用法

dangerouslySetInnerHTML 接收一个对象,其中 __html 键包含要渲染的 HTML 字符串。

function HTMLRenderer({ html }: { html: string }) {
  return <div dangerouslySetInnerHTML={{ __html: html }} />
}

function Demo(params: type) {
  return <HTMLRenderer html="<p>这是<strong>HTML</strong>内容</p>" />
}
// 使用示例

这是 React 对浏览器 innerHTML 的替代方案。


二、为什么叫 "dangerous"

属性名中的 "dangerous" 是有意的警告——使用 dangerouslySetInnerHTML 存在 XSS(跨站脚本攻击)风险

// ✗ 危险:用户输入可能包含恶意脚本
function UnsafeComponent({ userContent }: { userContent: string }) {
  return <div dangerouslySetInnerHTML={{ __html: userContent }} />
}

// 假设 userContent = '<img src=x onerror="alert(\'hacked\')">'
// 脚本会被执行

安全原则:只在 完全信任 内容来源时才使用。


三、安全的使用场景

✓ 来自可信 CMS 系统

function CMSContent({ htmlFromCMS }: { htmlFromCMS: string }) {
  return <div dangerouslySetInnerHTML={{ __html: htmlFromCMS }} />
}

✓ 经过处理的 Markdown 转 HTML

import { marked } from 'marked'
import DOMPurify from 'dompurify'

function MarkdownPreview({ markdown }: { markdown: string }) {
  const html = marked(markdown)
  const sanitized = DOMPurify.sanitize(html)

  return <div dangerouslySetInnerHTML={{ __html: sanitized }} />
}

✓ 富文本编辑器输出(经过清理)

function RichTextDisplay({ editorOutput }: { editorOutput: string }) {
  const sanitized = DOMPurify.sanitize(editorOutput)

  return <div dangerouslySetInnerHTML={{ __html: sanitized }} />
}

四、最佳实践

1. 使用内容清理库

永远用 DOMPurify 或类似库清理不完全可信的 HTML:

import DOMPurify from 'dompurify'

function SafeHTML({ html }: { html: string }) {
  const clean = DOMPurify.sanitize(html)
  return <div dangerouslySetInnerHTML={{ __html: clean }} />
}

2. 清晰标识内容来源

在代码中明确注释为什么这里使用 dangerouslySetInnerHTML

function TrustedContent({ html }: { html: string }) {
  // HTML 来自后端 CMS,已被系统管理员控制
  return <div dangerouslySetInnerHTML={{ __html: html }} />
}

五、替代方案

如果只需要展示普通文本,不要使用 dangerouslySetInnerHTML

// ✓ React 会自动转义 HTML 字符,防止 XSS
function SafeText({ text }: { text: string }) {
  return <div>{text}</div>
}

// 示例:即使 text = "<script>alert('x')</script>"
// 也会被安全地显示为文本,不会执行脚本

使用 JSX 而不是 HTML 字符串

// 当你可以控制结构时,直接用 JSX 更安全更清晰
function SafeComponent({ title }: { title: string }) {
  return (
    <div>
      <h1>{title}</h1>
      <p>这是一个段落</p>
    </div>
  )
}

核心原则:React 默认转义所有内容是为了保护你。只在有明确的必要且完全信任源时才用 dangerouslySetInnerHTML,并且总是搭配内容清理库使用。

On this page