使用 create-react-app 时在后端和前端之间共享模型 [英] Share model between backend and frontend when using create-react-app
问题描述
我使用 create-react-app 创建了一个项目../src
文件夹中描述了前端.我在项目的根目录上有一个名为 server.js
的文件.此文件使用 express 描述 API.
I created a project using create-react-app. The front-end is described inside the ./src
folder. I have a file named server.js
on the root of the project. This file is describing the API using express.
我想要一个文件夹,其中包含将在前端(./src
下)和后端(./server.js<下)之间共享的模型/code>).
I would like to have a folder with models that would be shared between the front-end (under ./src
) and the back-end (under ./server.js
).
我想分享的一个类的例子:
An example of a one of the classes that I would like to share:
export default class DataModel {
constructor(name) {
this.name = name;
}
}
如果我把这个类放在 ./src/models/DataModel.js
下,我可以在 ./src
中使用它,方法是使用 import DataModel from '../models/DataModel';
但我不能在 ./server.js
中使用它,因为它给了我以下错误:
If I put this class under ./src/models/DataModel.js
, I can use it inside ./src
by using import DataModel from '../models/DataModel';
but I can't use it inside ./server.js
as it gives me the following error:
意外的令牌导出
而且我不能将类直接放在我的项目的根目录下,因为 create-react-app 不接受从 ./src
文件夹外部导入,并且会给我以下错误:
And I can't put the class directly under to the root of my project as create-react-app do not accept import from outside the ./src
folder and will give me the following error:
未找到模块:您试图导入位于项目 src/目录之外的 ../../DataModel.不支持 src/之外的相对导入.
Module not found: You attempted to import ../../DataModel which falls outside of the project src/ directory. Relative imports outside of src/ are not supported.
<小时>
更新
我尝试使用 react-app-rewired
但没有成功.
我的 package.json 已更新:
My package.json has been updated:
{
//...
"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 类处于同一级别).
And I added the file config-overrides.js
on the root of my project (same level as package.json and my DataModel class).
/* config-overrides.js */
module.exports = function override(config, env) {
delete config.resolve.plugins.ModuleScopePlugin;
return config;
}
但我仍然有同样的问题:
But I still have the same issue:
未找到模块:您试图导入位于项目 src/目录之外的 ../../DataModel.不支持 src/之外的相对导入.
Module not found: You attempted to import ../../DataModel which falls outside of the project src/ directory. Relative imports outside of src/ are not supported.
更新 #2
我记录了通过 override
函数传递的 config
,这是我得到的:
Update #2
I logged the config
that is passed through the override
function and here is what I got:
{
"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
.那么为什么它仍然失败?
I log it before calling the delete
operation and as we can see their is no ModuleScopePlugin
. So why does it still failing?
欢迎任何帮助.
谢谢.
推荐答案
共享代码是模块的用途.理想情况下,您将模型打包到一个模块中并安装/导入到每个代码库中.为了保持开发简单,您可能需要使用 Lerna 和 Yarn(或类似替代品)的 monorepo 格式.
Sharing code is what modules are for. Ideally you would package up you models into a module and install/import into each codebase. To keep development simple you would probably want to utilise a monorepo format using Lerna and Yarn (or similar alternatives).
如果这超出了您的项目范围,那么我认为您应该能够在模型代码中使用 CommonJS 导出(如之前建议的那样),您将其保存在 React 应用程序 src 中并从您的服务器代码中要求.
If that's beyond the scope of your project then I think you should be able to get away with using CommonJS exports in your model code (as previously suggested) which you keep in the React app src and require from your server code.
./src/models/DataModel.js
:
class DataModel {
constructor(name) {
this.name = name;
}
}
module.exports = { DataModel };
来自 React 应用
from React app
import { DataModel } from "./models/DataModel";
来自服务器
const { DataModel } = require("./src/models/DataModel");
============================================================================
=========================================================================
据我所知,我们在这里遇到了一些深入的 js 模块伏都教.
So as far as I can tell we're hitting some deep js-module voodoo here.
我上面建议的代码在 codeandbox 中有效,但在本地无效.
The code I suggested above works in codesandbox, but not locally.
我尝试将 react-scripts 和 react 版本与 codeandbox 中的那些匹配,但没有成功.我只能假设 codeandbox 有自己的应对机制来缓解这个问题.
I have tried matching the react-scripts and react versions with those in codesandbox with no success. I can only assume that codesandbox has it's own coping mechanisms which alleviate the problem.
一旦使用 commonjs 导出在模块中定义了类,
As soon as the class is defined in the module using the commonjs export, things get weird with
module.exports = { DataModel };
和 import {DataModel}
给出 尝试导入错误:'DataModel' 未从 '../models 导出/DataModel'.
module.exports = { DataModel };
和 require
给出 TypeError:无法分配给对象#
和
exports.DataModel = DataModel;
和 require
给出 ReferenceError:exports is not defined
将 class
换成老式的类",并require
应用程序中的模块,一切都按预期进行.
Swap out the class
for an old-school "class" though, and require
the module from our app, and everything works as expected.
function DataModel(name) {
this.name = name;
}
DataModel.prototype.logName = function () {
console.log(this.name);
}
module.exports = { DataModel };
这似乎与 webpack/babel/et al 执行的所有魔法有关,以处理浮动的不同模块格式,但我真的无法确定.
It seems it's to do with all the magic being performed by webpack/babel/et al to deal with the different module formats floating around, but I really couldn't nail it down.
有那么一瞬间我以为这会解释它,但..它没有.. https://medium.com/webpack/webpack-4-import-and-commonjs-d619d626b655
For a second I thought this was going to explain it, but.. it didn't.. https://medium.com/webpack/webpack-4-import-and-commonjs-d619d626b655
这篇关于使用 create-react-app 时在后端和前端之间共享模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!