高效前端工程化:Monorepo、pnpm与Vue3集成实战指南

引言

在当今快速发展的前端开发领域,高效地管理和组织代码库成为提升开发效率的关键。随着项目规模的扩大,传统的单体仓库逐渐显露出局限性,而新兴的包管理工具如 PNPM、项目结构模式如 MonorepoTurborepo 开始受到广泛关注。将教会大家如何快速搭建 monorepo + pnpm + trborepo +vue3 + element-plus 项目架构。

pnpm:下一代包管理器

pnpm(Package Manager) 是一个快速、节省磁盘空间的 JavaScript 包管理器,它通过引入“链接”和“硬链接”的概念来优化 Node.js 项目的依赖管理。与 npmYarn 相比,pnpm 在安装依赖时,会创建依赖的唯一实例,并通过硬链接或符号链接的方式供各个项目共享,大大减少了磁盘占用和安装时间。此外,pnpm 的精确依赖解析机制能有效避免“dependency hell”,保障项目的稳定性和可复现性。

Monorepo:一统天下的仓库策略

Monorepo(单一仓库)是一种将多个相关项目的源代码存储在一个单一版本控制系统仓库中的策略。这种模式下,无论是微服务架构的后端服务,还是包含多个前端应用的大型项目,都可以共处一室,共享配置、依赖和工具链。Monorepo 的优势在于简化跨项目协作、代码复用、统一版本管理和 CI/CD 流程。然而,随之而来的是对版本控制系统的高效管理需求,以及如何处理大型仓库带来的构建速度问题。

Turborepo:为Monorepo加速

Turborepo 正是针对 Monorepo 模式下构建速度慢的问题提出的一种解决方案。它通过智能缓存、并行执行和增量构建等技术,显著加快了 Monorepo 中项目的构建和测试速度。Turborepo 能够识别出哪些文件或包没有变化,从而跳过不必要的工作,仅重新构建那些受影响的部分。这种优化对于大型组织而言尤为重要,它使得即使仓库包含成百上千个子项目,开发者也能获得接近即时的反馈循环,极大提升了开发效率。

单体仓库与上述方案的对比

相比之下,传统的单体仓库是指一个项目对应一个仓库的模式,适用于小型项目或初创阶段的项目。在单体仓库中,所有源代码、配置文件和依赖都紧密耦合在一起,便于管理但难以扩展。随着项目复杂度增加,代码库的维护成本和团队间的协调成本会迅速上升。

  • 可维护性与扩展性MonorepoTurborepo 由于支持跨项目共享和高效管理,明显优于单体仓库,尤其适合中大型项目和企业级应用。

  • 开发效率pnpm 通过优化依赖管理提升安装速度;Turborepo 则通过智能构建机制,解决了 Monorepo 的构建效率问题,两者共同推动了开发效率的飞跃。

  • 协作与代码复用Monorepo 鼓励跨项目代码共享,而 Turborepo 在此基础上进一步优化了协作体验,单体仓库在这方面则显得较为局限。

架构搭建

1.创建项目

新建文件夹自定义命名,暂且为 monorepo-demo,然后用VSCode编辑器打开,新建终端,操作如下:

2.利用 pnpm init 在根目录初始化

PS G:\wokespace\FullStackProjects\pnpm-monorepo-demo> pnpm init
Wrote to G:\wokespace\FullStackProjects\pnpm-monorepo-demo\package.json

