如何让Webpack在Monorepo中按包根目录解析依赖?
Yarn 3 Monorepo中Webpack跨包模块路径解析失败问题
环境与配置
根目录package.json(片段)
{ "name": "myweb", "workspaces": [ "client/*" ] }
目录结构
client/ client-A/ index.js src/ components/ AThing1.jsx AThing2.jsx client-B/ src/ components/ BThing.jsx
关键文件代码
- client/client-A/index.js:
export { default as AThing1 } from './components/AThing1'
- client/client-A/src/components/AThing1.jsx:
import AThing2 from 'components/AThing2'
- client/client-B/src/components/BThing.jsx:
import { AThing1 } from '@myweb/client-A'
报错信息
在client-B中启动Webpack 5开发服务器时出现:
ERROR in ../client-A/src/components/AThing1.jsx ... Module not found: Error: Can't resolve 'components/AThing2' in '/workplace/termly_web/client/client-A/src/components/AThing1'
问题核心:Webpack在client-B的上下文解析client-A的模块时,会默认从client-B的src目录查找components/AThing2,而非client-A自身的src目录。
已尝试但无效的操作
- 将client/client-A/src路径添加到client-B的Webpack配置
resolve.modules和resolve.roots数组 - 设置
resolve.preferRelative = true
需求:不想修改client-A中所有导入语句为长相对路径(如../../../../components/Foo),也不想给导入添加@myweb/前缀。
现有配置文件
client/client-B/webpack.config.js(片段)
resolve: { modules: [ 'src', 'node_modules', ], roots: [ path.resolve(__dirname, 'src'), ], extensions: ['.js', '.jsx', '.scss', '.css', '.sass'], }, module: { rules: [ { test: /\.jsx?$/, exclude: [ { and: [/node_modules/], not: [/@mycompany\/react-components\/src/], }, ], loader: 'babel-loader', }, ] }
client/client-B/babel.config.js
module.exports = { presets: [ '@babel/preset-env', '@babel/preset-react', ], plugins: [ '@babel/plugin-proposal-class-properties', [ 'module-resolver', { alias: { 'test': './test', 'react': '../../node_modules/react', 'react-dom': '../../node_modules/react-dom', }, }, ], ], env: { production: { plugins: [ 'transform-react-remove-prop-types', ], }, }, }
解决方案
1. 给client-A添加自身的babel模块解析配置(推荐)
在client-A目录下创建babel.config.js,使用babel-plugin-module-resolver将src设为模块解析根目录,确保client-A内部的导入在任何上下文都能正确解析:
// client/client-A/babel.config.js module.exports = { plugins: [ ['module-resolver', { root: ['./src'], // 将src设为模块解析根目录 alias: { // 可选:直接给components设置别名,进一步明确路径 components: './src/components' } }] ] }
同时,必须给client-A添加自己的package.json,指定包名才能让Yarn正确识别工作区包:
// client/client-A/package.json { "name": "@myweb/client-A", "main": "index.js", "private": true // 标记为私有包,避免意外发布到npm }
2. 在client-B的Webpack中针对client-A配置别名
如果不想给client-A单独加配置,可以在client-B的Webpack配置中添加别名,让Webpack解析client-A中的components/xxx时指向client-A的src目录:
// client/client-B/webpack.config.js const path = require('path'); module.exports = { // ...其他配置 resolve: { alias: { // 避免和client-B自身的components目录冲突,精确匹配client-A的组件路径 '@myweb/client-A/components': path.resolve(__dirname, '../client-A/src/components') }, // ...原有resolve配置 } }
3. 利用Yarn Workspace的exports字段(需调整导入语句,可选)
在client-A的package.json中设置exports字段,定义模块导出路径:
// client/client-A/package.json { "name": "@myweb/client-A", "exports": { "./components/*": "./src/components/*", ".": "./index.js" } }
此时client-A内部可改为import AThing2 from '@myweb/client-A/components/AThing2',但此方法需要修改导入语句,不符合需求,仅作备选。
内容的提问来源于stack exchange,提问作者dkreft
相关产品推荐
相关产品推荐

