javascript - 在同构 React 应用程序中渲染 HTML 字符串

标签 javascript node.js reactjs isomorphic-javascript

存在非 SPA 场景,使用经过净化但随机的 HTML 字符串作为输入:

<p>...</p>
<p>...</p>
<gallery image-ids=""/>
<player video-id="..."/>
<p>...</p>

该字符串源自 WYSIWYG 编辑器,包含嵌套的常规 HTML 标记和有限数量的应呈现给小部件的自定义元素(组件)。

目前像这样的 HTML 片段应该在服务器端 (Express) 单独呈现,但最终也会作为同构应用程序的一部分在客户端呈现。

我打算使用 React(或类似 React 的框架)来实现组件,因为它可能适合这种情况 - 它是同构的并且可以很好地呈现部分。

问题是像这样的子串

<gallery image-ids="[1, 3]"/>

应该变成

<Gallery imageIds={[1, 3]}/>

JSX/TSX 组件在某些时候,我不确定什么是正确的方法,但我希望它是一个常见的任务。

如何在 React 中解决这个问题?

最佳答案

通过解析 html 字符串并将生成的 Node 转换为 React 元素,经过清理的 HTML 可以转换为可以在服务器和客户端上运行的 React 组件。

const React = require('react');
const ReactDOMServer = require('react-dom/server');

const str = `<div>divContent<p> para 1</p><p> para 2</p><gallery image-ids="" /><player video-id="" /><p> para 3</p><gallery image-ids="[1, 3]"/></div>`;


var parse = require('xml-parser');

const Gallery = () => React.createElement('div', null, 'Gallery comp');
const Player = () => React.createElement('div', null, 'Player comp');

const componentMap = {
  gallery: Gallery,
  player: Player
};


const traverse = (cur, props) => {
  return React.createElement(
    componentMap[cur.name] || cur.name,
    props,
    cur.children.length === 0 ? cur.content: Array.prototype.map.call(cur.children, (c, i) => traverse(c, { key: i }))
  );
};

const domTree = parse(str).root;
const App = traverse(
   domTree
);

console.log(
  ReactDOMServer.renderToString(
    App
  )
);

但是请注意,正如您所提到的,您真正需要的不是 JSX/TSX,而是 React 渲染器的 React Node 树(在本例中为 ReactDOM)。 JSX 只是语法糖,来回转换它是不必要的,除非你想在你的代码库中维护 React 输出。

请原谅过度简化的 html 解析。它仅用于说明目的。您可能希望使用更符合规范的库来解析输入的 html 或适合您的用例的内容。

确保客户端包获得完全相同的 App 组件,否则你可能会 React 的客户端脚本重新创建 DOM 树,你将失去服务器端的所有好处渲染。

您也可以通过上述方法利用 React 16 的流式传输。

解决 Prop 问题

Prop 将作为属性从树中提供给您,并且可以作为 Prop 传递(当然要仔细考虑您的用例)。

const React = require('react');
const ReactDOMServer = require('react-dom/server');

const str = `<div>divContent<p> para 1</p><p> para 2</p><gallery image-ids="" /><player video-id="" /><p> para 3</p><gallery image-ids="[1, 3]"/></div>`;


var parse = require('xml-parser');

const Gallery = props => React.createElement('div', null, `Gallery comp: Props ${JSON.stringify(props)}`);
const Player = () => React.createElement('div', null, 'Player comp');

const componentMap = {
  gallery: Gallery,
  player: Player
};

const attrsToProps = attributes => {
  return Object.keys(attributes).reduce((acc, k) => {

    let val;
    try {
      val = JSON.parse(attributes[k])
    } catch(e) {
      val = null;
    }

    return Object.assign(
      {},
      acc,
      { [ k.replace(/\-/g, '') ]: val }
    );
  }, {});
};


const traverse = (cur, props) => {

  const propsFromAttrs = attrsToProps(cur.attributes);
  const childrenNodes = Array.prototype.map.call(cur.children, (c, i) => {

    return traverse(
      c,
      Object.assign(
        {},
        {
          key: i
        }
      )
    );
  });

  return React.createElement(
    componentMap[cur.name] || cur.name,
      Object.assign(
        {},
        props,
        propsFromAttrs
      ),
    cur.children.length === 0 ? cur.content: childrenNodes
  );
};

const domTree = parse(str).root;
const App = traverse(
  domTree
);

console.log(
  ReactDOMServer.renderToString(
    App
  )
);

但要小心自定义属性 - 您可能需要关注 this rfc .如果可能,坚持使用驼峰命名法。

关于javascript - 在同构 React 应用程序中渲染 HTML 字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45645424/

相关文章:

javascript - 将 php 页面 URL 中的变量传递给 JavaScript 和另一个 PHP

javascript - 为什么我在行驶路线时收不到数据?

javascript - 如何在 Jest 中为 ipcRenderer.on 和 ipcRenderer.send 编写单元测试?

ruby-on-rails - RSpec + Jasmine Node

javascript - 组件文件夹中 index.js 的好处

javascript - React-Native 获取,网络请求失败。不使用本地主机

javascript - jQuery:使用 select() 而不是 change() 选择 <option> 内容

javascript - 输入文本框上的正则表达式在 javascript 中不起作用

javascript - 如何导出多个类方法

node.js - PM2 无法获取 ~/.profile 文件中设置的更新值