1、简介
monorepo主要是将多个项目的代码放到一个文件夹中,便于同时管理、开发。一个monorepo仓库可以包含多个项目,也可以包含多个项目的不同模块,同一个项目拆分为不同模块不仅可以方便团队进行管理,也方便不同团队、不同项目之间多次复用,在多个系统中实现样式及标准的统一。目前来说比较流行。
2、搭建monorepo
首先,要有一个包管理工具,可以随意选择npm、pnpm、yarn、lerna、nx、rush等。注意使用npm安装包时切换镜像源,否则会很慢。但这些都无所谓。
其次,新建一个文件夹并进行初始化,初始化的目的是在当前文件夹下添加package.json,在根目录下规定当前的环境名称(workspace),方式有两种,一种是新建pnpm-workspace.yaml,一种是直接在package.json中配置workspace字段
// pnpm-workspace.yaml内容 https://juejin.cn/post/7145010706063523854
packages:
- "packages/*"
// package.json配置 https://juejin.cn/post/7081440800143310884
{
"name": "yarn-test",
"version": "1.0.0",
"private": true,
"workspaces": [
"packages/*"
],
}
再次、在在当前文件夹中创建子项目,最后形成形成如下方所示的目录结构
my-monorepo/
├── packages/
│ ├── blog/
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── BlogPost.tsx
│ │ │ │ └── ...
│ │ │ ├── pages/
│ │ │ │ ├── Home.tsx
│ │ │ │ └── ...
│ │ │ └── ...
│ │ ├── public/
│ │ │ ├── index.html
│ │ │ └── ...
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── components/
│ │ ├── src/
│ │ │ ├── Button.tsx
│ │ │ └── ...
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── ...
│ └── ...
├── package.json
└── tsconfig.json
最后,可以在根目录下添加指定的脚本,用于统一管控所有项目的运行、打包、测试。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev:blog": "pnpm -F @sun-world/blog dev",
"dev:icons": "pnpm -F @sun-world/icons dev",
"build:blog": "pnpm -F @sun-world/blog build",
"build:icons": "pnpm -F @sun-world/icons build",
"build:all": "pnpm -r exec pnpm run build:prod",
"build:all:staging": "pnpm -r exec pnpm run build:staging"
},
具体过程可以参考:掘金文章
3、注意点
- 根目录下package.json已有的依赖可以不用在子项目中重复添加,同理,多个项目共同的依赖也可以提升到根目录的依赖中。命令为:
pnpm install lib_name -W
或者pnpm install lib_name -Dw
,pnpm相关命令不再描述 - 如果项目中存在相互引用,则需要注意打包时的配置,涉及到的文件有package.json、vite.config.ts等。
// icon包打包配置
// package.json, 注意main、module、types
{
"name": "@sun-world/icons",
"private": true,
"version": "0.0.1",
"type": "module",
"main": "./dist/icons.es.js",
"module": "./dist/icons.es.js",
"types": "./dist/index.d.ts",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
}
}
// vite.config.ts配置
// ! build配置比较关键
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import typescript from "@rollup/plugin-typescript";
import path from "path";
const resolvePath = (str: string) => path.resolve(__dirname, str);
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
build: {
lib: {
entry: "src/index.ts",
name: "icons",
fileName: (format) => `icons.${format}.js`,
},
rollupOptions: {
external: ["react", "react-dom"],
output: {
globals: {
react: "React",
"react-dom": "ReactDOM",
},
},
plugins: [
typescript({
target: "esnext",
rootDir: resolvePath("src"),
declaration: true,
declarationDir: resolvePath("dist"),
exclude: resolvePath("node_modules/**"),
allowSyntheticDefaultImports: true,
}),
],
},
},
});
// 入口文件src/index.ts
export * from "./icons";
// blog子项目引用,需要构建icons子项目才能引用
"dependencies": {
"@sun-world/build": "workspace:*",
"@sun-world/icons": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}