平时开发项目时,我们会积累和沉淀许多通用的组件、方法等,它们通常适用于多个项目。但由于不同的项目具有不同的代码库,这些通用组件或方法不能简单地复制粘贴,这也不利于统一维护和更新。如果我们要在一个代码库中维护一套通用的组件、方法、模板等可能服务于其他项目的 package,这些 package 可以独立发布且互不干扰,我们应该如何操作呢?最初,我并不知道该如何进行调研,于是翻看了一些优秀库(如 vant、element-plus)的底层架构,大致了解了一些情况。结合自己的理解和认知,我使用 pnpm + vite 搭建了一个可供参考使用的 monorepo 多包管理框架。为何选择 pnpm:pnpm 相较于 npm、yarn 可以有效节省磁盘空间并提升安装速度。pnpm 内置了对单个代码仓库包含多个软件包的支持,是 monorepo 架构模式的不二之选。原理概括:pnpm 将所有的依赖包的层次提升到同一层次并安装存放在一个统一的地方(.pnpm),创建非扁平的 node_modules 目录,包之间的依赖关系可以很清楚地看到,一个包的依赖包以符号链接的形式存在,符号链接会指向当前包的原始位置,即所在的 .pnpm 的顶层位置。为何选择 vite:vite 随着 vue 3.x 而来,极大改善了开发体验,冷启动让人惊艳,再也不用担心项目越来越大启动速度越来越慢了。平时开发项目基本是 vue 3.x + vite + typescript 的组合,vite 有一个库模式的构建方式,这意味着我们在开发项目和通用包时,都可以使用 vite,而无需引入第三方构建工具,使用 vite 无疑具有天然优势。这里存在一个问题,现在普遍使用 typescript 来开发项目,开发的通用库在发布构建时也需要生成相应的 .d.ts 类型声明文件,vite 的库模式构建是不会自动生成 .d.ts 文件的,为了能自动生成类型声明文件我需要用到 tsc 和 vue-tsc,这里先简单提下,后面会用到。基本骨架:包的开发阶段可能需要在一个包中引用另一个包以方便调试,单包开发的话可能需要用 npm link 辅助调试,比较麻烦,多包模式下可以简单地使用 workspace。组件库:1. 我们需要在项目的根目录下执行 scripts 命令,以执行对应包中的 scripts 命令,需要用到 pnpm --dir 定位到需要执行命令的目录。注意:为了更加细粒度地控制构建的过程,这里封装了构建的脚手架命令 vswift-cli build,简单直接点的话可以直接用 "build": "rm -r ./dist && vite build && vue-tsc"。2. 组件库中有独立的 package.json、tsconfig.json、vite.config.ts 配置,package.json 中有几个关键配置:files、main、module、types、exports、publishConfig。files - 发布的包所包含的内容,默认会带上 package.jsonmain - require 的入口文件,如 dist/index.umd.cjsmodule - import 的入口文件,如 dist/index.jstypes - 类型声明的入口文件,如 dist/types/index.d.tsexports - 定义导出模块的路径别名publishConfig - 发布配置3. tsconfig.json 主要是配置 typescript 的作用范围 include,以及类型声明文件的编译输出。因为是组件库,所以会有 .vue 文件,vue-tsc 可以生成对应的 .vue.d.ts 类型声明文件。4. vite.config.ts 是库模式 build 的配置,基本配置如下:5. 最终输出原始的文件目录打包输出的文件目录组件库打包可能存在的问题解决方法:引入 vite-plugin-css-injected-by-js 插件解决。以 element-plus 开发场景为例,如果开发的组件库是在 element-plus 组件的基础上开发的,打包后用到的样式会被提取到一个通用的 .css 文件中,这其中包括用到的 element-plus 根(:root)样式变量,这可能会覆盖掉项目中的 :root 样式变量。解决方法:一个不错的解决方法是根据组件库的名称自定义 element-plus 命名空间,如何自定义命名空间,element-plus 官方文档有详细说明,这里不做赘述。这里有一点需要注意下,我们是打包自己的组件库,命名空间的设置不能是在 App.vue 根组件中了,而在你需要打包的组件中。方法库:不同于组件库是以 .vue 文件为主的视觉交互,通用方法中主要是以 .ts 文件为主的通用逻辑方法等,所以它和组件库最大的区别在于生成类型声明文件的工具,组件库是用 vue-tsc,通用方法库则是用 tsc,可参见上面的 vswift-cli build 脚本构建命令,简单直接点的话可以直接用 "build": "rm -r ./dist && vite build && tsc"。脚手架:封装自己的脚手架,有 3 个关键要点:bin 配置、files 配置、cli 顶部声明。脚手架 package 基本结构预览 ------------>这里解释下 "build": "pnpm clean && tsc && scp -r ../vswift-templates/src/templates ./dist" 的含义-----------> “pnpm clean && tsc” 无需多说,“scp -r ../vswift-templates/src/templates ./dist” 是将 vswift-templates/src/templates 中维护的通用模板拷贝到打包输出目录 dist 中,并随着 @vswift/cli 包一起发布,使用脚手架命令快速创建基础页面的模板将从 “dist/templates” 中获取。name - 发布后的包名,全局安装:npm install @vswift/cli -gbin.vswift-cli - 全局安装 @vswift/cli 后,即可在命令行使用 vswift-clifiles - 发布的文件别漏掉 bin.jsbin.js - 顶部需加 #!/usr/bin/env node 声明注意:脚手架不需要 vite.config.ts 配置,只需在 tsconfig.json 中稍加区别修改,执行 tsc 使用。outDir - 编译后的输出目录declaration - 执行 tsc 时输出 .d.ts 声明文件declarationDir - .d.ts 声明文件的输出目录最终输出目录 ----------------->这里推荐几个开发脚手架时常用的 package -------------------->commander - Node.js 命令行插件,可以格式化输出友好的命令符提示nanospinner - Node.js 终端微调器,进度、成功、失败等状态友好展示,也可用 ora 代替consola - 用于 Node.js 和浏览器的优雅控制台记录器chalk - 终端样式粉笔,着色高亮vswift-cli build 构建命令 ------------------>vswift-cli create 根据模板创建基础页面命令 --------------------->vswift-cli dev 启动组件库预览调试命令 -------------------->cli 命令行逻辑 --------------->自动生成的 vswift-cli 命令行 help ----------------------->发布包:每个 package 的 package.json 中都有 release 命令,用于发布当前 package,package 会在发布前先 build 操作生成 dist 打包目录,最终发布的内容为 package.json 中 files 配置目录文件,当前 package 的 package.json 会被自动添加进去。这里使用了 release-it 插件,以方便快速地进行发布。还需注意,package.json 中要有相应的发布配置 --------------->架构源码参考: