【问题标题】:Use fs module in React.js,node.js, webpack, babel,express在 React.js、node.js、webpack、babel、express 中使用 fs 模块
【发布时间】:2017-02-20 08:25:10
【问题描述】:

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

在努力实现这一点后,我发现我必须使用同构或通用 javascript,即在服务器端使用 react 和 render,因为我们不能在客户端使用 fs 模块。 Referred this for server side.

我运行它使用:npm run start

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

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

如何将 fs 模块与 react 一起使用?

下面是sn-ps代码:

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(反应组件)

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:

我进行了更多调查,发现如果我不使用 npm run build 显式构建我的应用程序并且只更新我的反应组件,我不会得到上述错误。 此外,在此之后,如果我将文件创建逻辑直接放在渲染方法中并在刷新页面时成功创建文件。 所以观察它不适用于按钮的 Onclick 并且如果我们刷新页面就可以工作。它进入服务器,这就是它以这种方式工作的原因。

编辑 2:

通过在我的 webpack 配置中使用 target:'node' 解决了页面刷新问题,但我确实收到了错误:

Uncaught ReferenceError: require is not defined

在 browser.so 文件创建逻辑中,直接在 render 方法中将在我们访问页面的那一刻创建文件。无需刷新。

谁能指导我实现我想要的要求的最佳方法是什么?

【问题讨论】:

  • 当您拖放要上传的文件时,您认为 Dropbox 会做什么?他们当然将该文件存储在他们的服务器上,但不使用服务器端渲染。您正在寻找的是两层应用程序架构。

标签: javascript node.js reactjs webpack isomorphic-javascript


【解决方案1】:

错误

首先让我们稍微检查一下你的错误:

当你不使用npm run buildnpm run start 时,你将不会使用webpack,因此require 语句不会被fs 模块的内容替换——而是你会离开使用 require 语句,您的浏览器不理解该语句,因为 require 是仅限节点的函数。因此,您关于 require 的错误没有被定义。

如果您确实使用npm run buildnpm run start 运行,webpack 会取出该 require 语句并将其替换为 fs 模块。但是,正如您所发现的,fs 在客户端不起作用。

替代品

那么,如果你不能使用fs来保存文件,你能怎么办?

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

如果您尝试将表单保存在本地,即与浏览器在同一设备上,您需要使用另一种策略,如 this 或使用客户端库,如 FileSaver。您选择哪个选项在一定程度上取决于您的用例,但如果您尝试在客户端保存文件,您可以搜索“从 Web 浏览器保存文件”或“保存文件客户端”以查看适合您的选项。

【讨论】:

    猜你喜欢
    • 2016-05-14
    • 2021-11-27
    • 2018-01-05
    • 2011-09-30
    • 2017-09-06
    • 2016-02-01
    • 2017-04-15
    • 2018-01-08
    • 2023-04-05
    相关资源
    最近更新 更多