React渲染时HTML自动转义显示为代码,如何正常渲染动态HTML?
问题背景
我写了个叫ExchangeListItem的React组件,render里的<li>和自带的<a>标签都能正常显示,但getListItem方法返回的HTML字符串却被当成纯文本打印在页面上,标签都被转义了。我不想用修改innerHTML的方式,就想让React一开始就渲染我要的动态内容,该咋弄?
附上我的代码:
class ExchangeListItem extends Component { constructor(props) { super(props); this.state = { }; } getListItem() { const retStr = '<a className="nav-link active" href="'+this.props.exchange.url+'">'+this.props.exchange.name + '</a>'; return retStr; } render() { return ( <li> <a href="https://wwww.google.com">Link</a> {this.getListItem()} </li> ); } }
核心问题就是:React会转义字符串里的特殊字符,导致HTML结构变成文本。我要的是React直接渲染预期元素,不是事后改innerHTML。
解决办法
其实你完全没必要拼接HTML字符串来做动态渲染——React本来就是用JSX来描述UI的,直接返回React元素才是正确打开方式。
1. 直接返回JSX元素(首推!)
把getListItem改成返回JSX元素,而不是拼接字符串。这样React会直接识别并渲染成DOM节点,根本不会有转义问题:
class ExchangeListItem extends Component { constructor(props) { super(props); this.state = { }; } getListItem() { // 直接返回JSX,不用拼字符串 return ( <a className="nav-link active" href={this.props.exchange.url}> {this.props.exchange.name} </a> ); } render() { return ( <li> <a href="https://www.google.com">Link</a> {this.getListItem()} </li> ); } }
这种方式完全符合React的设计思路,既安全好维护,还能让React高效更新UI,一举多得。
2. 实在要用到HTML字符串的情况(不推荐)
如果真的有特殊场景必须用HTML字符串,React提供了dangerouslySetInnerHTML属性来实现,但名字里的dangerously已经提醒你了——这玩意儿有XSS风险,一定要确保你渲染的内容是完全可信的,不能是用户输入的内容:
class ExchangeListItem extends Component { constructor(props) { super(props); this.state = { }; } getListItem() { const retStr = '<a className="nav-link active" href="'+this.props.exchange.url+'">'+this.props.exchange.name + '</a>'; return retStr; } render() { return ( <li> <a href="https://www.google.com">Link</a> {/* 用dangerouslySetInnerHTML渲染字符串 */} <div dangerouslySetInnerHTML={{ __html: this.getListItem() }} /> </li> ); } }
还是那句话,能不用就不用,优先选第一种JSX的方式。
为啥你的代码会被转义?
这是React的安全机制在保护你!默认情况下,React会把字符串里的<、>这些特殊字符转换成HTML实体(比如<、>),防止恶意脚本注入(也就是XSS攻击)。所以你拼的HTML字符串就被当成纯文本显示了。但如果你返回的是JSX元素,React会把它编译成React元素对象,最终渲染成真实的DOM节点,自然就不会被转义啦。
内容的提问来源于stack exchange,提问作者marcus