{
  "name": "pnpm-monorepo-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
PS G:\wokespace\FullStackProjects\pnpm-monorepo-demo>

打开 package.json 文件,"private": true 加这个为防止我们意外地将私有项目发布。

3. 根据pnpm中的文档在根目录创建 pnpm-workspace.yaml 文件

根据自己项目需求创建合适的目录结构,示例如下:

4.在 packages 文件下创建存放 公共的UI组件 (ui) 和公共的工具函数 (utils) 两个项目

  • 新建 uiutils 文件夹,并利用 pnpm init 进行初始化。同时在各自的 package.json 文件中 新增属性 "private": true, 其中 name 属性值,可以自定义合适的名称。

ui 项目的名称这里自定义为 @repo/uiutils 项目的名称这里自定义为 @repo/utils

  • ui 项目下自定义新建 components 文件夹,用来存放公共的UI组件,暂时新建两个组件 FormatMoney.vueSlider.vue;然后同层级下新建 index.js 文件来导出组件提供外部访问。
  1. 利用 element-plus UI组件来开发公共UI组件,因此先安装依赖
pnpm i vue element-plus
  1. 编写 FormatMoney.vue, Slider.vue, index.js 文件

FormatMoney.vue

<template>
  <el-form
    ref="formRef"
    style="max-width: 600px"
    :model="numberValidateForm"
    label-width="auto"
    class="demo-ruleForm"
  >
    <el-form-item
      label="金额"
      prop="money"
      :rules="[
        { required: true, message: '金额不能为空' },
        { type: 'number', message: '金额是数字类型' },
      ]"
    >
      <el-input
        v-model.number="numberValidateForm.money"
        type="text"
        autocomplete="off"
      />
    </el-form-item>
    <el-form-item
      label="格式化后的金额"
      prop="amount" 
    >
      <el-input
        v-model="numberValidateForm.amount"
        type="text"
        autocomplete="off"
        readonly
      />
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="submitForm(formRef)">格式化</el-button>
      <el-button @click="resetForm(formRef)">重置</el-button>
    </el-form-item>
  </el-form>
</template>

<script setup>
import "element-plus/dist/index.css";
import { ElForm, ElFormItem, ElInput, ElButton } from "element-plus";
import { reactive, ref } from 'vue'
import { formatMoney } from "repo-utils";

console.log('formatMoney', formatMoney(2342113241, '$'));

const formRef = ref()

const numberValidateForm = reactive({
  money: '',
  amount: ''
})

const submitForm = (formEl) => {
  if (!formEl) return
  formEl.validate((valid) => {
    if (valid) {
      numberValidateForm.amount = formatMoney(numberValidateForm.money, '$');
      console.log('submit!')
    } else {
      console.log('error submit!')
    }
  })
}

const resetForm = (formEl) => {
  if (!formEl) return
  formEl.resetFields()
}
</script>

Slider.vue

<script setup>
import "element-plus/dist/index.css";
import { ElSlider } from "element-plus";
import { ref } from 'vue'

const value1 = ref(0)
</script>

<template>
  <div class="slider-demo-block">
    <span class="demonstration">默认值</span>
    <el-slider v-model="value1" />
  </div>
</template>

<style scoped>
.slider-demo-block {
  max-width: 600px;
  display: flex;
  align-items: center;
}
.slider-demo-block .el-slider {
  margin-top: 0;
  margin-left: 12px;
}
.slider-demo-block .demonstration {
  font-size: 14px;
  color: black;
  width: 120px;
  padding: 10px;
}
</style>

index.js

import FormatMoney from './components/FormatMoney.vue'
import Slider from './components/Slider.vue'

export {
  FormatMoney,
  Slider
}
  1. UI 项目最后的目录结构如下:

  • utils 项目下新建两个文件 fun.js (存放各种工具方法)index.js (提供对外访问的方法)

fun.js

// 弹窗提示
export const tips = (message, title = "提示") => {
  window.alert(`${title}: ${message}`)
}

// 加运算
export const addOperation = (a, b) => {
  window.alert(`1加2的结果是${a + b}`);
}

// 格式化金额
export const formatMoney = (money, symbol = "", decimals = 2) => {
  return (Math.round((parseFloat(money) + Number.EPSILON) * Math.pow(10, decimals)) / Math.pow(10, decimals)).toFixed(
    decimals
  )
  .replace(/\B(?=(\d{3})+\b)/g, ",")
  .replace(/^/, `${symbol}`)
};

index.js

export * from "./fun.js";

5. 利用 vite 工具在 apps 文件下创建各种子项目,暂且创建 docsweb 两个项目,操作如下:

pnpm create vite 

执行上面的命令,按照提示一步步根据自己需求创建两个项目,最终目录结构如下:

6.回到根目录,在根目录下全局安装 @repo/ui@repo/utils ,这样在任何子应用或者子包都可以相互使用。 如果要安装到根项目中(即全局项目中)那么可以在指令后面加上 -w

pnpm i -w @reop/ui @repo/utils

安装完毕后,可以在 package.json 文件中看到如下信息:

7.在子项目 docsweb 中使用

  • 进入 web 项目,将 App.vue 文件内容修改如下:
<script setup>
import { tips } from "repo-utils";
import { FormatMoney } from "repo-ui";
</script>

<template>
  <h1>web项目</h1>
  <FormatMoney></FormatMoney>
