【问题标题】:How to parse html to React component?如何将 html 解析为 React 组件?
【发布时间】:2017-11-22 10:17:50
【问题描述】:

这是我的情景:
1.页面内容的应用请求CMS(内容管理系统)。
2. CMS返回"<div>Hi,<SpecialButton color="red">My Button</SpecialButton></div>"
3. 应用消费内容,用属性提供的数据渲染对应的组件。

我不知道如何以 React 方式执行 第 3 步,感谢任何建议。

感谢@Glenn Reyes,这里有一个Sandbox 来显示问题。

import React from 'react';
import { render } from 'react-dom';

const SpecialButton = ({ children, color }) => (
  <button style={{color}}>{children}</button>
);

const htmlFromCMS = `
<div>Hi, 
  <SpecialButton color="red">My Button</SpecialButton>
</div>`;

const App = () => (
  <div dangerouslySetInnerHTML={{__html: htmlFromCMS}}>
  </div>
);

// expect to be same as
// const App = () => (
//   <div>Hi, 
//     <SpecialButton color="red">My Button</SpecialButton>
//   </div>
// );

render(<App />, document.getElementById('root'));

Here is a live demo 由 Vuejs 制作。 String "&lt;div v-demo-widget&gt;&lt;/div&gt;" 可以被视为 Vuejs 指令并被渲染。 Source Code.

【问题讨论】:

  • Andy 没有明确的答案,但可能能够为您指明一个方向,具体取决于您获取 CMS 数据的方式。您是否尝试过使用更高阶的组件来呈现从请求返回的组件?我认为一个组件然后呈现您请求的组件可能是要走的路。
  • @BrettEast,更高阶的组件可以执行请求,但我的问题是在从 CMS 获取 &lt;h4&gt;Hello&lt;/h4&gt;&lt;reactcomponenct attr1="foo"&gt;&lt;/mycomponenct&gt; 之类的字符串后,如何让反应知道 &lt;reactcomponenct attr1="foo"&gt;&lt;/mycomponenct&gt; 的部分是组件,需要执行组件的代码。
  • 是的,这很棘手,您的传入数据是否有可能遵循反应命名模式,反应组件使用大写字母,html 元素使用小写字母?也许正则表达式可以解决问题?
  • 是的,我认为遵循 react 命名模式是可以的,但看起来我需要编写一个编译器来执行类似 angular 指令的操作...

标签: javascript html reactjs compilation content-management-system


【解决方案1】:

您可以尝试使用ReactDOMserver 在您的服务器上将&lt;MyReactComponent /&gt; 渲染成html,然后将其传递给客户端,您可以在客户端通过dangerouslySetInnerHTML 插入所有收到的html

【讨论】:

  • 根据ReactDOMServerRenderToString 接受component 作为参数,而不是像“”这样的字符串,因此无法正确渲染。
  • 没错,首先你需要通过组件名找到对应的类。然后渲染它。我只是假设您很容易在服务器端(在 CMS 中)执行此操作。否则,您将需要解析整个字符串,将纯 html 和 React 组件分开,然后将它们一起呈现在另一个组件中。无论如何,这不是一个简单的任务,我建议你找到一些解决方法。
  • 我明白了,所以你的建议是放在CMS中的组件逻辑,CMS返回渲染的组件字符串对吗?
  • 是的,最大的问题在于解析字符串。如果你可以在CMS中轻松获取组件名称,那么在CMS中渲染组件然后返回给客户端会更容易。
  • 看起来无论是在客户端还是在CMS中,我都需要一个来自html的解析器来响应组件
【解决方案2】:

您可能想深入了解dangerouslySetInnerHTML。这是一个如何从 React 组件中的字符串呈现 HTML 的示例:

import React from 'react';
import { render } from 'react-dom';

const htmlString = '<h1>Hello World! ?</h1>';

const App = () => (
  <div dangerouslySetInnerHTML={{ __html: htmlString }} />
);

render(<App />, document.getElementById('root'));

此处为完整示例:https://codesandbox.io/s/xv40xXQzE

在此处的 React 文档中阅读有关 dangerouslySetInnerHTML 的更多信息:https://facebook.github.io/react/docs/dom-elements.html#dangerouslysetinnerhtml

