使用 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 文档

参考链接

相关推荐
华玥作者2 小时前
[特殊字符] VitePress 对接 Algolia AI 问答(DocSearch + AI Search)完整实战(下)
前端·人工智能·ai
Mr Xu_2 小时前
告别冗长 switch-case:Vue 项目中基于映射表的优雅路由数据匹配方案
前端·javascript·vue.js
前端摸鱼匠2 小时前
Vue 3 的toRefs保持响应性:讲解toRefs在解构响应式对象时的作用
前端·javascript·vue.js·前端框架·ecmascript
lang201509282 小时前
JSR-340 :高性能Web开发新标准
java·前端·servlet
好家伙VCC3 小时前
### WebRTC技术:实时通信的革新与实现####webRTC(Web Real-TimeComm
java·前端·python·webrtc
未来之窗软件服务4 小时前
未来之窗昭和仙君(六十五)Vue与跨地区多部门开发—东方仙盟练气
前端·javascript·vue.js·仙盟创梦ide·东方仙盟·昭和仙君
嘿起屁儿整4 小时前
面试点(网络层面)
前端·网络
VT.馒头4 小时前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript
phltxy5 小时前
Vue 核心特性实战指南:指令、样式绑定、计算属性与侦听器
前端·javascript·vue.js
Byron07076 小时前
Vue 中使用 Tiptap 富文本编辑器的完整指南
前端·javascript·vue.js