【问题标题】:Share model between backend and frontend when using create-react-app使用 create-react-app 时在后端和前端之间共享模型
【发布时间】:2019-02-27 12:26:57
【问题描述】:

我使用create-react-app 创建了一个项目。 ./src 文件夹中描述了前端。我在项目的根目录上有一个名为 server.js 的文件。此文件使用 express 描述 API。

我想要一个包含模型的文件夹,该文件夹将在前端(./src 下)和后端(./server.js 下)之间共享。

我想分享的一个类的例子:

export default class DataModel {
    constructor(name) {
        this.name = name;
    }
}

如果我将这个类放在./src/models/DataModel.js 下,我可以通过import DataModel from '../models/DataModel';./src 中使用它,但我不能在./server.js 中使用它,因为它会给我以下错误:

意外的令牌导出

而且我不能将类直接放在我项目的根目录下,因为 create-react-app 不接受来自./src 文件夹之外的导入,并且会给我以下错误:

找不到模块:您尝试导入项目 src/ 目录之外的 ../../DataModel。不支持 src/ 之外的相对导入。


更新

我尝试使用react-app-rewired,但没有成功。

我的 package.json 已更新:

{
    //...
    "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  },
    "devDependencies": {
    "react-app-rewired": "^2.1.0"
  }
}

我在我的项目的根目录中添加了文件config-overrides.js(与 package.json 和我的 DataModel 类相同)。

/* config-overrides.js */

module.exports = function override(config, env) {
  delete config.resolve.plugins.ModuleScopePlugin;
  return config;
}

但我仍然有同样的问题:

找不到模块:您尝试导入项目 src/ 目录之外的 ../../DataModel。不支持 src/ 之外的相对导入。

更新 #2

我记录了通过override 函数传递的config,这是我得到的:

