• 第十二章 使用 Monorepo 方式管理组件生态


    组件库一般都会配有周边产品,比如 AdminTemplateCLI 工具等等。周边产品相当于有关联的多个项目,更准确的说法是多个软件包。这个时候就应该使用 Monorepo 方式组织代码,方便频繁在多个项目间同时交替开发,同时发布,保持版本间没有冲突。

    • 传统 Mutirepo 方式的不足

    所谓传统方式,我们称之为 Multirepo 方式,或者可以称之为 MutiPackage-MultiRepo 方式。就是说遇到多个软件包的场景,使用多个 Repo 仓库的方式组织代码。

    换句话说就是,一个软件包一个 Repo 仓库。其实我们常见的前端项目默认就是这样的模式。这种方式最大的问题就是在多个项目间切换开发会非常不方便。比如: 在开发 Admin 项目的时候,发现 UI 库需要增加了一个功能,那你需要以下步骤:

    • Git 库克隆 UI 库代码;
    • 修改 UI 库代码;
    • 推送 UI 库到 Git 库;
    • 推送 UI 库到 NPM 库;
    • Admin 中更新最新的 UI 库。

    这个过程假设一次修改不满意频繁更新,那么整个过程还会不断重复。

    优化的方案,是使用 npm link 方式把几个项目的本地目录链接起来。但是这种方法依然有弊端,比如在团队开发的时候,你必须随时同步所有的代码仓库。另外如果你的代码不希望公开到 NPM 上,你还需要建立私有的 NPM 仓库。

    • Monorepo 的优势

    Monorepo 其实就是将多个项目 (pacakage 软件包)放到同一个仓库 (Repo) 中进行管理。这种代码组织形式可以更好地管理多 Package 项目。主要的优点有:

    • 可见性 (Visibility): 每个开发者都可以方便地查看多个包的代码,方便修改跨 Package 的 Bug。比如开发 Admin 的时候发现UI 有问题,随手就可以修改。

    • 更简单的包管理方式(Simpler dependency management): 由于共享依赖简单,因此所有模块都托管在同一个存储库中,因此都不需要私有包管理器。

    • 唯一依赖源(Single source of truth): 每个依赖只有一个版本,可以防止版本冲突,没有依赖地狱。

    • 原子提交: 方便大规模重构,开发者可以一次提交多个包(package)。

    同样是上面的那个同时开发 AdminUI 的场景。当你开发 Admin 时,发现UI 有要修改之处,只需要切换目录修改,这时候马上就可以验证修改后的效果了,无需提交软件包,无需担心软件冲突。

    越复杂的场景,你会发现这种好处会更加明显。比如一个 UI 库对应两个Admin 。这时候你希望重构一下某个组件的属性。这种重构需要同时调整三个包中的代码。使用 Monorepo ,你可以不必有任何心智负担,调整后立刻验证两个 Admin 效果,同时发布就好。


    方案选型

    目前 JS 中常见的 Monorepo 大概有两种选择:LernaPnpm workspace

    其实在 Pnpm 横空出世前,基本上就是 lerna 一统天下的局面。连 Vue3 早期都是使用 lerna 做的 Monorepo 方案。

    lernaJS 是由 Babel 团队编写的多包管理工具。因为 Babel 体系的规模庞大后有很多子包需要管理,放在多个仓库管理起来比较困难。

    babel/packages 地址

    在这里插入图片描述

    以上图片只是一小部分,但是也已经很多了。

    2021 年底 Pnpm 横空出世,闪电般的性能一下子征服了所有前端开发者。更重要的是它还附带 monorepo 方案。这个时候基本上没有任何开发者会抵挡这种诱惑,包括Vue3.0

    最终毫无疑问,我们选择 Pnpm 来搭建我们的技术方案。


    修改软件包目录结构

    ├── packages
    |   ├── smarty-ui-vite  // UI组件库
    |   |   ├── package.json
    |   ├── docs-vite // docs文档
    |   |   ├── package.json
    ├── package.json
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    首先将原有的组件库代码移动至 smarty-ui-vite目录。

    在这里插入图片描述


    初始化Monorepo软件包

    • 在根目录重新初始化一个 npm。
    pnpm init
    
    • 1

    然后需要在软件包中禁用 npmyarn。这一步的目的是允许项目使用 pnpm 进行模块管理。不然的话会出现不兼容问题。

    方法是添加 preinstall npm hook 钩子,这个钩子会在安装模块前触发,检查该代码是否是使用 pnpm 运行。如果不是的话会推出并提示错误。

    文件名:./scripts/preinstall.js

    if (!/pnpm/.test(process.env.npm_execpath || '')) {
        console.warn(
          `\u001b[33mThis repository requires using pnpm as the package manager ` +
            ` for scripts to work properly.\u001b[39m\n`
        )
        process.exit(1)
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    文件名:package.json

    "scripts": {
      "preinstall": "node ./scripts/preinstall.js"
    }
    
    • 1
    • 2
    • 3

    或者可以考虑使用

    "scripts": {
        "preinstall": "npx only-allow pnpm"
    }
    
    • 1
    • 2
    • 3

    初始化工作空间

    monorepo项目中,每个软件包会存放在工作空间,方便管理。

    首先需要创建一个 pnpm-workspace.yaml,这个文件用于声明所有软件包全部存放在 packages 目录之中。其实目前 monorepo 风格的项目也普遍使用 packages 作为默认软件包目录位置。

    文件名:pnpm-workspace.yaml

    packages:
      # all packages in subdirs of packages/ and components/
      - 'packages/**'
    
    • 1
    • 2
    • 3

    创建一个新的软件包

    由于 smarty-ui-vite 这个组件库包已经放在 packages 目录之中了。无需其他过多的设置,它就可以当做 monorepo 工程中的一个项目了。

    下面我们试试从零开始如何使用 pnpm 创建一个子软件包,并正确引用组件库。

    比如:创建一个 docs-vite 用于文档化建设。在做文档化时需要引用 smarty-ui-vite 这个库,用于在网页上直接展示组件运行效果。

    • 初始化项目

    创建packages/docs-vite目录

    cd .\packages\docs-vite
    pnpm init
    
    • 1
    • 2
    • 安装 Vite

    这个时候就可以做一个选择了,假设我们认为 Vite 多个软件包都需要依赖,这个时候就可以选择将依赖安装到 workspace 中,这样每个包都可以使用 vite 而无需单独安装。

    # 安装 workspace 中
    pnpm i vite -w
    
    • 1
    • 2

    如果只安装在子 package 里面,可以使用 -r ,比如:

    # 子package安装
    pnpm i vue -r --filter smarty-ui-vite
    
    # 或者 直接在 docs-vite 目录下
    pnpm i vue
    
    • 1
    • 2
    • 3
    • 4
    • 5

    下面需要做的是将 study-vue-ui 安装到 docs-vite 中。

    # 内部依赖package安装
    pnpm i  study-vue-ui -r --filter docs-vite
    
    • 1
    • 2

    注意:study-vue-ui 是指你package.josnname,并不是文件夹名称

    在安装后, docs-vitestudy-vue-ui 的位置会指向到 workspace ,这也是 monorepo 的精髓所在。

    {
      "name": "docs-vite",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "dev": "vite"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "dependencies": {
        "study-vue-ui": "workspace:^1.0.4",
        "vue": "3.2.37"
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    编辑一个页面测试一下, 直接加载 node_module 中的 smarty-ui-vitemodulecss

    文件名:packages/docs-vite/index.html

    DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
    head>
    
    <body>
    
        <h1>🔨 SmartyUI Demoh1>
        <div id="app">div>
        <script type="module">
            import style from "study-vue-ui/dist/assets/entry.cb9ba4f4.css"
            import { createApp } from "vue/dist/vue.esm-bundler.js";
            import SmartyUI,{SFCButton,JSXButton,MyButton} from "study-vue-ui/dist/smarty-ui.mjs";
    
            createApp({
                template: `
            
    主要按钮 绿色按钮 灰色按钮 黄色按钮 红色按钮
    搜索按钮 编辑按钮 成功按钮 提示按钮 删除按钮
    `
    }).use(SmartyUI).mount('#app')
    script> body> html>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 添加启动命令并运行
    "scripts": {
        "dev": "vite"
      },
    
    • 1
    • 2
    • 3
    pnpm dev
    
    • 1
    • 浏览器查看结果

    在这里插入图片描述

  • 相关阅读:
    Mysql.索引详解
    nvm安装与永久配置
    简谈FPGA设计中系统运行频率计算方法与组合逻辑的层级
    【C++】Cmake使用教程(看这一篇就够了)
    工具分享:在线键盘测试工具
    力扣:343. 整数拆分
    nvidia docker安装和驱动安装
    融合多策略的萤火虫算法求解多目标优化问题(Matlab代码实现)
    基于知识问答的上下文学习中的代码风格11.20
    【ArcGIS】土地利用变化分析详解(矢量篇)
  • 原文地址:https://blog.csdn.net/qq_36362721/article/details/127993063