使用 reactjs 渲染原始 html

Posted

技术标签:

【中文标题】使用 reactjs 渲染原始 html【英文标题】:Rendering raw html with reactjs 【发布时间】:2015-03-12 02:57:02 【问题描述】:

那么这是用 reactjs 渲染原始 html 的唯一方法吗?

// http://facebook.github.io/react/docs/tutorial.html
// tutorial7.js
var converter = new Showdown.converter();
var Comment = React.createClass(
  render: function() 
    var rawMarkup = converter.makeHtml(this.props.children.toString());
    return (
      <div className="comment">
        <h2 className="commentAuthor">
          this.props.author
        </h2>
        <span dangerouslySetInnerHTML=__html: rawMarkup />
      </div>
    );
  
);

我知道用 JSX 标记东西有一些很酷的方法,但我主要感兴趣的是能够呈现原始 html(包含所有类、内联样式等)。像这样复杂的东西:

<!-- http://getbootstrap.com/components/#dropdowns-example -->
<div class="dropdown">
  <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-expanded="true">
    Dropdown
    <span class="caret"></span>
  </button>
  <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Action</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Another action</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Something else here</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Separated link</a></li>
  </ul>
</div>

我不想在 JSX 中重写所有这些。

也许我在想这一切都错了。请纠正我。

【问题讨论】:

这几乎是 JSX。如果您将大量标记呈现为原始 HTML,那么您将失去使用像 React 这样的库的好处。我建议做一些小的改动(比如“class”->“className”)让 React 处理元素。 对于这个具体的例子,有人有already done the work for you,但问题仍然代表一般情况。 medium.com/@to_pe/… HTML 到 JSX 转换:transform.tools/html-to-jsx 【参考方案1】:

现在有更安全的方法来呈现 HTML。我在之前的回答here 中对此进行了介绍。您有 4 个选项,最后一个使用 dangerouslySetInnerHTML

呈现 HTML 的方法

    最简单 - 使用 Unicode,将文件保存为 UTF-8 并将 charset 设置为 UTF-8。

    &lt;div&gt;'First · Second'&lt;/div&gt;

    更安全 - 对 javascript 字符串中的实体使用 Unicode 编号。

    &lt;div&gt;'First \u00b7 Second'&lt;/div&gt;

    &lt;div&gt;'First ' + String.fromCharCode(183) + ' Second'&lt;/div&gt;

    或者是一个包含字符串和 JSX 元素的混合数组。

    &lt;div&gt;['First ', &lt;span&gt;&amp;middot;&lt;/span&gt;, ' Second']&lt;/div&gt;

    不得已 - 使用 dangerouslySetInnerHTML 插入原始 HTML。

    &lt;div dangerouslySetInnerHTML=__html: 'First &amp;middot; Second' /&gt;

【讨论】:

想知道除了 __html 之外,dangerouslySetInnerHTML 还接收到哪些其他属性 @JuanSolano none,根据 TypeScript 环境中的自动完成条目。 在类似的问题***.com/questions/19266197/… 中,这个答案做得不太好。也许它更适合这个问题,或者人们没有阅读答案......:D 人们对dangerousSetInnerHtml 变得不正常。虽然开发人员应该了解它可能如何成为 XSS 攻击的载体,但在一些有效的用例中,这是合理的实用方法,尤其是在您设置的 html 不是由用户输入形成的情况下或由经过净化的用户输入形成。如果您知道 HTML 并且有理由将其存储在变量或其他东西中,那么这是迄今为止最优雅的方法。【参考方案2】:

您可以利用 html-to-react npm 模块。

注意:我是该模块的作者,几小时前刚刚发布。请随时报告任何错误或可用性问题。

【讨论】:

它在内部使用了 dangerouslySetInnerHTML 吗? 我刚刚检查了代码。它似乎没有使用危险的SetInnerHTML。 Mike 你还在积极维护这个 npm 模块吗? 嘿,Daniel,我没有,其中一位贡献者接管了它并在 7 个月前发布了最后一个版本。见github.com/aknuds1/html-to-react 这是臃肿的,使用 ramda 等和大型库 - 我不推荐【参考方案3】:

dangerouslySetInnerHTML 是 React 在浏览器 DOM 中使用 innerHTML 的替代品。一般来说,从代码中设置 HTML 是有风险的,因为很容易在不经意间将您的用户暴露给跨站点脚本 (XSS) 攻击。

在通过dangerouslySetInnerHTML 将原始 HTML 注入到 DOM 之前,最好/更安全地清理原始 HTML(使用例如 DOMPurify)。

DOMPurify - 适用于 HTML、MathML 和 SVG 的仅 DOM、超快速、超容错 XSS 清理器。 DOMPurify 使用安全的默认设置,但提供了很多可配置性和挂钩。

示例

import React from 'react'
import createDOMPurify from 'dompurify'
import  JSDOM  from 'jsdom'

const window = (new JSDOM('')).window
const DOMPurify = createDOMPurify(window)

const rawHTML = `
<div class="dropdown">
  <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-expanded="true">
    Dropdown
    <span class="caret"></span>
  </button>
  <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Action</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Another action</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Something else here</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Separated link</a></li>
  </ul>
</div>
`

const YourComponent = () => (
  <div>
     <div dangerouslySetInnerHTML= __html: DOMPurify.sanitize(rawHTML)  /> 
  </div>
)

export default YourComponent

【讨论】:

这正是我在这个答案中寻找的。它应该有更多的赞成票 谢谢你。这应该是正确的答案。 这是我可能考虑的方法 - 谢谢! 我在没有 JSDOM 的情况下使用了这个。它就像一个魅力。谢谢!阅读此主题后,我意识到带有 CMS 的 Web 应用程序如何暴露给非技术人员的 XSS 攻击。 该死!谢谢榆次!这个答案应该被考虑和接受。【参考方案4】:

我在快速和肮脏的情况下使用了这个:

// react render method:

render() 
    return (
      <div>
         this.props.textOrHtml.indexOf('</') !== -1
            ? (
                <div dangerouslySetInnerHTML=__html: this.props.textOrHtml.replace(/(<? *script)/gi, 'illegalscript') >
                </div>
              )
            : this.props.textOrHtml
          

      </div>
      )
  

【讨论】:

这不安全 - 如果 HTML 包含 &lt;img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" onload="alert('test');"&gt; 怎么办? 如果您可以控制正在呈现的 HTML,那就安全了【参考方案5】:

我试过这个纯组件:

const RawHTML = (children, className = "") => 
<div className=className
  dangerouslySetInnerHTML= __html: children.replace(/\n/g, '<br />') />

特点

采用classNameprop(更容易设置样式) 将\n 替换为&lt;br /&gt;(您经常想这样做) 使用组件时将内容放置为子项&lt;RawHTML&gt;myHTML&lt;/RawHTML&gt;

我已将组件放在 Github 的 Gist 中:RawHTML: ReactJS pure component to render HTML

【讨论】:

【参考方案6】:
export class ModalBody extends Component
    rawMarkup()
        var rawMarkup = this.props.content
        return  __html: rawMarkup ;
    
    render()
        return(
                <div className="modal-body">
                     <span dangerouslySetInnerHTML=this.rawMarkup() />

                </div>
            )
    

【讨论】:

以上对我有用,我将 html 传递给模态体。 如果您有带有内部锚点哈希的锚点标签,它将无法工作,到目前为止,我没有解决方案来引入带有内部锚点的 html 作为内部标签锚点,锚点想要重新将页面渲染到任何地方【参考方案7】:

我使用了this library called Parser。它满足了我的需要。

import React,  Component  from 'react';    
import Parser from 'html-react-parser';

class MyComponent extends Component 
  render() 
    <div>Parser(this.state.message)</div>
  
;

【讨论】:

21KB(压缩后的 8KB)。对于浏览器代码,我无法证明这一点。 这个库破坏了我的 Expo 应用程序【参考方案8】:

dangerouslySetInnerHTML 除非绝对必要,否则不应使用。根据文档,"This is mainly for cooperating with DOM string manipulation libraries"。当你使用它时,你就放弃了 React 的 DOM 管理的好处。

在您的情况下,转换为有效的 JSX 语法非常简单;只需将class 属性更改为className。或者,如上面的 cmets 所述,您可以使用 ReactBootstrap 库,它将 Bootstrap 元素封装到 React 组件中。

【讨论】:

感谢您的回答。我了解使用 innerHTML 的安全隐患,并且我知道我可以将其转换为 JSX。但我特别询问 React 对使用原始 HTML sn-ps 的支持。 “React 对使用原始 HTML sn-ps 的支持”到底是什么意思? 我别无选择,只能使用“dangerouslySetInnerHTML”!!!【参考方案9】:

这里是之前发布的 RawHTML 函数的一个不那么固执己见的版本。它让你:

配置标签 可选择将换行符替换为 &lt;br /&gt;'s 传递 RawHTML 将传递给创建的元素的额外道具 提供一个空字符串 (RawHTML&gt;&lt;/RawHTML&gt;)

这是组件:

const RawHTML = ( children, tag = 'div', nl2br = true, ...rest ) =>
    React.createElement(tag, 
        dangerouslySetInnerHTML: 
            __html: nl2br
                ? children && children.replace(/\n/g, '<br />')
                : children,
        ,
        ...rest,
    );
RawHTML.propTypes = 
    children: PropTypes.string,
    nl2br: PropTypes.bool,
    tag: PropTypes.string,
;

用法:

<RawHTML>'First &middot; Second'</RawHTML>
<RawHTML tag="h2">'First &middot; Second'</RawHTML>
<RawHTML tag="h2" className="test">'First &middot; Second'</RawHTML>
<RawHTML>'first line\nsecond line'</RawHTML>
<RawHTML nl2br=false>'first line\nsecond line'</RawHTML>
<RawHTML></RawHTML>

输出:

<div>First · Second</div>
<h2>First · Second</h2>
<h2 class="test">First · Second</h2>
<div>first line<br>second line</div>
<div>first line
second line</div>
<div></div>

它将中断:

<RawHTML><h1>First &middot; Second</h1></RawHTML>

【讨论】:

【参考方案10】:

我需要在我的脑海中使用一个带有 onLoad 属性的链接,其中 div 是不允许的,所以这给我带来了很大的痛苦。我目前的解决方法是关闭原始脚本标签,做我需要做的事情,然后打开脚本标签(由原始标签关闭)。希望这可以帮助那些绝对别无选择的人:

<script dangerouslySetInnerHTML= __html: `</script>
   <link rel="preload" href="https://fonts.googleapis.com/css?family=Open+Sans" as="style" onLoad="this.onload=null;this.rel='stylesheet'" crossOrigin="anonymous"/>
<script>`,/>

【讨论】:

以上是关于使用 reactjs 渲染原始 html的主要内容,如果未能解决你的问题,请参考以下文章

React.js相关

React JS 同构渲染

使用 ReactJS 的 Datatables.net,在一列中渲染一个 ReactJS 组件

ReactJS:使用枚举的条件渲染

ReactJS 使用不间断空格渲染字符串

条件渲染在 reactjs 中的作用不统一?