搭建 monorepo
实现对 es6 语法、ts、项目特定的编译, 尽可能还原 vue3 对多个模块的构建方式.
yarn 安装项目依赖
vue3 采用 menorepo 的方式,目前只有 yarn 支持
yarn init -y
yarn add typescript rollup rollup-plugin-typescript2 @rollup/plugin-node-resolve @rollup/plugin-json execa -D
# execa: 同时打包多个项目,是 node 的子进程
# p.s. —ignore-workspace-root-check 给根模块安装而不是子模块
| 依赖 | |
|---|---|
| typescript | 支持typescript |
| rollup | 打包工具 |
| rollup-plugin-typescript2 | rollup 和 ts的 桥梁 |
| @rollup/plugin-node-resolve | 解析node第三方模块 |
| @rollup/plugin-json | 支持引入json |
| execa | 开启子进程方便执行命令 |
目录结构
├── package.json # 配置运行命令
├── packages # n个repo
│ ├── reactivity # 响应式系统
│ │ ├── dist
│ │ ├── package.json
│ │ └── src
│ ├── runtime-core # 与平台无关的运行时核心
│ │ └── dist
│ ├── runtime-dom
│ │ └── dist
│ └── shared
│ ├── dist
│ ├── package.json
│ └── src
├── rollup.config.js # rollup配置文件
├── scripts # 打包命令
│ ├── build.js
│ └── dev.js
└── tsconfig.json
根package.json
作用: 维护
packages/*目录下的多个包
private:true:表示是个私有包- 指定工作空间:
workspace:["packages/*"],所有包都管理到 packages 目录下 - 执行
yarn install时,会将packages/*生成软链到 node_modules 下,这样就可以在当前任意模块引入其他模块,即多个包可以相互引用 - 一个包可以依赖另一个包:
yarn workspace @vue/reactivity add @vue/shared@1.0.0
{
+ "private": true,
+ "workspaces": [
+ "packages/*"
+ ],
"name": "crx-vue3-core-code",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"execa": "^5.1.1",
"rollup": "^2.52.3",
"rollup-plugin-typescript2": "^0.30.0",
"typescript": "^4.3.4"
}
}
子package.json
每个子模块需要遵循一定机制:
packages/reactivity/package.json
"name": "@vue/reactivity",
"main": "index.js", // node commonjs
"module": "dist/reactivity.esm-bundle.js",// import '@vue/reactivity' --> 找dist/reactivity.esm-bundle.js文件
"buildOptions":{
"name":"VueReactivity",// 给全局的包起名 <script src=".../dist/reactivity.esm-bundle.js"></script>引入后可以使用VueReactivity变量
// 配置当前模块支持 node、es6、全局模块
"formats":[
"cjs",
"esm-bundler",
"global"
]
}
packages/reactivity/package.json
{
"name": "@vue/shared",
"version": "1.0.0",
"description": "",
"main": "index.js",
"module": "dist/shared.esm-bundle.js",
"buildOptions":{
"name":"VueShared",
"formats":[
"cjs",
"esm-bundler"
]
},
"scripts": {},
"keywords": [],
"author": "",
"license": "ISC"
}
编译脚本、rollup、ts配置
package.json
"scripts": {
"dev": "node scripts/dev.js",
"build": "node scripts/build.js"
}
dev.js只针对具体的某个包打包 作用: 给 rollup 传入 target 环境变量,生成 rollup 配置,可以根据后面的参数 打包的类型 做一些忽略 忽略一些文件build.js打包package目录下的所有包
scripts/build.js
const fs = require("fs");
const execa = require("execa"); // 开启子进程打包,最终还是使用rollup来进行打包
// 过滤packages目录下的文件,保留文件夹
const targets = fs.readdirSync("packages").filter((f) => {
if (!fs.statSync(`packages/${f}`).isDirectory()) {
return false;
}
return true;
});
// 对目标依次、并行打包
async function build(target) {
// rollup -c --environment TARGET:shated
await execa("rollup", ["-c", "--environment", `TARGET:${target}`], {
stdio: "inherit",
}); // 当子进程打包的信息共享给父进程
}
function runParallel(targets, iteratorFn) {
const res = [];
for (const item of targets) {
const p = iteratorFn(item);
res.push(p);
}
return Promise.all(res);
}
runParallel(targets, build);
scripts/dev.js
const execa = require("execa");
const target = "reactivity";
build(target);
async function build(target) {
await execa("rollup", ["-cw", "--environment", `TARGET:${target}`], {
stdio: "inherit",
});
}
rollup.config.js
import path from "path";
import json from "@rollup/plugin-json";
import resolvePlugin from "@rollup/plugin-node-resolve";
import ts from "rollup-plugin-typescript2";
// 1.根据环境变量中的target属性 获取对应模块中的 pakcage.json
const packagesDir = path.resolve(__dirname, "packages");
const packageDir = path.resolve(packagesDir, process.env.TARGET); // packageDir 找到要打包的某个包/打包的基准目录
const resolve = (p) => path.resolve(packageDir, p);
const pkg = require(resolve("package.json"));
const name = path.basename(packageDir); // 取文件名 或者process.env.TARGET
// 对打包类型的映射表,根据提供的formats格式化需要打包的内容
const outputConfig = {
// 自定义的
"esm-bundler": {
file: resolve(`dist/${name}.esm-bundler.js`),
format: "es",
},
cjs: {
file: resolve(`dist/${name}.cjs.js`),
format: "cjs",
},
global: {
file: resolve(`dist/${name}.global.js`),
format: "iife", // 立即执行函数
},
};
const options = pkg.buildOptions;
// 生成rollup配置
function createConfig(format, output) {
output.name = options.name;
output.sourcemap = true; // 生成sourcemap
return {
input: resolve(`src/index.ts`),
output,
plugins: [
json(),
ts({
// ts 插件
tsconfig: path.resolve(__dirname, "tsconfig.json"),
}),
resolvePlugin(), // 解析第三方模块插件
],
};
}
// rollup 最终需要到出配置
export default options.formats.map((format) =>
createConfig(format, outputConfig[format])
);
tsconfig.json
{
"compilerOptions": {
"target": "ESNEXT",
"module": "ESNEXT",
"strict": false,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"@vue/*": ["packages/*/src"]
}
}
}