monorepo 搭建vue3开发环境

327 阅读3分钟

禁止转载,侵权必究!

Vue3 环境搭建

Vue3 中使用 pnpm workspace 来实现 monorepo (pnpm 是快速、节省磁盘空间的包管理器。主要采用符号链接的方式管理模块) Monorepo 是管理项目代码的一个方式,指在一个项目仓库(repo)中管理多个模块/包(package)。 Vue3源码采用 monorepo 方式进行管理,将模块拆分到package目录中。

  • 一个仓库可维护多个模块,不用到处找仓库
  • 方便版本管理和依赖管理,模块之间的引用,调用都非常方便

1. 全局安装 pnpm

npm install pnpm -g # 全局安装pnpm
pnpm init # 初始化配置文件

2.创建.npmrc 文件

shamefully-hoist = true

尝试安装 vue3, pnpm install vue@next 此时默认情况下 vue3 中依赖的模块不会被提升到 node_modules 下。 添加羞耻的提升可以将 Vue3,所依赖的模块提升到 node_modules 中

3.配置 workspace

新建 pnpm-workspace.yaml

packages:
  - "packages/*"

将 packages 下所有的目录都作为包进行管理。

4.安装依赖

Vue3 开发环境采用 esbuild。打包 Vue3 项目采用 rollup 进行打包代码。 typescript:在项目中支持 Typescript; esbuild(0.15.12): 构建工具,默认支持 TS; minimist: 命令行参数解析;

pnpm install typescript minimist esbuild -D -w  # w 安装在根目录

5.初始化 TS

pnpm tsc --init

tsconfig.json

{
  "compilerOptions": {
    "outDir": "dist", // 输出的目录
    "sourceMap": true, // 采用sourcemap
    "target": "es2016", // 目标语法
    "module": "esnext", // 模块格式
    "moduleResolution": "node", // 模块解析方式
    "strict": false, // 严格模式
    "resolveJsonModule": true, // 解析json模块
    "esModuleInterop": true, // 允许通过es6语法引入commonjs模块
    "jsx": "preserve", // jsx 不转义
    "lib": ["esnext", "dom"] // 支持的类库 esnext及dom
  }
}

6.创建模块

我们现在 packages 目录下新建两个 package

  • reactivity 响应式模块
  • shared 共享模块

所有包的入口均为 src/index.ts 这样可以实现统一打包,并在包信息中增添打包格式 formats 属性,用于最终打包后的格式。

  • reactivity/package.json
{
  "name": "@vue/reactivity",
  "version": "1.0.0",
  "module": "dist/reactivity.esm-bundler.js",
  "unpkg": "dist/reactivity.global.js",
  "buildOptions": {
    "name": "VueReactivity",
    "formats": ["esm-browser", "esm-bundler", "cjs", "global"]
  }
}
// module:构建工具里用的
// unpkg: 浏览器脚本引用
  • shared/package.json
{
  "name": "@vue/shared",
  "version": "1.0.0",
  "main": "index.js",
  "module": "dist/shared.esm-bundler.js",
  "buildOptions": {
    "formats": ["esm-bundler", "cjs"]
  }
}

formats 为自定义的打包格式

  • global 立即执行函数的格式,会暴露全局对象
  • esm-browser 在浏览器中使用的格式,内联所有的依赖项。
  • esm-bundler 在构建工具中使用的格式,不提供.prod 格式,在构建应用程序时会被构建工具一起进行打包压缩。
  • cjs 在 node 中使用的格式,服务端渲染。
# 在reactivity包中安装 shared模块
pnpm install @vue/shared@workspace --filter @vue/reactivity

配置 ts 引用关系

"baseUrl": ".",
"paths": {
    "@vue/*": ["packages/*/src"]
}

7.开发环境搭建

创建开发时执行脚本, 参数为要打包的模块 解析用户参数 package.json

"scripts": {
    "dev": "node scripts/dev.js reactivity -f esm"
}

scripts/dev.js

const path = require("path");
const { build } = require("esbuild");

const args = require("minimist")(process.argv.slice(2)); // {_: ['reactivity], f: 'esm} minimist: 解析执行命令时的参数

const target = args._[0] || "reactivity"; // 打包的模块
const format = args.f || "global"; // 打包格式

const pkg = require(path.resolve(
  __dirname,
  `../packages/${target}/package.json`
)); // 打包的package.json

const outputFormat = format.startsWith("global")
  ? "iife"
  : format === "cjs"
  ? "cjs"
  : "esm"; // 输出的文件格式

// reactivity.global.js
// reactivity.esm.js
// reactivity.cjs.js
const outfile = path.resolve(
  __dirname,
  `../packages/${target}/dist/${target}.${format}.js`
); // 输出的文件

build({
  entryPoints: [path.resolve(__dirname, `../packages/${target}/src/index.ts`)], // 入口文件
  outfile,
  bundle: true,
  sourcemap: true,
  format: outputFormat,
  globalName: pkg.buildOptions?.name,
  platform: format === "cjs" ? "node" : "browser",
  watch: {
    // 监控文件变化
    onRebuild(error) {
      if (!error) console.log(`rebuilt~~~~`);
    },
  },
}).then(() => {
  console.log("watching~~~");
});