{
  "mode": "development",
  "devtool": "cheap-module-source-map",
  "entry": [
    "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\react-dev-utils\\webpackHotDevClient.js",
    "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\src\\index.js"
  ],
  "output": {
    "pathinfo": true,
    "filename": "static/js/bundle.js",
    "chunkFilename": "static/js/[name].chunk.js",
    "publicPath": "/"
  },
  "optimization": {
    "splitChunks": {
      "chunks": "all",
      "name": false
    },
    "runtimeChunk": true
  },
  "resolve": {
    "modules": [
      "node_modules"
    ],
    "extensions": [
      ".web.mjs",
      ".mjs",
      ".web.js",
      ".js",
      ".json",
      ".web.jsx",
      ".jsx"
    ],
    "alias": {
      "react-native": "react-native-web"
    },
    "plugins": [
      {
        "topLevelLoader": {}
      },
      {
        "appSrcs": [
          "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\src"
        ],
        "allowedFiles": {}
      }
    ]
  },
  "resolveLoader": {
    "plugins": [
      {}
    ]
  },
  "module": {
    "strictExportPresence": true,
    "rules": [
      {
        "parser": {
          "requireEnsure": false
        }
      },
      {
        "test": {},
        "enforce": "pre",
        "use": [
          {
            "options": {
              "formatter": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\react-dev-utils\\eslintFormatter.js",
              "eslintPath": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\eslint\\lib\\api.js",
              "baseConfig": {
                "extends": [
                  "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\eslint-config-react-app\\index.js"
                ],
                "settings": {
                  "react": {
                    "version": "999.999.999"
                  }
                }
              },
              "ignore": false,
              "useEslintrc": false
            },
            "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\eslint-loader\\index.js"
          }
        ],
        "include": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\src"
      },
      {
        "oneOf": [
          {
            "test": [
              {},
              {},
              {},
              {}
            ],
            "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\url-loader\\dist\\cjs.js",
            "options": {
              "limit": 10000,
              "name": "static/media/[name].[hash:8].[ext]"
            }
          },
          {
            "test": {},
            "include": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\src",
            "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\babel-loader\\lib\\index.js",
            "options": {
              "customize": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\babel-preset-react-app\\webpack-overrides.js",
              "babelrc": false,
              "configFile": false,
              "presets": [
                "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\babel-preset-react-app\\index.js"
              ],
              "cacheIdentifier": "development:babel-plugin-named-asset-import@0.2.3:babel-preset-react-app@6.1.0:react-dev-utils@6.1.1:react-scripts@2.1.1",
              "plugins": [
                [
                  "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\babel-plugin-named-asset-import\\index.js",
                  {
                    "loaderMap": {
                      "svg": {
                        "ReactComponent": "@svgr/webpack?-prettier,-svgo![path]"
                      }
                    }
                  }
                ]
              ],
              "cacheDirectory": true,
              "cacheCompression": false
            }
          },
          {
            "test": {},
            "exclude": {},
            "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\babel-loader\\lib\\index.js",
            "options": {
              "babelrc": false,
              "configFile": false,
              "compact": false,
              "presets": [
                [
                  "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\babel-preset-react-app\\dependencies.js",
                  {
                    "helpers": true
                  }
                ]
              ],
              "cacheDirectory": true,
              "cacheCompression": false,
              "cacheIdentifier": "development:babel-plugin-named-asset-import@0.2.3:babel-preset-react-app@6.1.0:react-dev-utils@6.1.1:react-scripts@2.1.1",
              "sourceMaps": false
            }
          },
          {
            "test": {},
            "exclude": {},
            "use": [
              "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\style-loader\\index.js",
              {
                "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\css-loader\\index.js",
                "options": {
                  "importLoaders": 1
                }
              },
              {
                "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\postcss-loader\\src\\index.js",
                "options": {
                  "ident": "postcss"
                }
              }
            ]
          },
          {
            "test": {},
            "use": [
              "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\style-loader\\index.js",
              {
                "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\css-loader\\index.js",
                "options": {
                  "importLoaders": 1,
                  "modules": true
                }
              },
              {
                "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\postcss-loader\\src\\index.js",
                "options": {
                  "ident": "postcss"
                }
              }
            ]
          },
          {
            "test": {},
            "exclude": {},
            "use": [
              "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\style-loader\\index.js",
              {
                "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\css-loader\\index.js",
                "options": {
                  "importLoaders": 2
                }
              },
              {
                "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\postcss-loader\\src\\index.js",
                "options": {
                  "ident": "postcss"
                }
              },
              "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\sass-loader\\lib\\loader.js"
            ]
          },
          {
            "test": {},
            "use": [
              "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\style-loader\\index.js",
              {
                "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\css-loader\\index.js",
                "options": {
                  "importLoaders": 2,
                  "modules": true
                }
              },
              {
                "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\postcss-loader\\src\\index.js",
                "options": {
                  "ident": "postcss"
                }
              },
              "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\sass-loader\\lib\\loader.js"
            ]
          },
          {
            "exclude": [
              {},
              {},
              {}
            ],
            "loader": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules\\file-loader\\dist\\cjs.js",
            "options": {
              "name": "static/media/[name].[hash:8].[ext]"
            }
          }
        ]
      }
    ]
  },
  "plugins": [
    {
      "options": {
        "template": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\public\\index.html",
        "templateContent": false,
        "filename": "index.html",
        "hash": false,
        "inject": true,
        "compile": true,
        "favicon": false,
        "minify": false,
        "cache": true,
        "showErrors": true,
        "chunks": "all",
        "excludeChunks": [],
        "chunksSortMode": "auto",
        "meta": {},
        "title": "Webpack App",
        "xhtml": false
      },
      "version": 4
    },
    {
      "replacements": {
        "NODE_ENV": "development",
        "PUBLIC_URL": "",
        "REACT_APP_DEFAULT_LANGUAGE": "fr"
      }
    },
    {
      "appPath": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT"
    },
    {
      "definitions": {
        "process.env": {
          "NODE_ENV": "\"development\"",
          "PUBLIC_URL": "\"\"",
          "REACT_APP_DEFAULT_LANGUAGE": "\"fr\""
        }
      }
    },
    {
      "options": {},
      "fullBuildTimeout": 200,
      "requestTimeout": 10000
    },
    {
      "options": {},
      "pathCache": {},
      "fsOperations": 0,
      "primed": false
    },
    {
      "nodeModulesPath": "C:\\Users\\me\\Documents\\nodejs\\MY_PROJECT\\node_modules"
    },
    {
      "options": {
        "resourceRegExp": {},
        "contextRegExp": {}
      }
    },
    {
      "opts": {
        "publicPath": "/",
        "basePath": "",
        "fileName": "asset-manifest.json",
        "transformExtensions": {},
        "writeToFileEmit": false,
        "seed": null,
        "filter": null,
        "map": null,
        "generate": null,
        "sort": null
      }
    }
  ],
  "node": {
    "dgram": "empty",
    "fs": "empty",
    "net": "empty",
    "tls": "empty",
    "child_process": "empty"
  },
  "performance": false
}

