通过变量呈现基本 html 组件的 ReactJS 错误

ReactJS errors with rendering basic html component via variable

我正在尝试将 jsx 模板名称的字符串传递给一个组件,该组件将负责只渲染基本的 html 组件,没有交互(我们有很多)。

'use strict';

import React from 'react';

export default class TemplateLoader extends React.Component {
  displayName = 'TemplateLoader'

  constructor(props) {
    super(props);
  }

  render() {
    var template = require('./static-templates/'+this.props.template+'.jsx');
    return template(this);
  }
}

并且控制台中的输出是

    ./app/components/template-loader/static-templates/xxx.jsx
Module parse failed: /Users/xxx/Sites/xxx/app/components/template-loader/static-templates/c017-top-phones-reasons.jsx Line 1: Unexpected token <
You may need an appropriate loader to handle this file type.
| <div class="xxx">
|
| </div>;
 @ ./app/components/template-loader/static-templates ^\.\/.*\.jsx$

如果我切换到

'use strict';

import React from 'react';
import jsx from 'react-jsx';
import fs from 'fs';

export default class TemplateLoader extends React.Component {
  displayName = 'TemplateLoader'

  constructor(props) {
    super(props);
  }

  render() {
    var file = fs.readFileSync('./static-templates/'+this.props.template+'.jsx');
    var template = jsx.server(file, 'utf-8');
    return template(this);
  }
}

我明白了

Error: ENOENT, no such file or directory './static-templates/xxx.jsx'
    at Object.fs.openSync (fs.js:438:18)
    at Object.fs.readFileSync (fs.js:289:15)

正如下面评论的那样,我的出发点是网络包,我们使用 gocardless.com 的启动页面作为基础。我添加了 html-loader、file-loader、css-loader。

我的文件配置有

node: {
  fs: "empty"
},
module: {
  loaders: [
    { test: /\.html$/, loader: "html-loader" },
    { test: /\.(jpe?g|png|gif|svg|eot|woff|ttf)$/, loader: 'file-loader' },
    { test: /\.js$/, exclude: /node_modules/, loaders: ['react-hot-loader', 'babel-loader'] },
    { test: /\.json$/, exclude: /node_modules/, loader: 'json-loader' },
    { test: /\.scss$/, loader: 'style-loader!css-loader!sass-loader' },
    { test: /\.css$/, loader: 'style-loader!css-loader' },
  ],
},

1。启用 .jsx 文件的编译

通知 Webpack 在遇到以 .jsx 结尾的文件时使用 babel-loader。参见 https://github.com/babel/babel-loader#usage

因此,请将以下内容添加到您的加载器列表中:

'{ test: /\.jsx$/, exclude: /node_modules/, loaders: ['react-hot-loader', 'babel-loader'] },'

修改您现有的测试,使其同时捕获 .js.jsx:

{ test: /\.jsx?$/, exclude: /node_modules/, loaders: ['react-hot-loader', 'babel-loader'] },
             ^^
              \- add x? here

在这里,测试正则表达式中的 ? 标记运算符表示 "zero or one",因此它捕获了 .js.jsx

2。根据 CommonJS 约定编写你需要的模块

这在您需要时不起作用,因为没有导出任何内容。默认情况下,CommonJS 模块中的所有内容都是该模块私有的,您必须显式导出您希望 require 的调用者接收的任何内容。所以这行不通:

<div class="panel">

</div>;

相反,导出 JSX 表达式:

module.exports = <div className="panel">
    I am static template 2!
</div>;

http://know.cujojs.com/tutorials/modules/authoring-cjs-modules

请记住,在 JSX 中,您不是直接使用 HTML。您需要使用 className 而不是 class。请参阅 https://facebook.github.io/react/docs/jsx-gotchas.html 以获取与 "plain" HTML.

的其他差异列表

3。让它与 React 工具一起工作

现在您的模板已找到、加载和编译,但它仍然无法呈现。您会收到此错误:Uncaught ReferenceError: React is not defined 或类似的错误。让我们看一下由 babel-loader 生成的 JavaScript,它将 JSX 转换为普通的 JavaScript:

"use strict";

module.exports = React.createElement(  // <-- error here
  "div",
  { className: "panel" },
  "I am a static template 2!"
);

所以 JSX 转换的输出期望 React 在范围内。这是有道理的,因为 JSX 只是编写 React.createElement(... 的一种方便语法。所以最后,为了使它们协同工作,您的模板需要如下所示:

import React from 'react';
// or var React = require('react');

module.exports = <div className="panel">
    I am a static template 2!
</div>

警告:我没有尝试直接使用 react-hot-loader,但它确实在 webpack-dev-server 下工作,我相信它使用 react-hot-loader。所以这应该可行,但它不会尝试删除 react-hot-loader。

我的工作实现如下所示。请注意,我将模板作为 prop 传入,而不是在组件中计算它。我认为这是更惯用的 React。这也使用了一些新的 ES6 语法来获得乐趣,但是你使用的是 babel 所以它应该没问题。不完全是你在做什么,但很接近。需要注意的一件事是,最好调用 require 一次(在构造函数中或 componentDidMount 中),而不是在 render 函数中调用,后者会被频繁调用。

var StaticTemplateComponent = React.createClass({
    render: function () {
        return this.props.template;
    }
});

window.mountStaticComponent = (templateName, element) => {
    var templatePath = `./static-templates/${templateName}.jsx`;
    var template = require(templatePath);

    var staticTemplateComponent = React.createElement(StaticTemplateComponent, {template: template});
    React.render(staticTemplateComponent, element);
};

4。自动将静态模板包装在所需的 CommonJS

超出问题范围,作为练习留给 reader。应该可以编写一个自定义的 webpack 加载程序,自动将您的模板包装在适当的 import/export 语句中。自动将 HTML5 转换为兼容的 JSX 的奖励积分。


至于为什么它不能与 jsx.server 一起使用,我从未使用过它。根据这个错误,我猜它是在错误的目录中查找你的文件,所以再次说明,可能是配置问题。