• 【Monorepo实战】pnpm+turbo+vitepress构建公共组件库文档系统


    Monorepo架构可以把多个独立的系统放到一起联调,本文记录基于pnpm.workspace功能,如何构建将vitepress组件库进行联调,并且使用turbo进行任务编排。

    技术栈清单: pnpmvitepressturbo

    一、需求分析

    1、最终目标

    我们最终需要实现这样的一个Monorepo项目架构:

    --project
      --package.json
      --packages
        --docs
          --package.json
        --project-one
          --package.json
      --pnpm-workspace.yaml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    其中各个目录功能如下:

    project:根目录

    packages:子项目包

    packages/docs:基于vitepress的文档项目

    packages/project-one:组件库

    2、Monorepo技术选型

    实现Monorepo架构的方式主要有以下两种:

    • lerna+yarn

    • pnpm中workspace配置

    这里选择后者,因为基于pnpm的包管理逻辑使用软硬链接,并且重复使用本地依赖包缓存,可以大大提升安装效率,节省依赖包存储空间。而且pnpm还继承了npm的所有功能,并且支持node版本管理,这里非常推荐pnpm

    3、为什么使用vitepress?

    之前有使用过vuepress,后来vite伴随vue3一起被推出,体验后果断切换到vite,基于esmodule的调试模式,调试效率直线提升。所以同理,这里也同样推荐使用vitepress,而不是vuepress

    4、turbo的作用

    turbo全名turborepo,本身也是一个Monorepo管理工具。这里主要使用其任务编排能力,能够按正确的顺序执行Monorepo内的项目的任务。
    例如:Monorepo里有三个项目A,B,C。A和C还需要依赖于B。也就是说B项目需要先构建好后,A和C才能构建。

    正常每个项目都按照这个顺序做任务编排:lint->build->test->deploy。即检查规范->构建项目->测试项目->部署项目。

    img

    从上图可以看到,使用lerna时,A、B、Cd这三个项目中的lint、build、deploy都是并行进行的。如果A、C都依赖B,那么A、C在build的时候,很可能 B d还没有build完成,会拖慢A、C的build效率。

    但是turbo能够将每个项目里的任务进行拓扑排序再执行。即图中B项目并行执行lint,test,build,之后A和C就可以更快的执行构建,大大提高了整体的效率。

    二、开发

    1、项目初始化

    1. 创建project根目录packages/docspackages/project-one三个项目目录,然后分别进入这三个目录执行pnpm init做项目初始化,生成package.json文件。
    2. 然后在根目录创建 pnpm-workspace.yaml 文件:
    packages:
      - 'apps/*'
      - 'packages/*'
    
    • 1
    • 2
    • 3

    识别packagesapps目录下的项目,作为子项目。这样在执行pnpm -F subProject add XXX --workspace安装命令时,就会到子项目中去寻找名为XXX的包。

    完成上述操作后,文件结构如下:

    --project
      --package.json
      --packages
        --docs
          --package.json
        --project-one
          --package.json
      --pnpm-workspace.yaml
      --turbo.json
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2、turbo编排

    1. 全局安装【turbo】:pnpm add turbo -g
    2. 然后在项目根目录创建turbo.json文件,代码如下:
    {
        "$schema": "https://turborepo.org/schema.json",
        "pipeline": {
            "build": {
                "dependsOn": [
                    "^build"
                ]
            },
            "test": {
                "dependsOn": [
                    "build"
                ],
                "outputs": [],
                "inputs": [
                    "src/**/*.tsx",
                    "src/**/*.ts",
                    "test/**/*.ts",
                    "test/**/*.tsx"
                ]
            },
            "lint": {
                "outputs": []
            },
            "deploy": {
                "dependsOn": [
                    "build",
                    "test",
                    "lint"
                ],
                "outputs": []
            }
        }
    }
    
    • 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

    配置项说明:

    • $schema:定义了 json 的类型,类似于 ts 对于 js 的约束
    • dependsOn:表示当前命令所依赖的相关流程
    • pipeline.build:表示当执行 turbo build 时会预先执行 ^build^build 就是所有项目的 package.json 里的那个 build 脚本,^ 是一个标记。如果像 pipeline.test 中的 build,他就指的是 pipeline 中的第一个 build 命令。
    • outputs:指定缓存输出目录
    • inputs: 配置的目录下文件有变化,才会重新执行此命令

    了解更多请参照:turbo官网

    3、根目录script命令行代理

    #package.json
    "scripts": {
        "turbo-test": "turbo run test",
        "turbo-build": "turbo run build",
        "test": "echo \"Error: no test specified\" && exit 1",
        "docs:dev": "pnpm --filter \"docs\" dev",
        "docs:build": "pnpm --filter \"docs\" build",
        "docs:serve": "pnpm --filter \"docs\" serve",
        "docs:preview": "pnpm --filter \"docs\" preview",
        "one:dev": "pnpm --filter \"project-one\" dev"
    }
    
    #docs package.json
    "scripts": {
        "dev": "vitepress dev",
        "build": "vitepress build",
        "serve": "vitepress serve",
        "preview": "vitepress preview",
        "test": "echo \"Error: no test specified\""
    }
    
    #project-one package.json
    "scripts": {
        "dev": "node index2.js",
        "build": "node index2.js",
        "test": "echo \"Error: no test specified\""
    }
    
    • 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

    4、初始化project-one项目

    代码如下:

    #index.js
    const addOne = (x = 0, msg = '未填') => {
        console.log('调用project-one的项目是:', msg);
        return x + 1;
    }
    export default addOne
    
    #index2.js
    console.log('nihao index2');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5、初始化docs项目

    docs使用vitepress构建,打开终端,进入docs目录,执行下面命令,对docs初始化:

    # 安装vitepress
    pnpm add -D vitepress 
    
    # 初始化项目,这里会出现交互式命令行,默认回车下一步就行
    pnpm dlx vitepress init
    
    • 1
    • 2
    • 3
    • 4
    • 5

    调整docs/package.json代码如下:

    {
      "name": "docs",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "dev": "vitepress dev",
        "build": "vitepress build",
        "serve": "vitepress serve",
        "preview": "vitepress preview",
        "test": "echo \"Error: no test specified\""
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "dependencies": {
        "project-one": "workspace:^",
        "vitepress": "1.0.0-rc.20",
        "vue": "^3.2.45"
      },
      "devDependencies": {}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    创建docs/index.md文件,代码如下:

    ---
    layout: home
    hero:
      name: VitePress
      text: Vite & Vue powered static site generator.
      tagline: Lorem ipsum...
      actions:
        - theme: brand
          text: Get Started
          link: https://github.com/jkkdeng
        - theme: alt
          text: View on GitHub
          link: https://github.com/jkkdeng
    
    features:
      - icon: 🛠️
        title: Simple and minimal, always
        details: Lorem ipsum...
      - icon: 🛠️
        title: Simple and minimal, always
        details: Lorem ipsum...
      - icon: 🛠️
        title: Simple and minimal, always
        details: Lorem ipsum...
    ---
    
    
    • 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

    注:import addOne from 'project-one',这行会在所有子项目中寻找project-one项目,然后识别project-one/package.json"main": "index.js",配置,然后获取到exportaddOne方法。最终打印到浏览器控制台。

    6、执行

    到这里就基本都配置完成了,这里我们把项目跑起来,看下运行效果。

    我们打开终端命令行,进入project根目录.

    1. 执行pnpm run turbo-build

      #turbo.json 
      "build": {
         "dependsOn": ["^build"]
      },
      
      • 1
      • 2
      • 3
      • 4

      系统会找到turbo.jsonpipeline.build下的配置:"dependsOn": ["^build"]^build含义是指上游的build任务优先执行。猜测turbo应该对各个子包中的package.json进行扫描,查看是否存在依赖关系,例如我们这里是docs项目依赖project-one项目,所有优先对proect-one项目进行build,然后在对docs进行build。看下面的运行结果也的确如此。
      image-20231009164000877

    2. 执行pnpm run turbo-test

      #turbo.json 
      "test": {
         "dependsOn": ["build"]
      },
      
      • 1
      • 2
      • 3
      • 4

      这里的build就是pipeline中的build任务,含义是:每个项目要执行自己的test任务,就要先执行build任务。

      可以看到执行顺序依次是:

      • project-one -> build
      • docs -> build
      • project-one -> test
      • docs -> test image-20231009165111296
    3. 执行pnpm run one:dev
      image-20231009165422131

    4. 执行pnpm run docs:dev
      image-20231009165515955

    image-20231009165640512

    源码

  • 相关阅读:
    HTB_Start Point_Tier2——Oopsie
    48.Java Lambda表达式
    MySql中事务详细理解学习(事务管理、事务隔离级别、事务传播机制)
    KVM虚拟化介绍和安装使用方法
    【LLM】金融大模型场景和大模型Lora微调实战
    Flume系列之:Java读取读取flume配置文件,并把配置写入到zookeeper节点,再根据写入到zookeeper中的配置启动flume agent
    超声波气象站——环境监测领域强大助手
    美妆行业如何通过自媒体提升品牌曝光
    Java多线程秘籍,掌握这5种方法,让你的代码优化升级
    Spring和SpringBoot简介
  • 原文地址:https://blog.csdn.net/bobo789456123/article/details/133708444