使用 Turborepo 管理的 Monorepo 项目中如何共享代码

上篇文章中介绍了 Turborepo 管理 Monorepo 项目的基本使用。其中提到按照常见的开发约束,我们一般会将完整的应用放在 apps 目录下,将共享代码的部分抽取分装成多个包放在 packages 目录下。在实际项目开发中,当项目增多和需求复杂,在不同项目中复用和共享公用代码是十分常见的最佳实践。

本文介绍在使用 Turborepo 管理的 Monorepo 项目中如何共享代码。主要参考以下几篇官方文档:

内部库和外部库(Internal Packages, External packages)

按照 Turborepo 文档 ,会先将公用代码区分为内部库和外部库(Internal Packages, External Packages

  • 内部库(Internal Packages),指只会在同一个 Monorepo 项目中使用的,通常很简单搭建起来,并且不需要经过打包(打包由引用的项目打包时一起打包)、发布版本环节提供给外部项目使用,这是本文主要讨论的使用方式。
  • 外部库(External packages),通常会经过打包、发布版本、推送到一个集中的 npm 仓库提供给不同的项目使用,在跨项目使用的场景下使用,比如团队内基础组件库

搭建一个内部库

1. 创建项目

bash 复制代码
mkdir packages/math-helpers

package.json 申明内部库名称,以及如果有需要的第三方依赖

json 复制代码
{
  "name": "math-helpers",
  "version": "0.0.1",
  "dependencies": {
    "typescript": "latest"
  }
}

创建 src 文件夹,并且导出公共代码。

如在 packages/math-helpers/src/index.ts 中创建 add, subtract 示例函数

typescript 复制代码
export const add = (a: number, b: number) => {
  return a + b;
};

export const subtract = (a: number, b: number) => {
  return a - b;
};

另外由于是 Typescript 项目,不要忘记配置 tsconfig.json

packages/math-helpers/tsconfig.json

json 复制代码
{
  "compilerOptions": {
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "preserveWatchOutput": true,
    "skipLibCheck": true,
    "noEmit": true,
    "strict": true
  },
  "exclude": ["node_modules"]
}

这样一个最简单的内部库即创建完成。

2. 定义导出文件路径

需要在 package.json 中明确申明内部库文件的导出路径,这样使用内部库的应用才能找到对应文件。如定义 exports 这个字段,所有文件都从 ./src/index.ts 引入。

json 复制代码
{
  "name": "math-helpers",
  "exports": {
    ".": "./src/index.ts"
  },
  "dependencies": {
    "typescript": "latest"
  }
}

3. 应用中使用内部库

首先在 apps/web/package.json 中加入依赖,这里由于是项目内文件,直接填 * 即可

json 复制代码
{
  "dependencies": {
    "math-helpers": "*"
  }
}

注意 pnpmnpm, yarn 不同包管理工具对于依赖引用的区别

json 复制代码
{
  "dependencies": {
    "math-helpers": "workspace:*"
  }
}

使用的时候,直接从包名引入,就和我们常见的 npm 包一样使用。

typescript 复制代码
import { add } from "math-helpers";
 
add(1, 2);

4. 配置打包工具识别和打包引用的内部库

如果我们直接启动应用,一般会遇到报错,比如 Next.js 的报错:

bash 复制代码
../../packages/math-helpers/src/index.ts
Module parse failed: Unexpected token (1:21)
You may need an appropriate loader to handle this file type,
currently no loaders are configured to process this file.
See https://webpack.js.org/concepts#loaders
> export const add = (a: number, b: number) => {
|   return a + b;
| };

我们需要配置打包工具,让他能正确识别和打包引用的内部库,不同的打包工具配置不一样,如 Next.js 中的 transpilePackages 配置

javascript 复制代码
/** @type {import('next').NextConfig} */
const nextConfig = {
  transpilePackages: ['math-helpers'],
};
 
module.exports = nextConfig;

Umi 项目中如果开启了 mfsu,建议将内部库排除,避免内部库代码更新由于 mfsu 缓存 而未生效。

javascript 复制代码
{
  mfsu: {
    strategy: 'normal',
    exclude: ['math-helpers'],
  },
}

Vite 项目模式是会打包依赖的,所以不需要额外配置。

实践中遇到的问题

Umi 框架下暂时不支持解析只定义了 exports 导出的依赖。

解决方法:main 字段中定义导出文件路径,如:

json 复制代码
{
  "name": "math-helpers",
  "main": "./src/index.ts",
  "exports": {
    ".": "./src/index.ts"
  },
}

实际上,依赖的路径解析是一个非常复杂的话题,本文不详细展开。具体可以参考 Node.js 文档

参考链接

相关推荐
brzhang4 小时前
我操,终于有人把 AI 大佬们 PUA 程序员的套路给讲明白了!
前端·后端·架构
止观止4 小时前
React虚拟DOM的进化之路
前端·react.js·前端框架·reactjs·react
goms5 小时前
前端项目集成lint-staged
前端·vue·lint-staged
谢尔登5 小时前
【React Natve】NetworkError 和 TouchableOpacity 组件
前端·react.js·前端框架
Lin Hsüeh-ch'in5 小时前
如何彻底禁用 Chrome 自动更新
前端·chrome
augenstern4167 小时前
HTML面试题
前端·html
张可7 小时前
一个KMP/CMP项目的组织结构和集成方式
android·前端·kotlin
G等你下课7 小时前
React 路由懒加载入门:提升首屏性能的第一步
前端·react.js·前端框架
蓝婷儿8 小时前
每天一个前端小知识 Day 27 - WebGL / WebGPU 数据可视化引擎设计与实践
前端·信息可视化·webgl
然我8 小时前
面试官:如何判断元素是否出现过?我:三种哈希方法任你选
前端·javascript·算法