搭建monorepo项目

Aug 31 · 3min

开发环境搭建

  • 初始化项目

    pnpm init
  • 根目录新增.npmrc文件

    内容如下

    shamefully-hoist=true

    shamefully-hoist=true不扁平化node_modules目录,方便依赖引入(使用pnpm安装的话,会默认扁平化安装目录)

  • 根目录新增pnpm-workspace.yaml文件

    内容如下

    # 包管理在packages目录下
    packages:
      - 'packages/*'

    表示包所在的目录

    image-20250830230627486

  • 配置ts和打包

    pnpm install typescript esbuild minist -D -w

    -D 开发依赖

    -w 安装在根目录的package.json中,所有的包都可以共享此依赖

  • 初始化tsconfig.json配置

    npx tsc --init

    配置内容如下

    {
      "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
        "baseUrl": ".", // 基础路径
        "paths": {
          "@vue/*": ["packages/*/src"] // 路径别名 支持在ts文件中通过@vue/reactivity引入reactivity包
        }
      }
    }
  • 配置打包脚本

    1.配置package.json文件

    package文件字段解读:地址

    image-20250830231615852

private:true发布npm包的时候,不发布此包

type:module使用esm模块规范 (支持import xxx from xxx)

dev配置打包脚本。参数说明:reactivity包名, -f esm 打包格式

{
  "name": "monorepo-vue",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "node scripts/dev.js reactivity -f esm"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "packageManager": "pnpm@10.15.0",
  "dependencies": {
    "vue": "^3.5.20"
  },
  "devDependencies": {
    "esbuild": "^0.25.9",
    "minimist": "^1.2.8",
    "typescript": "^5.9.2"
  }
}

2.配置dev.js脚本

新增scripts文件夹,在文件夹新增dev.js文件

内容如下

// 打包的脚本
// node scripts/dev.js reactivity -f esm
// reactivity 包名
// -f esm 打包的格式

// 使用esm模块规范  
// package.json =>  "type": "module",
import minimist from "minimist"
import { fileURLToPath } from 'url'
import { dirname, resolve } from 'path'
import { createRequire } from 'module'
// process.argv.slice(2) => 获取命令行参数  => ['reactivity', '-f', 'esm']
const args = minimist(process.argv.slice(2))
const target = args._[0] || 'reactivity' // 包名
const format = args.f || 'esm' // 打包的格式
// import.meta.url 获取当前文件的url
// fileURLToPath 将url转换为路径
// __filename 当前文件的路径
const __filename = fileURLToPath(import.meta.url)
// __dirname 当前文件的目录
const __dirname = dirname(__filename)
// createRequire 创建一个require函数
const require = createRequire(import.meta.url)
// 入口文件
const entry = resolve(__dirname, `../packages/${target}/src/index.ts`)
  • 配置packages

    其中文件名为包名,每个包都有自己的package.json文件,入口文件index.ts

image-20250830233059958

注意:package.json的文件名命名为@xxx1/xxx2,其中xxx1代表项目名称,xxx2代表包名称。例如@vue/reactivity

为了使导入包的支持这种格式,tsconfig.json新增配置:

{
	// ...其他配置项
    "baseUrl": ".", // 基础路径
    "paths": {
      "@vue/*": ["packages/*/src"] // 路径别名 支持在ts文件中通过@vue/reactivity引入reactivity包
    }
  }
}

现在可以在某个包导入packages里的其他包

image-20250830233631927

如果要在某个里包安装packages里的其他包,使用命令行

pnpm install @vue/shared --workspace --filter @vue/reactivity

image-20250830233937701

image-20250830234138741

pnpm-workspace地址

reactivitynode_moudles里新增了安装的包

  • 配置esbuild

    更新dev.js文件

    // 打包的脚本
    // node scripts/dev.js reactivity -f esm
    // reactivity 包名
    // -f esm打包的格式
    
    // 使用esm模块规范  
    // package.json =>  "type": "module",
    import minimist from "minimist"
    import { fileURLToPath } from 'url'
    import { dirname, resolve } from 'path'
    import { createRequire } from 'module'
    import esbuild from 'esbuild'
    // process.argv.slice(2) => 获取命令行参数  => ['reactivity', '-f', 'esm']
    const args = minimist(process.argv.slice(2))
    const target = args._[0] || 'reactivity' // 包名
    const format = args.f || 'esm' // 打包的格式
    // import.meta.url 获取当前文件的url
    // fileURLToPath 将url转换为路径
    // __filename 当前文件的路径
    const __filename = fileURLToPath(import.meta.url)
    // __dirname 当前文件的目录
    const __dirname = dirname(__filename)
    // createRequire 创建一个require函数
    const require = createRequire(import.meta.url)
    // 入口文件
    const entry = resolve(__dirname, `../packages/${target}/src/index.ts`)
    const pkg = require(resolve(__dirname, `../packages/${target}/package.json`))
    // esbuild打包
    esbuild.build({
      // 入口文件
      entryPoints: [entry],
      // 输出文件
      outfile: resolve(__dirname, `../packages/${target}/dist/${target}.js`),
      // 依赖的包会打包到一起
      bundle: true,
      // 给浏览器使用
      platform: 'browser',
      // 生成sourcemap 可以调试
      sourcemap: true,
      // 打包的格式
      format, // esm cjs iife(立即执行函数)
      // 全局变量
      globalName: pkg.buildOptions?.name
    }).then(() => {
      console.log('打包成功');
    }).catch(() => {
      console.log('打包失败');
    })
    

    运行打包命令:

    pnpm dev

dist为输出目录,里面有输出文件,如果此包依赖packages里其他的包,会同时打包进来

image-20250831000227399

image-20250831000356223

CC BY-NC-SA 4.0 2024-PRESENT © hujiacheng