使用 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。
<div>'First · Second'</div>
更安全 - 对 javascript 字符串中的实体使用 Unicode 编号。
<div>'First \u00b7 Second'</div>
或
<div>'First ' + String.fromCharCode(183) + ' Second'</div>
或者是一个包含字符串和 JSX 元素的混合数组。
<div>['First ', <span>&middot;</span>, ' Second']</div>
不得已 - 使用 dangerouslySetInnerHTML
插入原始 HTML。
<div dangerouslySetInnerHTML=__html: 'First &middot; Second' />
【讨论】:
想知道除了 __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 包含<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" onload="alert('test');">
怎么办?
如果您可以控制正在呈现的 HTML,那就安全了【参考方案5】:
我试过这个纯组件:
const RawHTML = (children, className = "") =>
<div className=className
dangerouslySetInnerHTML= __html: children.replace(/\n/g, '<br />') />
特点
采用className
prop(更容易设置样式)
将\n
替换为<br />
(您经常想这样做)
使用组件时将内容放置为子项:
<RawHTML>myHTML</RawHTML>
我已将组件放在 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 函数的一个不那么固执己见的版本。它让你:
配置标签 可选择将换行符替换为<br />
's
传递 RawHTML 将传递给创建的元素的额外道具
提供一个空字符串 (RawHTML></RawHTML>
)
这是组件:
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 · Second'</RawHTML>
<RawHTML tag="h2">'First · Second'</RawHTML>
<RawHTML tag="h2" className="test">'First · 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 · 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的主要内容,如果未能解决你的问题,请参考以下文章