【问题标题】:Using next.js with yarn workspaces将 next.js 与纱线工作区一起使用
【发布时间】:2018-12-10 18:52:33
【问题描述】:

今天遇到这样的项目结构很复杂

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

root lvl package json 定义了workspaces: [packages/*],其中app 和ui-kit 都是nextjs 应用。

我在根 lvl package.json 中有以下脚本

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

在我介绍shared 文件夹之前,这两个都运行良好,该文件夹本质上包含一些功能/组件等......在包之间重复使用。一旦我将它包含到 app 或 ui-kit 中,我就会收到这样的错误

在 ./packages/shared/index.js

模块解析失败:意外令牌 (4:21) 您可能需要一个 适当的加载器来处理这种文件类型。 |从导入反应 '反应' | |导出默认()=>你好共享! |

所以看起来 nextjs 没有将任何加载器应用于它所指向的文件夹之外的任何内容。有没有办法以某种方式解决这个问题?即从根文件夹开始下一步,但基于不同的脚本命令以某种方式指向不同的入口文件?

【问题讨论】:

  • 如果用 NODE_PATH 设置根级别?
  • 没有解决,我认为下一个需要以某种方式在包内运行,但指向特定的应用程序

标签: javascript reactjs nextjs


【解决方案1】:

从 NextJs 11 开始,有一个名为 externalDir 的新实验选项运行良好,并且不需要使用 next-transpile-modules。

为了清楚起见,让我们一步一步地做,它可能看起来很长,但是一旦你掌握了它,它就很容易(实际上是 3 个步骤)


1。纱线 V3(可选)

为了改善体验,我建议升级yarn to 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.*

为什么?因为您可以使用workspace: alias protocol。 (也可以在 pnpm 中使用)


2。严格的工作区拓扑(可选)

我建议严格限制软件包所依赖的内容(有明确的界限)。这不是绝对要求,而是一种可以避免难以调试情况的良好做法。

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

换句话说,每个包/应用程序都有自己的 package.json,您可以在其中显式添加他们需要的 deps(不在根 package.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)

rootpackage.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",
  }
}

为什么?这样你就不会陷入与部门冲突的奇怪问题。


3。打字稿别名

即使您不使用 typescript,NextJs 也会读取 tsconfig.json 并查找 typescript path mapping 配置。如果您不知道它是什么......它只是一个您声明(再一次)您的部门的配置。 Nextjs 会将它们转换为它在后台编译 deps 时使用的内容(即:babel-plugin-module-resolver 和可能稍后的 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 配置(目前处于实验阶段,但效果很好,feedback thread here)

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-kit:

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

它的美妙之处在于快速刷新可以开箱即用(无需构建)。它很快,你不需要 NX(+ 昂贵的 nx.cloud)、rush 或任何东西......

Nextjs 将简单地导入文件,按需构建它们,甚至将它们缓存在它自己的优化缓存中(尤其是在 webpack 5 中速度很快,并且也可以在 CI 上启用)...

如果您想了解更多信息,我会维护一个示例存储库,该存储库将在此存储库上提供完整的生命周期视角(ci、github 操作、linter、部署...):https://github.com/belgattitude/nextjs-monorepo-example。

PS:也关注 yarn 3+ 开发和版本here,他们现在做得很好。

【讨论】:

    【解决方案2】:

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

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

    归功于:https://github.com/belgattitude/nextjs-monorepo-example#step-33-next-config

    【讨论】:

      【解决方案3】:

      This other answer 不适用于 Next.js 9.2+。我不得不使用一个名为 next-transpile-modules 的分叉包。

      您真正需要做的就是将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 配置 as described in the official documentation 来告诉 webpack 监视您在上面的 next.config.js 中定义的模块的目录。

      【讨论】:

        【解决方案4】:

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

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

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

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

        【讨论】:

        • 这是百万美元的答案。希望我能投票 10 倍。非常感谢。
        • Next 11 好像不行,你还有什么建议吗?
        • 事情已经发生了很大的变化,你不需要任何 babel 自定义,因为最近发布的 nextjs 11 有一个非常适用于 monorepo 的选项。有关说明,请参阅github.com/belgattitude/nextjs-monorepo-example 中的操作指南。顺便说一句,yarn 3+ 和 pnpm 支持工作空间:协议(“my-shared-lib”:“workspace:*”)。干净多了
        猜你喜欢
        • 2023-03-26
        • 2018-07-18
        • 2021-12-18
        • 2020-09-30
        • 2019-07-13
        • 1970-01-01
        • 2020-08-21
        • 1970-01-01
        • 2021-07-14
        相关资源
        最近更新 更多