在 React.js、node.js、webpack、babel、express 中使用 fs 模块 [英] Use fs module in React.js,node.js, webpack, babel,express

查看:24
本文介绍了在 React.js、node.js、webpack、babel、express 中使用 fs 模块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

在努力实现这一目标后,我发现我必须使用同构或通用 javascript,即在服务器端使用 react 和 render,因为我们不能在客户端使用 fs 模块.参考了这个服务器端.

我使用:npm run start

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

<块引用>

'bundle.js:18 Uncaught Error: 找不到模块fs"'

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

以下是代码片段:

webpack.config.js

"use strict";const debug = process.env.NODE_ENV !==生产";const webpack = require('webpack');const path = require('path');模块.出口 = {开发工具:调试?'inline-sourcemap' : null,条目:path.join(__dirname, 'src', 'app-client.js'),开发服务器:{内联:真实,端口:3333,contentBase: "src/static/",historyApiFallback: 真},输出: {路径:path.join(__dirname, 'src', 'static', 'js'),publicPath: "/js/",文件名:'bundle.js'},模块: {装载机:[{测试:path.join(__dirname, 'src'),loader: ['babel-loader'],询问: {//cacheDirectory: 'babel_cache',预设:调试?['react', 'es2015', 'react-hmre'] : ['react', 'es2015']}}]},插件:调试?[] : [新的 webpack.DefinePlugin({'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)}),新的 webpack.optimize.DedupePlugin(),新的 webpack.optimize.OccurenceOrderPlugin(),新的 webpack.optimize.UglifyJsPlugin({压缩:{警告:假},mangle:真的,源图:假,美化:假,死代码:真}),]};

package.json

{"name": "样品",版本":1.0.0","description": "简单的应用,展示了如何使用 React 和 Express 实现通用渲染和路由.","main": "src/server.js",脚本":{"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"},依赖关系":{"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","快递": "^4.14.0",反应":^ 15.3.1","react-dom": "^15.3.1",反应路由器":^ 2.6.1"},开发依赖":{"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';从路径"导入路径;从'http'导入{服务器};从快递"导入快递;从反应"导入反应;import { renderToString } from 'react-dom/server';从反应路由器"导入{匹配,RouterContext};从'./routes'导入路由;从 './components/NotFoundPage' 导入 NotFoundPage;//从'fs'导入fs;//console.log("server" + fs);//初始化服务器并配置对 ejs 模板的支持const app = new Express();const 服务器 = 新服务器(应用程序);app.set('视图引擎', 'ejs');app.set('views', path.join(__dirname, 'views'));//定义将用于静态资产的文件夹app.use(Express.static(path.join(__dirname, 'static')));//通用路由和渲染app.get('*', (req, res) => {比赛({ 路线,位置:req.url },(错误,redirectLocation,renderProps)=>{//console.log("renderProps"+ Object.values(routes));//console.log("req.url "+ req.url);//出现错误时显示错误信息如果(错误){返回 res.status(500).send(err.message);}//在重定向的情况下,将重定向传播到浏览器如果(重定向位置){返回 res.redirect(302, redirectLocation.pathname + redirectLocation.search);}//为当前路由生成 React 标记让标记;如果(渲染道具){//如果当前路由匹配,我们有 renderPropsmarkup = renderToString(<RouterContext {...renderProps}/>);} 别的 {//否则我们可以渲染一个 404 页面标记 = renderToString();res.status(404);}//使用嵌入的 React 标记渲染索引模板return res.render('index', { markup });});});//启动服务器const port = process.env.PORT ||3000;const env = process.env.NODE_ENV ||'生产';console.log(`服务器开始于 http://localhost:${port} [${env}]`)server.listen(端口,错误 => {如果(错误){返回 console.error(err);}console.info(`服务器运行在 http://localhost:${port} [${env}]`);});

HomePage.js(React 组件)

从'react'导入React;从 'fs' 导入 fs;从日期格式"导入日期格式;console.log("主页" + fs);-- 第 1 行class HomePage 扩展了 React.Component{checkDirectory(目录,回调){fs.stat(目录,功能(错误,统计){//检查是否定义了错误且错误代码为不存在"如果(错误&& err.errno === 34){//创建目录,调用回调.fs.mkdir(目录,回调);} 别的 {//以防万一有不同的错误:回调(错误)}});}句柄点击(){var obj = JSON.stringify($('#statusForm').serializeArray());this.checkDirectory("directory/"+currentDate, function(error) {如果(错误){console.log("哦不!!!", 错误);} 别的 {//继续,一切顺利,目录存在/创建.fs.writeFile("directory/"+currentDate+name+".json", obj, function(err) {如果(错误){返回 console.log(err);}console.log("文件已保存!");});console.log("存在");}});*/}使成为() {返回 (<div className="容器"><form id="statusForm" className="form-horizo​​ntal" ><div className="form-group"><label className="control-label col-sm-2" for="names">选择列表:</label><div className="col-sm-10"><select name="names" className="form-control" id="names"><option>选择</option><option>abc</option><option>xyz</option></选择>

<div className="form-group"><label className="control-label col-sm-2" for="team">选择列表:</label><div className="col-sm-10"><select name="team" className="form-control" id="team"><option>选择</option><option>team 1</option><option>team 2</option></选择>

<div className="form-group"><label className="control-label col-sm-2" for="pwd">密码:</label><div className="col-sm-10"><input type="textarea" className="form-control" id="todayTask" name="todayTask" placeholder="输入任务"/>

<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)}>提交</button>