我在调用delete 操作之前记录了它,我们可以看到它们不是ModuleScopePlugin。那么为什么它仍然失败呢?

欢迎任何帮助。

谢谢。

【问题讨论】:

  • stackoverflow.com/questions/44114436/… 。并使用 CommonJS 导出而不是 ES 模块。
  • 但是需要eject。如果我不想弹出我的应用怎么办?
  • 你可以使用 react-app-rewired。它是一个或另一个。更通用的方法是制作私有 common 包并在两者中使用它。
  • 我不知道 react-app-rewired (我是新手)。我会尽快尝试,谢谢。
  • 我试过react-app-rewired,但它似乎不起作用。知道我做错了什么吗?

标签: reactjs model-view-controller


【解决方案1】:

共享代码是模块的用途。理想情况下,您会将模型打包成一个模块并安装/导入到每个代码库中。为了保持开发简单,您可能希望使用 Lerna 和 Yarn(或类似替代方案)的 monorepo 格式。

如果这超出了您的项目范围,那么我认为您应该能够在模型代码中使用 CommonJS 导出(如前所述),您将其保留在 React 应用程序 src 中并从您的服务器代码中获取。

./src/models/DataModel.js:

class DataModel {
  constructor(name) {
    this.name = name;
  }
}
module.exports = { DataModel };

来自 React 应用程序

import { DataModel } from "./models/DataModel";

来自服务器

const { DataModel } = require("./src/models/DataModel");

================================================ ============================

更新

据我所知,我们在这里遇到了一些深层次的 js-module 巫术。

我上面建议的代码在codesandbox中有效,但在本地无效。

我尝试将 react-scripts 和 react 版本与 codesandbox 中的版本进行匹配,但没有成功。我只能假设codeandbox有它自己的应对机制来缓解这个问题。

一旦使用 commonjs 导出在模块中定义了类,事情就会变得奇怪

module.exports = { DataModel };import {DataModel}Attempted import error: 'DataModel' is not exported from '../models/DataModel'.

module.exports = { DataModel };require 给出TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

exports.DataModel = DataModel;require 给出ReferenceError: exports is not defined

不过,将 class 换成老式的“班级”,并将 require 换成我们应用程序中的模块,一切正常。

function DataModel(name) {
  this.name = name;
}

DataModel.prototype.logName = function () {
  console.log(this.name);
}

module.exports = { DataModel };

这似乎与 webpack/babel/et al 为处理浮动的不同模块格式所执行的所有魔法有关,但我真的无法确定。

有一瞬间我以为这会解释它,但是..它没有..https://medium.com/webpack/webpack-4-import-and-commonjs-d619d626b655

【讨论】:

  • 其实在大多数情况下,共享代码可能是最好的解决方案,但出于内部原因并不是我们要做的(+项目很小,不需要拆分)。混合importrequire 的解决方案没问题,但我仍然有一个问题:我不能在React 应用程序中使用我的构造函数。如果我尝试new DataModel('my model');,则会收到以下错误:尝试导入错误:'DataModel' 未从'./models/DataModel' 导出。
  • 您是否使用了命名的导入格式,即import { DataModel },而不是默认的导入格式import DataModel
  • 我使用了命名的导入格式(就像你在示例中所做的那样)
  • 我不知道出了什么问题。也许你的 webpack 配置中有一些东西?我不明白为什么这不起作用,它在这里对我有用.. codesandbox.io/s/8l17wn84x0
  • 我的 webpack 配置是 create-react-app 给出的默认配置。但无论如何,感谢您的宝贵时间,我将继续为此寻找解决方案。
猜你喜欢
  • 2021-03-08
  • 2019-09-07
  • 1970-01-01
  • 2016-07-31
  • 1970-01-01
  • 2022-11-20
  • 2018-01-15
  • 1970-01-01
  • 2021-10-28
相关资源
最近更新 更多