在React.js,node.js,webpack,babel,express中使用fs模块

我有一个要求,我正在呈现视图,其中我显示一个表单。 在提交表单时,我需要收集表单数据并创build一个文件,并将表单数据保存为该文件中的JSON。 我正在使用React.js,node.js,babel和webpack。

挣扎了一下,实现了这一点,我想到了,我必须使用同构或通用JavaScript即使用反应和渲染在服务器端,因为我们不能在客户端使用FS模块。 推荐这个服务器端 。

我运行它使用: npm run start

在这之后,我可以在控制台中看到[Object Object]从下面的反应组件(HomePage.js)的第1行打印在控制台上。 但是稍后当我访问这个页面时,会出现一个错误:

'bundle.js:18未捕获错误:找不到模块“fs”“

我如何使用FS模块的反应?

以下是代码片段:

webpack.config.js

 "use strict"; const debug = process.env.NODE_ENV !== "production"; const webpack = require('webpack'); const path = require('path'); module.exports = { devtool: debug ? 'inline-sourcemap' : null, entry: path.join(__dirname, 'src', 'app-client.js'), devServer: { inline: true, port: 3333, contentBase: "src/static/", historyApiFallback: true }, output: { path: path.join(__dirname, 'src', 'static', 'js'), publicPath: "/js/", filename: 'bundle.js' }, module: { loaders: [{ test: path.join(__dirname, 'src'), loader: ['babel-loader'], query: { //cacheDirectory: 'babel_cache', presets: debug ? ['react', 'es2015', 'react-hmre'] : ['react', 'es2015'] } }] }, plugins: debug ? [] : [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) }), new webpack.optimize.DedupePlugin(), new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false }, mangle: true, sourcemap: false, beautify: false, dead_code: true }), ] }; 

的package.json

 { "name": "sample", "version": "1.0.0", "description": "Simple application to showcase how to achieve universal rendering and routing with React and Express.", "main": "src/server.js", "scripts": { "start": "SET NODE_ENV=production&&babel-node src/server.js", "start-dev": "npm run start-dev-hmr", "start-dev-single-page": "node_modules/.bin/http-server src/static", "start-dev-hmr": "webpack-dev-server --progress --inline --hot", "build": "SET NODE_ENV=production&&webpack -p" }, "dependencies": { "babel-cli": "^6.11.4", "babel-core": "^6.13.2", "babel-loader": "^6.2.5", "babel-plugin-react-html-attrs": "^2.0.0", "babel-preset-es2015": "^6.13.2", "babel-preset-react": "^6.11.1", "babel-preset-react-hmre": "^1.1.1", "ejs": "^2.5.1", "express": "^4.14.0", "react": "^15.3.1", "react-dom": "^15.3.1", "react-router": "^2.6.1" }, "devDependencies": { "http-server": "^0.9.0", "react-hot-loader": "^1.3.0", "webpack": "^1.13.2", "webpack-dev-server": "^1.14.1" } } 

server.js

 use strict'; import path from 'path'; import { Server } from 'http'; import Express from 'express'; import React from 'react'; import { renderToString } from 'react-dom/server'; import { match, RouterContext } from 'react-router'; import routes from './routes'; import NotFoundPage from './components/NotFoundPage'; //import fs from 'fs'; //console.log("server" + fs); // initialize the server and configure support for ejs templates const app = new Express(); const server = new Server(app); app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); // define the folder that will be used for static assets app.use(Express.static(path.join(__dirname, 'static'))); // universal routing and rendering app.get('*', (req, res) => { match( { routes, location: req.url }, (err, redirectLocation, renderProps) => { //console.log("renderProps "+ Object.values(routes)); //console.log("req.url "+ req.url); // in case of error display the error message if (err) { return res.status(500).send(err.message); } // in case of redirect propagate the redirect to the browser if (redirectLocation) { return res.redirect(302, redirectLocation.pathname + redirectLocation.search); } // generate the React markup for the current route let markup; if (renderProps) { // if the current route matched we have renderProps markup = renderToString(<RouterContext {...renderProps}/>); } else { // otherwise we can render a 404 page markup = renderToString(<NotFoundPage/>); res.status(404); } // render the index template with the embedded React markup return res.render('index', { markup }); } ); }); // start the server const port = process.env.PORT || 3000; const env = process.env.NODE_ENV || 'production'; console.log(`Server starting on http://localhost:${port} [${env}]`) server.listen(port, err => { if (err) { return console.error(err); } console.info(`Server running on http://localhost:${port} [${env}]`); }); 

HomePage.js(React组件)

 import React from 'react'; import fs from 'fs'; import dateformat from 'dateformat'; console.log("home page" + fs); -- Line 1 class HomePage extends React.Component{ checkDirectory(directory, callback) { fs.stat(directory, function(err, stats) { //Check if error defined and the error code is "not exists" if (err && err.errno === 34) { //Create the directory, call the callback. fs.mkdir(directory, callback); } else { //just in case there was a different error: callback(err) } }); } handleClick(){ var obj = JSON.stringify($('#statusForm').serializeArray()); this.checkDirectory("directory/"+currentDate, function(error) { if(error) { console.log("oh no!!!", error); } else { //Carry on, all good, directory exists / created. fs.writeFile("directory/"+currentDate+name+".json", obj, function(err) { if(err) { return console.log(err); } console.log("The file was saved!"); }); console.log("exists"); } });*/ } render() { return ( <div className="container"> <form id="statusForm" className="form-horizontal" > <div className="form-group"> <label className="control-label col-sm-2" for="names">Select list:</label> <div className="col-sm-10"> <select name="names" className="form-control" id="names"> <option>Select</option> <option>abc</option> <option>xyz</option> </select> </div> </div> <div className="form-group"> <label className="control-label col-sm-2" for="team">Select list:</label> <div className="col-sm-10"> <select name="team" className="form-control" id="team"> <option>Select</option> <option>team 1</option> <option>team 2</option> </select> </div> </div> <div className="form-group"> <label className="control-label col-sm-2" for="pwd">Password:</label> <div className="col-sm-10"> <input type="textarea" className="form-control" id="todayTask" name="todayTask" placeholder="Enter Task"/> </div> </div> <div className="form-group"> <div className="col-sm-offset-2 col-sm-10"> <button type="button" className="btn btn-default" onClick={this.handleClick.bind(this)}>Submit</button> </div> </div> </form> </div> ); } } export default HomePage; 

编辑1:

我调查了更多,发现如果我不build立我的应用明确使用npm运行构build,只是更新我的反应组件,我不会得到上述错误。 此外,之后,如果我把文件创build逻辑直接在渲染方法和刷新页面,它成功地创build一个文件。 所以观察是不能用Onclick的button,并可以工作,如果我们刷新页面。 它去服务器,这就是为什么它这样工作。

编辑2:

页面刷新问题通过在我的webpackconfiguration使用目标:'节点'解决,但我得到的错误:

未捕获的ReferenceError:require是未定义的

在browser.so文件创build逻辑里面,render方法直接在我们访问页面的时候创build文件。 不需要刷新。

任何人都可以指导我什么是达到我所希望的要求的最佳方式?

错误

首先让我们仔细检查一下你的错误:

当你不使用npm run buildnpm run start ,你将不会使用webpack,因此require语句不会被fs模块的内容所取代 – 而是留下一个require语句,浏览器不理解,因为require是一个Node-only函数。 因此,你的错误需要不被定义。

如果使用npm run buildnpm run start ,webpack会将这个需要的语句取出并用fs模块replace它。 但是,正如你所发现的, fs在客户端不起作用。

备择scheme

所以,如果你不能使用fs来保存文件,你可以做什么?

如果要将文件保存到服务器,则必须将表单中的数据提交给节点服务器,并且节点服务器可以使用fs与服务器的文件系统交互以保存文件。

如果您尝试在本地保存表单,即与浏览器位于同一设备上,则需要使用另一种类似的策略或使用客户端库(如FileSaver) 。 你select哪一个选项取决于你的用例,但是如果你试图在客户端保存,你可以search“从web浏览器保存文件”或者“保存文件客户端”来查看哪些是适合你的。