Monorepo 是什么?
Monorepo
是管理项目代码的一种方式,指在一个仓库中管理多个模块/包
优点:
- 一个仓库可以同时维护多个模块,不用拆分多个仓库
- 方便版本管理和依赖管理,模块之间互相引用,调用方便
缺点:
- 因为多个模块/包共存,仓库体积会比较大
仿照Vue3的代码结构,手写一套
搭建项目结构
- 创建项目目录
mkdir vue3-monorepo && cd vue3-monorepo
yarn init -y
mkdir packages && cd packages
mkdir reactivity
mkdir shared
- 修改
package.json
{
"name": "vue3-monorepo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
+ "private": true,
+ "workspaces":[
+ "packages/*"
+ ]
}
- init reactivity shared
cd reactivity && yarn init -y
cd shared && yarn init -y
- 创建
reactivity
shared
的目录
vue3-monorepo
|---package.json
---packages
---reactivity
| | package.json
| |
+ | ---src
+ | index.ts
---shared
| package.json
+ ---src
+ index.ts
- 给
reactivity
shared
的入口文件index.ts
增加点代码
// reactivity/src/index.ts
+ const Reactivity = {}
+ export { Reactivity }
// shared/src/index.ts
+ const Shared = {}
+ export { Shared }
搭建构建环境
- 安装依赖
包名 | 描述 |
---|---|
typescript | 支持ts |
rollup | 打包工具 |
rollup-plugin-typescript2 | rollup和ts的桥梁 |
@rollup/plugin-node-resolve | 解析node的第三方模块 |
@rollup/plugin-json | 支持引入json文件 |
execa | 开启子进程 |
# --ignore-workspace-root-check or -W 允许在根目录下安装依赖
yarn add typescript rollup rollup-plugin-typescript2 @rollup/plugin-node-resolve @rollup/plugin-json execa --ignore-workspace-root-check
- 初始化
typescript
配置
# 在根目录(vue3-monorepo)下执行
# 前面已经安装过 typescript,所以在项目的 node_modules/.bin 目录下会有 tsc,此时就是调用这个命令生成 tsconfig.json 文件
npx tsc --init
vue3-monorepo
|---node_modules
|---packages
|---package.json
+ ---tsconfig.json
// tsconfig.json
{
"target": "ESNext",
"module": "ESNext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@vue/*":["packages/*/src"]
}
}
- 修改
reactivity
shared
目录下的package.json
配置模块/包名以及打包选项
# reactivity/package.json
{
+ "name": "@vue/reactivity",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
+ "module": "dist/reactivity.esm-bundler.js", // ESModule的入口
+ "buildOptions": { // 自定义字段,用来构建配置
+ "name": "VueReactivity",
+ "formats": [
+ "esm-bundler",
+ "cjs",
+ "global"
+ ]
+ }
}
# shared/package.json
{
+ "name": "@vue/shared",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
+ "module": "dist/reactivity.esm-bundler.js", // ESModule的入口
+ "buildOptions": { // 自定义字段,用来构建配置
+ "name": "VueShared",
+ "formats": [
+ "esm-bundler",
+ "cjs",
+ ]
+ }
}
修改完成后,在根目录执行 yarn install
,然后查看 node_modules
下会有一个 @vue
目录,下面有 reactivity
shared
两个软链,如果没有,刷新文件目录
- 增加命令
# vue3-monorepo/package.json
{
"name": "vue3-monorepo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"workspaces": [
"packages/*"
],
+ "scripts": {
+ "dev": "node scripts/dev.js",
+ "build": "node scripts/build.js"
+ },
"dependencies": {}
}
- 创建脚本文件
// vue3-monorepo/scripts/build.js
// 把packages 目录下的所有包进行打包
const fs = require('fs') // node 文件模块
const execa = require('execa') // 开启子线程
// 读取 packages 目录,并排除不是目录的文件
const targets = fs.readdirSync('packages').filter(f => fs.statSync(`packages/${f}`).isDirectory())
async function build(targets) {
await execa('rollup', ['-c', '--environment', `TARGET:${targets}`], {
stdio: 'inherit'
})
}
// 遍历packages下的目录,并执行build
function runParallel(targets, iteratorFn) {
const res = []
for (const iterator of targets) {
const p = iteratorFn(iterator)
res.push(p)
}
return Promise.all(res)
}
runParallel(targets, build)
// vue3-monorepo/scripts/dev.js
// 只针对具体的某个包
const fs = require('fs')
const execa = require('execa')
const target = 'reactivity' // 自定义要开发的包名
async function build(target) {
await execa('rollup', ['-cw', '--environment', `TARGET:${target}`], {
stdio: 'inherit'
})
}
build(target)
- 创建
rollup
配置文件
// vue3-monorepo/rollup.config.js
import path from 'path'
import json from '@rollup/plugin-json'
import ts from 'rollup-plugin-typescript2'
import resolvePlugin from '@rollup/plugin-node-resolve'
const packageName = process.env.TARGET // 脚本文件中build函数传递过来的自定义环境变量
const packagesPath = path.resolve(__dirname, 'packages') // packages path
const packageDirPath = path.resolve(packagesPath, packageName) // packages 每个包的 path
const resolve = p => path.resolve(packageDirPath, p)
const packageJSON = require(resolve('package.json'))
const outputConfig = {
'esm-bundler': {
file: resolve(`dist/${packageName}.esm-bundler.js`),
format: 'es'
},
cjs: {
file: resolve(`dist/${packageName}.cjs.js`),
format: 'cjs'
},
global: {
file: resolve(`dist/${packageName}.global.js`),
format: 'iife'
}
}
const buildOptions = packageJSON.buildOptions
function createConfig(format, output) {
output.name = buildOptions.name
output.sourcemap = buildOptions.sourcemap
return {
input: resolve(`src/index.ts`),
output,
plugins: [
json(),
ts({
tsconfig: path.resolve(__dirname, 'tsconfig.json')
}),
resolvePlugin()
]
}
}
export default buildOptions.formats.map(format => createConfig(format, outputConfig[format]))
- 执行打包构建
yarn run build # 同时构建packages下所有模块
yarn run dev # 单独构建指定的模块