通过变量呈现基本 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
一起使用,我从未使用过它。根据这个错误,我猜它是在错误的目录中查找你的文件,所以再次说明,可能是配置问题。
我正在尝试将 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
一起使用,我从未使用过它。根据这个错误,我猜它是在错误的目录中查找你的文件,所以再次说明,可能是配置问题。