将next.js与yarn工作区结合使用



今天遇到了类似的项目结构的复杂性

packages
/app
pages/
package.json
/ui-kit
pages/
package.json
/shared
.babelrc
package.json

root lvl包json定义了workspaces: [packages/*],其中appui-kit都是nextjs应用程序。

我在root lvl包.json 中有以下脚本

"dev:app": "next packages/app",
"dev:ui-kit": "next packages/ui-kit"

这两种方法都很好,直到我引入了shared文件夹,它基本上包含了一些在包之间重复使用的函数/组件等。一旦我把它包括在appui-kit中,我就会得到这样的错误

中/packages/shared/index.js

模块解析失败:意外的令牌(4:21)您可能需要处理此文件类型的适当加载程序。|从导入React'react'||导出默认值()=>你好,共享!|

所以看起来nextjs没有将任何加载器应用到它所指向的文件夹之外的任何东西。有解决方案吗?即从根文件夹开始下一步,但根据不同的脚本命令将其指向不同的条目文件?

由于NextJs 11,有一个新的实验选项称为externalDir,它工作得很好,不需要使用下一个transfile模块。

为了清晰起见,让我们一步一步地做一个如何操作,这看起来可能是一个漫长的过程,但一旦你得到了它,它就很容易了(实际上是3步)


1.纱V3(可选)

为了提高体验,我建议将yarn升级到v3+(yarn set version 3.0.2 && yarn plugin import workspace-tools),并编辑生成的配置.yarnrc.yml,类似于以下内容:

# Yarn 2+ supports pnp or regular node_modules installs. Use node-modules one.
nodeLinker: node-modules
nmMode: hardlinks-local
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
yarnPath: .yarn/releases/yarn-3.0.2.cjs

PS:你可能也想把这个添加到.gitignore

.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
.pnp.*

为什么因为您可以使用工作区:别名协议(在pnpm中也可用)


2.严格的工作空间拓扑结构(可选)

我建议对包裹所依赖的东西要严格(要有明确的界限)。这不是一个绝对的要求,但这是一个很好的实践,可以避免难以调试的情况。

为了帮助包管理器,我建议正确地声明每个应用程序/包的依赖项及其边界。

换句话说,每个软件包/应用程序都有自己的软件包.json,您可以在其中显式地添加他们需要的dep(而不是在rootpackage.json中)

以你为例,

apps/
packages
/app
package.json (app depend on ui-kit through yarn workspace: alias)
tsconfig.json (we will add typescript path aliases there too) 
next.config.js
/ui-kit
package.json
package.json (do not put nextjs as dep here, only in app)

package.json示例

{
"name": "monorepo",
"private": true,
"workspaces": [
"packages/*"  // Enable package discovery in packages/* directory.
],
"devDependencies": {
"husky": "7.0.2", // Only what's needed for monorepo management
}

packages/app/package.json的一个例子

{
"name": "my-app",
"devDependencies": {
"@types/node": "16.10.1",
"@types/react": "17.0.29",
"@types/react-dom": "17.0.9",
"typescript": "4.4.4"
},
"dependencies": {
// Assuming the name of packages/ui-kit is ui-kit,
// we explicitly declare the dependency on it through
// workspace: alias (package-manager perspective)
"ui-kit": "workspace:*",
"next": "11.1.2",
"react": "17.0.2",
"react-dom": "17.0.2",
}
}

为什么这样你就不会遇到与dep冲突的奇怪问题。


3.打字别名

即使您没有使用typescript,NextJs也会读取tsconfig.json并查找typescript路径映射配置。如果你不知道它是什么……它只是一个配置,你可以(再次)声明你的dep。Nextjs将把它们转换为它在后台编译deps时使用的内容(即:babel插件模块解析器,可能还有稍后的swc)。

按照您的示例,只需以这种方式编辑./packages/app/tsconfig.json

{
"compilerOptions": {
// here baseUrl is set at ./src (good practive), can
// be set to '.'  
"baseUrl": "./src",
"paths": {
// Declare deps here (keep them in sync with what
// you defined in the package.json)
// PS: path are relative to baseUrl
"ui-kit/*": ["../../ui-kit/src/*"],
// if you have a barrel in ui-lib 
"ui-kit": ["../../ui-kit/src/index"],
}
},
}

为什么更多的是工具之间的限制(包管理器和路径有不同的视角)


4.Nextjs配置

packages/app/nextjs.config.js中,启用externalDir配置(目前处于实验阶段,但运行良好,此处为反馈线程)

const nextConfig = {
experimental: {
// this will allow nextjs to resolve files (js, ts, css)
// outside packages/app directory. 
externalDir: true,
},
};
export default nextConfig;

PS:对于较旧的nextjs版本,完全可以通过自定义的webpack配置进行同样的操作。问你是否需要一个例子。


您将得到什么

在你的应用程序中,你应该能够像这样导入你的ui套件:

import { Button } from 'ui-kit';
// or
import Avatar from 'ui-kit/components/Avatar'

其美妙之处在于快速刷新将开箱即用(无需构建)。它很快,你不需要NX(+昂贵的NX.cloud)、匆忙或任何事情。。。

Nextjs只需导入文件,根据需要构建文件,甚至将其缓存在自己优化的缓存中(尤其是在webpack 5中速度更快,也可以在CI上启用)。。。

如果你喜欢更多信息,我维护了一个示例存储库,它将对这个repo进行全生命周期透视(ci、github-action、linters、deployment…):https://github.com/belgattitude/nextjs-monorepo-example.

附言:在这里也关注纱线3+的开发和版本,他们现在做得很好。

问题是下一个文件夹之外的代码没有被Babel转换。这是因为.babelrc文件没有被考虑在内,即使它位于代码的根目录中。

但是,如果您使用babel.config.js文件(从Babel版本7开始推荐)并将其放在代码的根目录(有效地替换了.babelrc文件),则可以使用以下插件:

https://github.com/josephluck/next-plugin-custom-babel-config

我已经做到了,而且效果很好!

另一个答案对Next.js 9.2+不起作用。我不得不使用一个分叉包,而不是称为Next-transfile模块。

您真正需要做的就是将yarn add next-transpile-modules添加到每个包中,然后使用以下内容添加/编辑next.config.js

// next.config.js
const withTM = require('next-transpile-modules')(['somemodule', 'and-another']); // pass the modules you would like to see transpiled
module.exports = withTM();

如果您想使用Next.js默认值,您甚至可能不需要再将babel.config.js放入根目录。

该包的工作方式是实现一个自定义的Webpack配置,如官方文档中所述,告诉Webpack监视您在上面的next.config.js中定义的模块的目录。

尝试将其添加到next.config.js中,以启用下一个根文件夹之外的编译typescript文件:

module.exports = {
// ... other settings
experimental: {
externalDir: true,
},
}

信用额度:https://github.com/belgattitude/nextjs-monorepo-example#step-33下一次配置

最新更新