</template>

<style scoped>
h1 {
  margin-bottom: 50px;
}
</style>
  • 进入 docs 项目,将 App.vue 文件内容修改如下:
<script setup>
import { tips } from "repo-utils";
import { FormatMoney, Slider } from "repo-ui";
tips("我是docs项目");
</script>

<template>
  <h1>docs项目</h1>
  <Slider></Slider>
</template>
<style scoped>
h1 {
  margin-bottom: 50px;
}
</style>

分别启动项目,运行效果如下:

使用 Turborepo 构建打包

1.全局安装 Turborepo

pnpm i -g turbo

# 检测是否安装成功
λ turbo --version
1.13.3

2.根目录新建文件 turbo.json, 默认内容如下:

{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": ["**/.env.*local"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", "dist/**"]
    },
    "lint": {
      "dependsOn": ["^lint"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

3.在根目录下修改 package.json 文件,添加执行命令脚本, 具体如下:

4. 进入各个子项目或者子包中,修改 .gitignore 文件,增加如下内容:

.turbo
build/
dist/
.next/

5.根目录执行 pnpm devpnpm build,会对子项目全量启动或打包,具体如下:

  • 全量启动项目
λ pnpm dev

> monorepo-demo@1.0.0 dev G:\wokespace\FullStackProjects\pnpm-monorepo-demo
> turbo dev

• Packages in scope: docs, repo-ui, repo-utils, web
• Running dev in 4 packages
• Remote caching disabled
docs:dev: cache bypass, force executing 0906b5c91c3b269b
web:dev: cache bypass, force executing b446edc8270ef0f2
docs:dev:
docs:dev: > docs@0.0.0 dev G:\wokespace\FullStackProjects\monorepo-demo\apps\docs
docs:dev: > vite
docs:dev:
web:dev:
web:dev: > web@0.0.0 dev G:\wokespace\FullStackProjects\monorepo-demo\apps\web
web:dev: > vite
web:dev:
docs:dev: Port 5173 is in use, trying another one...
web:dev:
web:dev:   VITE v5.2.8  ready in 20313 ms
web:dev:
web:dev:   ➜  Local:   http://localhost:5173/
web:dev:   ➜  Network: use --host to expose
web:dev:   ➜  Vue DevTools: Open http://localhost:5173/__devtools__/ as a separate window
web:dev:   ➜  Vue DevTools: Press Alt()+Shift()+D in App to toggle the Vue DevTools
web:dev:
docs:dev:
docs:dev:   VITE v5.2.8  ready in 20368 ms
docs:dev:
docs:dev:   ➜  Local:   http://localhost:5174/
docs:dev:   ➜  Network: use --host to expose
docs:dev:   ➜  Vue DevTools: Open http://localhost:5174/__devtools__/ as a separate window
docs:dev:   ➜  Vue DevTools: Press Alt()+Shift()+D in App to toggle the Vue DevTools
docs:dev:

  • 全量打包子项目
λ pnpm build

> monorepo-demo@1.0.0 build G:\wokespace\FullStackProjects\monorepo-demo
> turbo build

• Packages in scope: docs, repo-ui, repo-utils, web
• Running build in 4 packages
• Remote caching disabled
web:build: cache miss, executing 5987b2c98bceeb10
docs:build: cache miss, executing aff8ae65ab7f527e
web:build:
docs:build:
web:build: > web@0.0.0 build G:\wokespace\FullStackProjects\monorepo-demo\apps\web
web:build: > vite build
web:build:
docs:build: > docs@0.0.0 build G:\wokespace\FullStackProjects\monorepo-demo\apps\docs
docs:build: > vite build
docs:build:
web:build: vite v5.2.8 building for production...
docs:build: vite v5.2.8 building for production...
web:build: transforming...
docs:build: transforming...
web:build: ✓ 1435 modules transformed.
docs:build: ✓ 1435 modules transformed.
docs:build: rendering chunks...
web:build: rendering chunks...
docs:build: computing gzip size...
web:build: computing gzip size...
docs:build: dist/index.html                       0.43 kB │ gzip:  0.29 kB
web:build: dist/index.html                       0.43 kB │ gzip:  0.29 kB
docs:build: dist/assets/AboutView-C6Dx7pxG.css    0.09 kB │ gzip:  0.10 kB
web:build: dist/assets/AboutView-C6Dx7pxG.css    0.09 kB │ gzip:  0.10 kB
web:build: dist/assets/index-B_6bWB-a.css      328.68 kB │ gzip: 45.17 kB
web:build: dist/assets/AboutView-9L8e1QJt.js     0.23 kB │ gzip:  0.20 kB
docs:build: dist/assets/index-DM4ReSuC.css      328.68 kB │ gzip: 45.17 kB
web:build: dist/assets/index-BUYK55Bj.js       175.04 kB │ gzip: 65.02 kB
docs:build: dist/assets/AboutView-CgU-TZmY.js     0.23 kB │ gzip:  0.20 kB
docs:build: dist/assets/index-wHPYhhNI.js       195.12 kB │ gzip: 72.35 kB
web:build: ✓ built in 14.67s
docs:build: ✓ built in 14.67s

 Tasks:    2 successful, 2 total
Cached:    0 cached, 2 total
  Time:    25.729s

关于 Turborepo 具体教程,请参考官网文档进行查阅,目前正在翻译官网文档,后续会开放浏览地址供阅览。

写在最后

至此一步步完成了利用 pnpm, monorepo, turborepo 等技术搭建多项目多包统一仓库管理的架构,解决了项目规模扩大后的管理难题,提高了开发效率和团队协作水平。选择合适的工具和策略,对提升项目成功率至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/628115.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

批量下载huggingface的仓库全部权重文件

下载huggingface的仓库全部权重文件 配置和下载git-lfs **ubuntu:**sudo apt-get install git-lfs 其他&#xff1a; 下载git-lfs Releases git-lfs/git-lfs (github.com) 配置&#xff1a; export PATH$PATH://home/software/lfs/git-lfs-3.5.1/ # 其中目录为你文件夹的目…

功能安全如何在公司顺利开展?-亚远景科技

亚远景功能安全主题线上会议报名开启&#xff01; 随着汽车技术的不断发展&#xff0c;汽车系统的复杂性和交互性大幅增加&#xff0c;功能安全成为确保驾驶员、乘客及行人安全的关键。 本场功能安全线上会议&#xff0c;亚远景为汽车行业的相关人员准备了以下内容&#xff1a…

多维 HighChart

showHighChart.html <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><!-- js脚本都是官方的,后两个是highchart脚本 --><script type"text/javascript" src"jquery1.7.1.min.js"&g…

中成科信票务管理系统 SeatMapHandler.ashx SQL注入漏洞复现

0x01 产品简介 中成科信票务管理系统是专注于演出剧院、体育场馆、旅游景区、游乐园、场地活动的票务管理系统,并为特殊客户量身定制票务应用解决方案,可根据用户的要求采用不同的技术载体实现门票的防伪:二维条码门票防伪技术、RFID电子门票防伪技术、手机二维码门票技术、…

09.Hadoop的安装

Hadoop的安装 1.拖拽安装包上传到/opt/software 2.解压文件到/opt/module [itwisenode2 software]$ tar -zxvf hadoop-3.1.3.tar.gz -C /opt/module/3查看文件 drwxr-xr-x. 2 itwise itwise 4096 9月 12 2019 bin drwxr-xr-x. 3 itwise itwise 4096 9月 12 2019 etc …

基于jsp+servlet的网上商城

网上商城系统&#xff08;jspservlethtmlcssjsbootstrap&#xff09; 一、运行项目 在项目的doc文件夹下&#xff0c;有文档&#xff0c;教您怎么启动项目。 二、运行截图 a.项目前端页面 b.后台登录界面 c.后台展示界面 三、用户名和密码 前台的用户名是&#xff1a;mor…

07. 【Java教程】Java 集成开发环境 - IntelliJ IDEA

本小节我们将介绍如何在我们的电脑上安装并配置开发工具&#xff1a;IntelliJ IDEA 1. IDE 概述 1.1 IDE 是什么&#xff1f; IDE 即 Integrated Development Environment 的缩写&#xff0c;中文意为集成开发环境&#xff0c;是用于提供程序开发环境的应用程序&#xff0c;一…

stl学习以及abc比赛例题

1.引例 一提到查找&#xff0c;我们一上来想的肯定是find()函数或者search()函数&#xff0c;但是这种查找的底层逻辑终究是用顺序查找的方式&#xff0c;运行的时间成本非常高昂&#xff0c;所以平时能不用就不用&#xff0c;比赛的时候用这种查找和自己while遍历&#xff0c…

全域运营平台是什么?优缺点有哪些?

当下&#xff0c;全域运营赛道逐渐兴盛&#xff0c;全域运营服务商的数量也开始呈现爆发趋势。在此背景下&#xff0c;很多人都对某些品牌的全域运营平台优缺点产生了浓厚的兴趣。由于小编只使用过微火全域运营平台&#xff0c;因此&#xff0c;本期会着重分析微火运营平台的优…

python 视频转mp3

今天分享一个 Python 脚本&#xff0c;这个 Python脚本借助moviepy和FFmpeg&#xff0c;将指定的视频文档转码为mp3文档。 本Python脚本借助Everything的强大搜索能力&#xff0c;在协助用户搜索和定位视频文件方面提供了极大的便利。 本Python脚本默认将转码的文件与源视频文…

ORACLE ODAX9-2的一个误告警Affects: /SYS/MB的分析处理

在运维的多套ORACLE ODAX9-2版本&#xff0c;都遇到了一个计算节点的告警&#xff1a;Description: The service Processor poweron selftest has deteced a problem. Probabity;:100, UulD:cd1ebbdf-f099-61de-ca44-ef646defe034, Resource:/SYS/MB,&#xff1b;此告警从描述上…

Go系列:git status 高级技巧

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

单片机片上资源——串口讲解

串口模式图 SBUF&#xff1a;串口数据缓存寄存器&#xff0c;物理上是两个独立的寄存器&#xff0c;但占用相同的地址。写操作时&#xff0c;写入的是发送寄存器&#xff0c;读操作时&#xff0c;读出的是接收寄存器

【全开源】JAVA红娘婚恋相亲交友系统源码支持微信小程序+微信公众号+H5+APP

红娘婚恋相亲交友系统&#xff1a;遇见你的命中注定 在快节奏的现代生活中&#xff0c;许多单身男女都在寻找一个平台&#xff0c;希望能遇见那个能与自己携手共度一生的伴侣。红娘婚恋相亲交友系统正是为了满足这一需求而诞生的&#xff0c;它旨在为广大单身男女提供一个安全…

7.学习STL中的string类:版本、组件、构造、操作及应用

目录 1. 什么是STL 2. STL的版本 3. STL的六大组件 1. 为什么学习string类&#xff1f; 1.1 C语言中的字符串 2. 标准库中的string类 2.1 string类(了解) 2.2 string类的常用接口说明 1. string类对象的常见构造 2. string类对象的容量操作 reserve 3. string类对象…

百面算法工程师 | python解释器基础问答

本文给大家带来的百面算法工程师是深度学习python解释器面试总结&#xff0c;文章内总结了常见的提问问题&#xff0c;旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中&#xff0c;我们还将介绍一些常见的python用法&#xff0c;并提供参考的回答及其理论基础&…

【Flask框架】

6.Flask轻量型框架 6.1Flask简介 python提供的框架中已经写好了一个内置的服务器&#xff0c;服务器中的回应response行和头已经写好&#xff0c;我们只需要自己写显示在客户端&#xff0c;的主体body部分。 ---------------------------------------------------------- Fla…

OpenAI新模型GPT-4o“炸裂登场” 响应速度堪比真人 关键还免费!

GPT-4o模型基于来自互联网的大量数据进行训练&#xff0c;更擅长处理文本和音频&#xff0c;并且支持50种语言。更值得一提的是&#xff0c;GPT-4o最快可以在232毫秒的时间内响应音频输入&#xff0c;几乎达到了人类的响应水平。 GPT-4o有多“炸裂”&#xff1f;核心能力有三 G…

Web前端学习路线

本文发表于入职啦(公众号: ruzhila) 大家可以访问入职啦学习更多的编程实战。整理了一份关于前端学习的指南&#xff0c;希望对大家有所帮助。 为什么需要学习前端&#xff1f; 本文讲的前端是指Web开发前端&#xff0c;不包括Android、iOS、小程序等移动端开发。 当前的浏览…

【面试必看】MySQL部分

MySQL 1. 基础 1. 什么是关系型数据库&#xff1f; 一种建立在关系模型的基础上的数据库。关系模型表明了数据库中所存储的数据之间的联系&#xff08;一对一、一对多、多对多&#xff09;。各种表中&#xff08;比如用户表&#xff09;&#xff0c;表中的每一行就存放着一条…