Lerna+monorepo整合Vue3组件库实践

UI组件库重构实践——整合Lerna与pnpm

背景与目标

最近对原有的UI组件库进行了重构,以解决之前一次性打包所有组件导致无法单独引入的问题。通过引入Lerna管理和pnpm作为包管理工具,使得每个组件能够独立打包、独立引入,同时也保留了整体引入所有组件的功能。

使用工具与技术栈

  • Lerna V8
  • pnpm
  • Rollup

初始化Lerna与pnpm集成

  1. 执行 lerna init 生成 lerna.json 配置文件,并对其做了如下调整:

    {
      "packages": ["packages/*"],
      "npmClient": "pnpm"
    }
    
    
  2. 创建并配置 pnpm-workspace.yaml 文件以指定包范围:

    packages:
      - "packages/*"
    
  3. 在每个子包(例如Form、Table等组件)目录下创建各自的 package.json 文件,并设置打包命令,使其指向Rollup配置文件:

    "scripts": {
      "build": "rollup --config ../../scripts/rollup.config.prod.js --environment PROJECT_NAME:autoComplete,MODE:development"
    }
    

    这样便能在各子包目录下分别产出 dist 包,便于单独引入每个组件。

组件聚合与整体引入

为了同时支持一次性引入所有组件的功能,编写了一个名为 build.cjs 的脚本,用于将所有子包编译后生成的 dist 目录下的文件复制到 lib 目录,并创建一个聚合所有组件的 index.js 文件。

以下是 build.cjs 脚本内容:

/**
 * @Author: yuxuan-ctrl
 * @Date: 2024-03-12 16:46:09
 * @LastEditors: yuxuan-ctrl
 * @LastEditTime: 2024-03-13 17:21:44
 * @FilePath: \scripts\build.cjs
 * @Description: 聚合子包dist文件到lib目录
 */

const fs = require('fs-extra');
const path = require('path');

const targetDir = path.resolve(__dirname, '../lib'); // 目标目录
const packagesDir = path.resolve(__dirname, '../packages'); // 子包目录
const srcDir = path.resolve(__dirname, '../src'); // 源码目录(这里假设src也需要同步到lib)

// 遍历子包并将dist目录下的文件复制至lib对应目录
fs.readdirSync(packagesDir).forEach(packageName => {
  const packageDir = path.join(packagesDir, packageName, 'dist');
  const packageJsonFile = path.join(packagesDir, packageName, 'package.json');

  if (fs.existsSync(packageDir)) {
    fs.readdirSync(packageDir).forEach(file => {
      const sourceFile = path.join(packageDir, file);
      const targetFile = path.join(targetDir, packageName, file);

      // 若目标路径的上级目录不存在,则递归创建
      if (!fs.existsSync(path.dirname(targetFile))) {
        fs.mkdirSync(path.dirname(targetFile), { recursive: true });
      }

      // 复制文件
      fs.copyFileSync(sourceFile, targetFile);
    });
  }
});

// 同时将src目录下的源码结构完整复制到lib目录
fs.copy(srcDir, targetDir, (err) => {
  if (err) {
    console.error('An error occurred while copying:', err);
  } else {
    console.log('Successfully copied src directory to lib directory!');
  }
});

通过运行该脚本,所有组件被打包后的产物会被复制到 lib 目录,并且保持原包结构。接着,在 lib 目录下创建一个 index.js 文件,用于一次性导入并注册所有组件。这样既实现了单个组件的精细管理,也兼顾了全量引入的需求,提高了组件库的灵活性和可维护性。

Lerna V8踩坑

自Lerna v7.0.0开始,Lerna默认移除了lerna bootstraplerna add以及lerna link命令。这一改动促使用户采用更加现代化的方式管理项目依赖关系,即利用包管理器自带的工作空间功能来替代Lerna的这部分职责。

核心理念转变在于认识到Lerna不再负责仓库中的依赖安装和链接工作,而是由对应的包管理器更胜任此任务。为此,您应当利用包管理器提供的工作空间特性:

启用工作空间后,包管理器会在执行install命令时自动完成之前lerna bootstraplerna link所实现的相同链接操作,无需额外执行其他命令(前提是已按照上述包管理器文档正确配置了工作空间)。

同样地,替换lerna add命令也很直接。添加或移除依赖本来就是包管理器的基本功能,在启用工作空间后,您可以直接运行适当的安装命令来向特定包或工作空间添加依赖项,系统会自动处理相关的本地链接。

具体来说,当你迁移至Lerna v7及以上版本时,以下是比较明确的变化及前后使用方式对比:

之前(Lerna v6及更低版本)

  • 安装并链接所有包依赖:lerna bootstrap
  • 添加依赖到某个包:lerna add <dependency> --scope=<package-name>
  • 创建本地链接:lerna link

现在(Lerna v7及以上版本配合包管理器工作空间)

  • 安装并链接所有包依赖:仅需运行pnpm install(若使用pnpm)、npm install(若使用npm带有workspaces配置)或yarn install(若使用yarn工作空间)
  • 添加依赖到某个包:直接在相应的包目录下运行如pnpm add <dependency>npm install <dependency>(npm会依据workspace配置自动定位到正确的包)或yarn add <dependency>,包管理器会自动将其链接到适当的位置。

通过这样的调整,项目构建流程更加简洁,依赖管理更为透明,且充分利用了现代包管理器的功能。

如何执行Link操作呢?

以Pnpm举例:

pnpm link


pnpm link --global
pnpm link --global

Links package from <dir> folder to node_modules of package from where you’re executing this command or specified via --dir option.

例如,如果你在 ~/projects/foo 里面,执行 pnpm link --dir ../bar,那么 foo 就会链接到 bar/node_modules/foo

英:For example, if you are inside ~/projects/foo and you execute pnpm link --dir ../bar, then foo will be linked to bar/node_modules/foo.

最后打包目录结构如下:

在这里插入图片描述

  • 8
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值