</表单>

);}}导出默认主页;

编辑 1:

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

编辑 2:

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

<块引用>

未捕获的 ReferenceError: require 未定义

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

任何人都可以指导我实现所需要求的最佳方法是什么?

解决方案

错误

首先让我们稍微回顾一下您的错误:

当您不使用 npm run buildnpm run start 时,您将不会使用 webpack,因此 require 语句不会不会被 fs 模块的内容替换——相反,你会留下一个 require 语句,你的浏览器无法理解它,因为 require 是一个 Node-only 函数.因此,您关于 require 未定义的错误.

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

替代方案

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

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

如果您尝试在本地保存表单,即在与浏览器相同的设备上,您需要使用另一种策略,例如 或使用客户端库,如 FileSaver.您选择哪个选项取决于您的用例,但如果您尝试在客户端保存,您可以搜索从 Web 浏览器保存文件".或保存文件客户端"看看什么对你有用.

I have a requirement in which I am rendering view in which I display a form. On submit of form i need to gather the form data and create a file and save form data as JSON in that file. I am using React.js, node.js, babel and webpack.

After struggling a bit to achieve this I figured out that I have to use isomorphic or universal javascript i.e use react and render on server side as we cannot use fs module on client side. Referred this for server side.

i run it using: npm run start

After this I can see in console that [Object Object] is printed on console from Line 1 in below react component (HomePage.js). But later on when I access this page it gives an error:

'bundle.js:18 Uncaught Error: Cannot find module "fs"'

How can i use fs module with react ?

Below are code snippets :

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 component)

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;

EDIT 1:

I investigated more and found out that if i do not build my app explicitly using npm run build and just update my react component i do not get above error. Also, after this if i put file creation logic directly inside render method and on refreshing page it successfully create a file. So observation is it does not work with Onclick of button and can work if we refresh the page. it goes to server and thats why it works this way.

EDIT 2:

Page refresh issue resolved by using target:'node' in my webpack config but I do get the error:

Uncaught ReferenceError: require is not defined

In browser.so file creation logic directly inside render method will create the file the moment we access the page. No refresh required.

Can anyone guide me what is the best way to achieve my desired requirement?

解决方案

Errors

First let's go through your errors a little bit:

When you don't use npm run build or npm run start, you won't use webpack and therefore the require statement doesn't get replaced with the contents of the fs module--instead you are left with a require statement, which your browser doesn't understand since require is a Node-only function. Thus, your error about require not being defined.

If you do run with npm run build or npm run start, webpack takes that require statement out and replaces it with the fs module. But, as you've discovered, fs doesn't work on the client side.

Alternatives

So, if you can't use fs to save files, what can you do?

If you are trying to save the file to a server, you have to submit data from your form to the Node server, and the Node server can use fs to interact with the server's filesystem to save the file.

If you are trying to save the form locally, i.e., on the same device as the browser, you need to use another strategy like this or by using a client-side library like FileSaver. Which option you take depends somewhat on your use case, but if you are trying to save on the client side you can search around "saving files from web browser" or "saving files client side" to see what works for you.

这篇关于在 React.js、node.js、webpack、babel、express 中使用 fs 模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