【问题标题】:ReactJS errors with rendering basic html component via variable通过变量渲染基本 html 组件的 ReactJS 错误
【发布时间】:2015-08-19 13:34:51
【问题描述】:

我正在尝试将 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)

如下所述,我的设置是 web pack,我们使用 gocardless.com 的 Splash Pages 作为基础。我添加了 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' },
  ],
},

【问题讨论】:

  • 你能描述一下你的构建系统吗?似乎您使用的 webpack 没有适用于 JSX 的加载器。您用于构建的命令和配置文件会有所帮助。

标签: reactjs require fs


【解决方案1】:

1。启用 .jsx 文件的编译

当遇到以.jsx 结尾的文件时,通知 Webpack 使用 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

这里,测试正则表达式中的? 标记运算符表示“零或一”,因此它同时捕获了.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 了解与“纯” 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。

我的工作实现看起来像这样。请注意,我将模板作为道具传递,而不是在组件中计算它。我认为这是更惯用的 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 中

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


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

【讨论】:

  • 谢谢!我相信这是正确的道路,但是我遇到:ReferenceError: React is not defined at Object. (/Users/xxx/Sites/xxx/app/components/template-loader/static-templates/xxx.jsx :1:18) 在 Module._compile (module.js:456:26) 在 normalLoader (/Users/xxx/Sites/xxx/node_modules/babel-core/lib/babel/api/register/node.js:150: 5) 在 Object.require.extensions.(anonymous function) [as .jsx] (/Users/xxx/Sites/xxx/node_modules/babel-core/lib/babel/api/r 我假设 Babel 正在将 JSX 解析为新规则及其预期的 React 包含在所有模块中吗?
  • 非常感谢,这很有效,一旦我在整个项目中实现了这个,我会考虑接受挑战 4 :)
猜你喜欢
  • 2020-12-06
  • 2016-03-04
  • 1970-01-01
  • 1970-01-01
  • 2017-11-06
  • 2018-11-02
  • 2021-03-08
  • 1970-01-01
  • 2018-07-20
相关资源
最近更新 更多