【讨论】:

  • 如果 htmlString 包含像 "&lt;h4&gt;Hello&lt;/h4&gt;&lt;MyReactComponent attr1="foo"&gt;&lt;/MyReactComponent&gt;" 这样的 reactComponent 字符串怎么办? MyReactComponent 将是 html 字符串而不是真正的组件
  • 对不起各位,给我一秒钟我做一个完整的例子来减少误解。
  • 有趣,我知道你在找什么。我在这里创建了一个代码沙箱:codesandbox.io/s/WnKvoY6BE
  • 谢谢你,我已经编辑了我的问题,稍微调整一下你的沙盒
  • 今天才注意到。随时将此答案标记为已解决,谢谢!
【解决方案3】:

对于未来的任何 GProst Answer 增强功能,您都可以使用 ReactDOMserver,这就是我们可以实现相同功能的方式。

import React from "react";
import { render } from "react-dom";
import { renderToString } from "react-dom/server";

const SpecialButton = ({ children, color }) => (
  <button style={{ color }}>{children}</button>
);

const renderButton = renderToString(<SpecialButton>MyButton</SpecialButton>);

const htmlFromCMS = `
<div>Hi, 
  ${renderButton}
</div>`;

const App = () => <div dangerouslySetInnerHTML={{ __html: htmlFromCMS }} />;

render(<App />, document.getElementById("root"));

【讨论】:

    【解决方案4】:

    如果您不想使用dangerouslySetInnerHTML 属性,可以使用react-html-parser

    import React from 'react';
    import { render } from 'react-dom';
    import ReactHtmlParser from 'react-html-parser';
    
    const SpecialButton = ({ children, color }) => (
      <button style={{color}}>{children}</button>
    );
    
    const htmlFromCMS = `
    <div>Hi, 
      <SpecialButton color="red">My Button</SpecialButton>
    </div>`;
    
    const App = () => (
      <div>
         {ReactHtmlParser(htmlFromCMS)}
      </div>
    );
    
    
    render(<App />, document.getElementById('root'));
    

    编码愉快!!!

    【讨论】:

    • 这个库是否在其背景中使用了“dangerouslySetInnerHTML”?
    • react-html-parser 在后台不使用dangerouslySetInnerHTML;它使用htmlparser2
    • 问题是它没有呈现按钮本身。我有一个 specialbutton html 元素。该组件未呈现。 &lt;specialbutton color="red"&gt;My Button&lt;/specialbutton&gt;
    • @AlekseyYaremenko 对于自定义组件,您必须使用transform 选项:github.com/peternewnham/react-html-parser/issues/…
    【解决方案5】:

    正如 EsterlingAccimeYoutuber 在this 的回答中指出的那样,您可以使用parser,以防您不想使用dangerouslySetInnerHTML 属性。

    到现在,react-html-parser已经3年没有更新了,所以我去寻找一个不同的模块。

    html-react-parser 做同样的工作,但经常维护和更新。

    清理html-String 以防止XSS 攻击应该是一个好习惯。 dompurify 可以用于此。

    我将 EsterlingAccimeYoutuber 的代码示例更新为以下内容:

    import React from 'react';
    import { render } from 'react-dom';
    import parse from 'html-react-parser';
    import DOMPurify from 'dompurify';
    
    const SpecialButton = ({ children, color }) => (
      <button style={{color}}>{children}</button>
    );
    
    const htmlFromCMS = `
    <div>Hi, 
      <SpecialButton color="red">My Button</SpecialButton>
    </div>`;
    
    const htmlFrom = (htmlString) => {
            const cleanHtmlString = DOMPurify.sanitize(htmlString,
              { USE_PROFILES: { html: true } });
            const html = parse(cleanHtmlString);
            return html;
    }
    
    const App = () => (
      <div>
         {htmlFromCMS && htmlFrom(htmlFromCMS)}
      </div>
    );
    
    
    render(<App />, document.getElementById('root'));
    

    受上述原帖启发,特此感谢原作者!

    【讨论】:

    • 感谢您指出维护良好的库 html-react-parser 而不是 react-html-parser
    猜你喜欢
    • 2018-10-29
    • 2019-06-13
    • 2020-06-12
    • 2022-01-20
    • 2019-09-21
    • 1970-01-01
    • 2016-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多