如何在使用Webpack进行开发时避免React重复加载

53

给定以下目录结构:

my-project
|
|-- node_modules
    |
    |-- react
    |-- module-x
        |
        |--node_modules
            |
            |--react
你可以看到my-projectmodule-x都需要React。我有与这个问题描述的相同问题,但建议是从package.json依赖项中删除React。我这样做了,一切正常,只要在module-x中没有安装node_modules,因为Webpack将使用my-project中的React。但是如果我正在开发module-x并且已经安装了node_modules,Webpack将同时使用来自my-projectmodule-x的React。
是否有办法使Webpack确保仅使用一个React实例,即使它在两个不同的级别上都被需要?
我知道我可以在开发时将module-x保持在单独的目录中,但这似乎意味着我必须发布它,然后在my-project中安装它进行测试,这并不高效。我想过npm link,但由于它仍然在module-x中安装了node_modules,所以没有成功。
这里提到的听起来很像我正在遇到的相同挑战,但似乎npm dedupe或Webpack的dedupe选项不起作用。我可能没有理解一些重要的细节。
4个回答

103

当使用npm link时,通常会出现这个问题。一个链接的模块将在自己的模块树中解析其依赖项,这与需要它的模块的模块树不同。因此,npm link命令会安装peerDependenciesdependencies

您可以使用resolve.alias强制require('react')解析为您本地版本的React。

resolve: {
  alias: {
    react: path.resolve('./node_modules/react'),
  },
},

我花了半天时间解决这个问题。不幸的是,无论是浏览器中的错误消息还是Webpack文档都没有什么帮助。你救了我的一天!谢谢!! - Ed_
3
这个解决方案仍会导致Webpack给从应用程序和链接模块中引用的React分配不同的ID,因此Webpack会创建多个React模块的实例。 - viskin
救了我。这里是文档:https://webpack.github.io/docs/configuration.html#resolve-alias - Mario Tacke

60

如果您不想(或无法)修改项目配置,有一个更简单的解决方案:只需npm link将React本身链接回到您的项目中:

# link the component
cd my-app
npm link ../my-react-component

# link its copy of React back to the app's React
cd ../my-react-component
npm link ../my-app/node_modules/react

1
这可能会变得混乱。库是一对多的(一个库安装在许多应用程序中),但是通过将该应用程序的 React 文件夹链接到库,这将库与仅一个应用程序相关联。因此,同时使用两个应用程序是不可能的(例如库的示例页面和消费者应用程序)。 - Matthias
@MatthiasDailey,如果我们有更多的项目,你有什么解决方法吗? - alek kowalczyk
2
@alek 我发现yalc很有用,如果你使用yarn,yarn工作区是另一个解决方案。 - Matthias
1
这个答案在我使用 CRA(Create React App)时救了我的一天。如果添加绝对路径的结果链接,这个答案会更容易理解。 - Möhre
1
@dan 我有一个共享的组件库,它被用于两个 Create React App 应用程序。我应该将它链接到哪一个? - Jon Rimmer
显示剩余3条评论

0

以防对其他人有用,上面提出的解决方案对我没有起作用,我不得不执行以下步骤来解决它:

在库中

  1. 将生成问题的库设置为package.json中的peerDependencies而不是dependenciesdevDependencies,例如在我的情况下react
"peerDependencies": {
  "react": "^16.8.6",
  ...
}
  1. 运行 npm install
  2. 构建库(在我的情况下,使用 rollup -c npm 脚本)

在我的主应用程序中

  1. 更改我的库的版本,以相对路径在 package.json 中指向我的本地项目,例如:
"dependencies": {
  "my-library": "file:../../libraries/my-library",
  ...
}
  1. resolve.symlinks = false 添加到我的主应用程序的 webpack 配置中

  2. --preserve-symlinks-main--preserve-symlinks 添加到我的 package.json 启动脚本中,例如:

"scripts": {
  "build": "set WEBPACK_CONFIG_FILE=all&& webpack",
  "start": "set WEBPACK_CONFIG_FILE=all&& webpack && node --preserve-symlinks-main --preserve-symlinks dist/server.js",
}
  • 运行 npm install
  • 运行 npm run start

  • 0
    在与此处已接受答案相同的思路下,以下是您如何使用Craco实现所需结果:
    const path = require('path')
    
    module.exports = {
      webpack: {
        configure: config => {
          config = {
            ...config,
            resolve: {
              ...config.resolve,
              alias: {
                ...config.resolve.alias,
                'react': path.resolve('./node_modules/react'),
              },
            },
          }
          // console.log(config)
          return config
        },
      },
    }
    

    需要注意的是,你不能仅仅将resolve作为一个键传递进去,你必须使用configure回调函数进行自己的深度合并。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接