6

我有一个相当标准的 lerna monorepo,它看起来像这样:

packages/
    main/  - This is the main deployable application, it depends on both dep and react-dep
    dep/   - Just some pure functions, no problems here
    react-dep/ - A design system built with react, this is where we have problems. 

因此,一个真正常见的问题是,一旦您开始在依赖库中使用钩子,您就会收到以下消息:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and fix this problem.

这是因为您的应用程序中有两个版本的 React,一个来自主应用程序,一个来自依赖项。

现在 - 我已经使用和工作的一个常见解决方案是在你的 webpack 配置中声明react和任何其他共享/对等依赖项。例如,正如这里所建议的那样。或查看来自 react 的这个 Github 问题线程externals

但是,我不喜欢这个解决方案,首先,如果我不使用 webpack 怎么办,其次我不应该手动跟踪我需要标记为外部的依赖项。

我认为应该起作用的是:

react-dep我声明react两者devDependenciespeerDependencies。我把它放进去的原因devDependencies是因为我的依赖库可能使用故事书或类似的东西来开发组件,所以我确实需要在开发中做出反应。

我认为如果我要发布react-dep到 npm 并使用 npm in 中的编译代码,这应该可以工作main,因为只会dependencies获取。

但是,我认为由于 lerna 符号链接,在这种情况下发生的情况是 dev 依赖仍然存在,我们得到了这个错误。

有没有办法为 lerna monorepo 解决这个问题?

这是演示此问题的 github 存储库:https ://github.com/dwjohnston/lerna-react-monorepo

4

1 回答 1

2

正如我所看到的,这个问题可能可以使用lerna、或来解决。 我想在那里提出另一种解决方案,为您的回购打开了一个公关。如果解决方案不适合您 - 请忽略此答案。npmyarnwebpack
webpackwebpack

它比externals机制好一点,因为它会自动跟踪重叠的对等依赖关系。

module.exports = function(config) {
    config.plugins.push(
        new NormalModuleReplacementPlugin(re, function(resource) {
            // first, remove babel and other loaders paths
            const requestWithoutLoaderMeta = resource.request.split('!');
            const requestPath = requestWithoutLoaderMeta.length && requestWithoutLoaderMeta[requestWithoutLoaderMeta.length - 1];

            if (requestPath) {
                // looking for a dependency and package names
                const packagesPath = resolve(__dirname, '../') + '/';
                const requestPathRel = requestPath.replace(packagesPath, '');
                const [packageName, _, depName] = requestPathRel.split('/');

                // if the main package has this dependency already - just use it
                if (dependencies[packageName]) {
                    console.log('\x1b[35m%s\x1b[0m', `[REPLACEMENT]: using dependency <${depName}> from package [main] instead of [${packageName}]`);
                    resource.request = resource.request.replace(`${packageName}/node_modules/${depName}`, `main/node_modules/${depName}`)
                }
            }
        })
    );

    return config;
}

此代码将main使用webpack.NormalModuleReplacementPlugin.

关于 webpack 的注意事项:由于所有三个前端大王都在他们的 CLI(angular、react、vue)中使用它,我认为您可以轻松安全地使用它进行此类自定义。

我很高兴听到有关解决此问题的替代技术(例如纱线)。

@nrwl/nx试试你的下一个 monorepo 而不是lerna.
主要区别在于nrwl项目通常package.json只有一个(使用谷歌经验),因此您需要为所有包安装一次依赖项,并且不会遇到您描述的问题。

于 2020-06-02T12:23:05.370 回答