从零开始打造基于Module Federation的微前端架构(一)——Monorepo架构与远程模块集成

前言

本篇开始,一起讨论一下,基于Module Federation的微前端架构的架设方案。内容主要涉及到以下几个方面:

把单体应用,拆分为多个微应用,不可能一拆了之。微前端解决单体应用痛点的同时,也会带来很多挑战。这些挑战主要体现在各个微前端应用之间,如何共享状态和通信?以及什么是最符合微前端架构的工作流程?无论是项目结构、路由管理、数据状态管理、还是git工作流,都发生了巨大的转变。

我们的工作,主要围绕,探寻拆分和共享之间存在哪些问题?进而找到解决问题的最佳方案而展开。

  1. 从零开始打造基于Module Federation的微前端架构(一)------ Monorepo架构与远程模块集成
  2. 从零开始打造基于Module Federation的微前端架构(二)------ 静态模块集成和模板管理
  3. 从零开始打造基于Module Federation的微前端架构(三)------ 路由管理挑战和解决方案
  4. 从零开始打造基于Module Federation的微前端架构(四)------ 数据状态管理方案
  5. 从零开始打造基于Module Federation的微前端架构(五)------ 工作流最佳实践
  6. 从零开始打造基于Module Federation的微前端架构(六)------ Unit Test单元测试
  7. 从零开始打造基于Module Federation的微前端架构(七)------ Auto Test自动化测试
  8. 从零开始打造基于Module Federation的微前端架构(八)------ 打包和部署

本篇文章的内容,将围绕相关概念、主要工具、基础项目结构、远程模块集成展开讨论。

导读

  • 阐述什么是微前端?有哪些微前端的实现方案?以及微前端有优缺点?
  • 讨论微前端项目最佳管理方案monorepos,以及monorepos的优缺点。
  • 引入pnpm、turborepo等工具,解决monorepos带来的一些痛点
  • 安装配置module federation,集成动态模块

一、微前端

先来看一下,什么是微前端?只有理解了什么是微前端,了解微前端的价值所在,才有可能搭建出优秀微前端架构。

1、什么是微前端?

微前端是一种多个团队,通过独立发布功能的方式,来共同构建现代化 web 应用的技术手段及方法策略。

  • 首先,微前端不是新技术,是构建web应用的策略和方法,也就是前端架构管理的一种模式;
  • 其次,微前端鼓励团队拆分,各司其职,各个团队负责的模块可独立开发、构建、测试和部署;
  • 另外,微前端是将体量大、复杂难以管理的单体应用,拆分成多个微型前端应用,且这些微应用功能上是相互关联的,最终重新聚合成一个统一应用。

总之,微前端架构,借鉴了微服务的架构理念,将Web应用由单一的单体应用,转变为多个小型前端应用聚合为一的一种手段,一种面向垂直划分系统的前端集成。

可能大家依然心存疑惑,为什么要拆分应用,再重新聚合?这么做的意义在哪里?换句话说,微前端到底能解决应用开发和管理中那些痛点呢?

2、微前端有哪些优点?

微前端的优点有很多,大多数都是针对单体巨石应用的痛点。

A、构建性能效率提升

单体应用,随着代码量、和文件数量日益增加,项目变得越来沉重。无论开发启动、还是修改刷新,速度都会变得越来越缓慢。如果说,单次启动需要花费10多分钟,还可以忍受,每次修改刷新等待10几秒,是无论如何都无法忍受的事情。

微前端的思路是,把巨石应用拆分成多个微型应用,每个微应用都有自己独立的构建体系。任务并行执行,最大限度的使用线程,以空间换时间,构建速度的问题自然就解决了。

B、微应用各个应用独立自治

单体应用是一个整体,即使很小的调整,都要整体打包,无法实现增量打包和部署。长年累月堆积了无数技术债,在这种牵一发而动全身的模式下,变的越来越无法管理。

而微前端模型下的微应用彼此隔离,每个微应用都具备独立的构建体系、测试体系、部署流程、运行服务。所以具备以下优点:

  • 每个微应用是独立完备,且彼此隔离,所以可以突破技术栈限制,选择更加灵活;
  • 微应用无需每次都构建部署所有应用,可以增量按需独立构建部署;
  • 微应用都有自己的运行服务,彼此之间影响较小;
  • 微应用隔离特性,有利于应用重构升级;
  • 松散耦合的架构体系,更易于维护和扩展;

3、微前端有哪些缺点?

把应用拆分成,十数个、甚至数十个彼此独立的微型应用,可以解决巨石应用构建打包效率低效的问题;但随着应用数量的急速增加,相应的也会带来一些问题:

  • 重复构建,部署频次增加,增加了生产发布风险
  • 每次启动多个开发服务,会带来内存压力
  • 重复命令行执行动作,效率低,易出错
  • 应用版本升级管理复杂、繁琐
  • 生态工具,缺乏对应的支持,比如router和store

但相对比,巨石应用带来的超低开发效率和管理困难问题,这些问题相对来说更容易解决。微应用方案收益也很明显。

4、微前端常用的实现方案

前面我们讨论了,微前端的概念和优缺点。下面我们一起看一下,目前流行的几种微前端架构的实现方案。

我们的重点是module federation,所以这里对实现方案,不做深入讨论,简单了解一下。

  • 乾坤(qiankun):蚂蚁金融科技孵化项目,方案基于 single-spa,采用路由劫持和沙箱隔离,html entry实现方案

  • micro-app 是基于 webcomponent + qiankun sandbox 的微前端方案。不同于乾坤,micro-app采用组件的方式加载子应用。

  • 无界(wujie):微前端方案基于 webcomponent 容器 + iframe 沙箱,能够完善的解决适配成本、样式隔离、运行性能、页面白屏、子应用通信、子应用保活、多应用激活、vite 框架支持、应用共享等用户的核心诉求

  • module federation:webpack提供的一种拆分和加载远程模块的机制,可用于实现微前端。

我们方案是module federation,我们将借助module federation实现微前端应用集成。下面我们先了解一下module federation,方便后面为后面集成微应用,理清楚思路。

二、什么是Module Federation 模块联邦

简单说,module federation(下面简称MF)是一种拆分和加载远程模块的机制。MF出现之前,webpack模块只有本地模块,只区分同步加载和异步加载的模块。但随着微服务、全栈开发、服务端渲染的发展日渐成熟,远程模块的需求愈加迫切。MF便是为了弥补远程模块这一块缺失而产生的。

事实上,弥补了远程模块这块缺失,也为实现拆分微型应用提供技术支持。MF把模块区分为本地模块和远程模块。本地模块是当前构建的一部分,而远程模块则不然,远程模块属于异步加载操作,是远程构建的一部分。

MF把每个构建看做容器,每个容器都可以对外暴露本地模块,也可以加载其他容器暴露的远程模块。因此,容器都具备双重角色,即是消费者,也可以是生产者。生产者称之为Host容器,消费者称之为Remote容器。

MF解决了拆分和加载远程微模块的问题。但是如何组织管理微模块,却是个复杂的问题。按照什么维度拆分应用?如何提取管理共享模块?如何更有效的组织代码库?如何更有效的减少多应用带来的重复流程?

简而言之,我们首先如何高效的集中管理拆分出来的多个微应用?这些都是接下来我们将要讨论的monorepo功能范畴。

三、Monorepo应用架构模型

1、monorepo概念

单体应用架构模型叫做monolithic,所有业务代码放在统一的代码库中,共享一个单独的构建部署流程。

单体应用,搭建简单,易于管理,易于构建、测试、部署。大多数情况下,单体应用架构模型,就是理想的项目架构模型。

但是单体应用,容易受到规模效应的影响。容易累积成巨石应用,管理效率会越来越低。而且构建部署在同一个应用里面,业务模块耦合度比较高,容易影响到整体应用。难以重构,容易积累技术债。

一旦积累成巨石应用,最终势必会面临,重新拆解成多个微型的应用。

管理多个微型应用,比较流行的架构模型主要有两种:multirepomonorepo

multirepo架构模型也叫polyrepo,这种架构模型下,每个子应用都有独立的git代码库、独立的构建和应用独立部署。项目之间天然隔离,拥有充分的自由度,项目内部高度自治。易于重构,进而享受新技术红利。

但是这种高度隔离模式也会带来很多缺陷:

  • 重复流程、低效率、易出错、维护成本高;
  • 共享成本比较高,提取共享模块困难;
  • 不能直观反馈项目之间依赖关系,容易出现生产bug;

另外一种架构模型,所有的子应用,共用一个git代码库,但子应用保留独立的构建和部署,这种应用架构模型就叫做monorepo

从架构模型的演变过程角度,monorepo 可以看做前面两种架构模型的变体。

multirepo 汲取 monolithic 的单一代码库的优点,把所有应用代码集中放置,保留独立构建的同时,兼备了易共享的特性,形成了新的应用架构模型monorepo

2、monorepo优点

monorepo架构模型,保留单体应用的易共享的特性;同时还兼顾了multirepo模式下,应用之间高度隔离的特性。

monorepo兼备两种模型的优点:

  • 共享时间线,迫使团队之间积极交流,每个人都得努力跟上变化
  • 容易提取共享模块,版本维护简单
  • 容易共享配置,统一代码质量标准和代码风格的一致性
  • 微应用之间保持隔离性,可以使用不同的技术栈,易于项目重构
  • 独立启动服务、独立构建,容易实现增量发布

从特性上来看,multirepo 适合关联度不太紧密的项目拆分,但高度隔离性,使得提取共享模块困难,重复搭建和配置,调整、构建、测试、部署流程几何倍增加;而 monorepo 更适合关联性比较紧密的项目拆分,保留了单体应用的统一管理优势的同时,解决了单体应用体量过大造成的低效问题。monorepo 更适合微应用架构。

3、monorepo缺点

微应用背后的设计思路,其实是空间换时间。多个应用并行执行,占用了更大的内存和更多进程,但提高了编译速度,节省了编译时间,解决了巨石应用造成的开发效率问题。

但微应用数量急速增加同时,也带来了一些问题:

  • 依赖包管理,程数量级增加
  • task执行,程数量级增加
  • 应用版本管理变得更加复杂
  • 代码质量和统一代码风格,变得尤为重要
  • 整体应用编译时间较长,需要并行编译和编译缓存
  • 对开发人员的整体素质要求更高

为了解决上述问题,我们把微应用,统一放置在同一个workspace下。只有集中放置微应用,才能借助适合的worksapce工具,实现统一管理依赖、task和应用版本的目标。只有解决掉这些痛点,monorepo架构模型的价值,才能真正的体现出来。

4、创建monorepo workspace

我们简单的按照集成方式,把monorepo中的模块划分为两种:

  • 一种是可以在本地打包集成的模块叫做静态模块,静态模块通过npm模式加载
  • 另外一种是runtime运行时,远程加载集成的模块叫做动态模块,动态模块通过http server(module federation)模式加载

创建项目microfe

sh 复制代码
mkdir microfe && cd microfe

创建apps和packages文件夹,apps目录下放置应用(动态远程模块),packages下放置共享模块()。

sh 复制代码
mkdir apps packages

创建微应用目录

sh 复制代码
mkdir apps/main-app apps/book-app apps/movie-app app/tv-app app/music-app
mkdir packages/pc-react-ui packages/pc-vue-ui packages/utils
  • main-app 为主应用,集成加载展示其他应用的入口
  • apps/main-app apps/book-app apps/movie-app 是react应用
  • app/tv-app app/music-app 是vue应用
  • packages/pc-react-ui 是react组件库
  • packages/pc-vue-ui 是vue组件库
  • packages/utils 是通用js工具库

创建主应用,目录结构如下

sh 复制代码
# apps/main-app
|--src
    |--index.js
    |--App.js
    |--subApps

其他应用,将通过module federation暴露应用入口

创建子应用,目录结构如下基本相同

react应用

sh 复制代码
# apps/movie-app app/book-app
|--src
    |--index.js
    |--bootstarp.js
    |--App.vue

Vue应用

sh 复制代码
# app/music-app app/tv-app
|--src
    |--index.js
    |--bootstarp.js
    |--App.vue

目录结构

在实现应用代码之前,我们还需要解决一个棘手的问题。那就是批量安装和卸载依赖,以及批量执行task的问题。

管理十几个甚至数十个微应用、微模块。手动安装和卸载或者升级依赖,几乎是一个不可能完成的任务。我们需要支持workspace的包管理器,来解决这个问题。

四、选择合适的包管理工具

目前流行的前端包管理工具npm、yarn、pnpm都支持workspace。综合对比后,我们选择pnpm,作为包管理工具。

那么pnpm相对npm和yarn有哪些优势呢?

1、为什么选择pnpm?

  • 安装速度提升,pnpm安装速度至少是npm、yarn的2倍;

  • 大幅度节省磁盘空间,pnpm包安装在一个地方,并通过链接的方式实现共享,不会在每个依赖的地方重复安装,这相对npm和yarn每个应用都要安装组件的依赖,安装大幅度节省了磁盘空间;

  • 解决了幽灵依赖问题,npm和yarn依赖包扁平化,造成了一个问题,未在package.json里显性声明的,依赖包B,由于被A依赖,扁平化依赖结构时,B被提升到了node_modules的根目录下。因此可以在项目中import或者require到B。但是一旦A被移除或者升级,就会造成B可能被移除,这时候就会报错。pnpm的依赖结构是嵌套式的,不存在这个问题;

  • 支持monorepos,pnpm内置更加完善的monorepo支持;

2、安装和使用pnpm

A、pnpm是什么?

pnpm全称是 "Performant NPM",即高性能的 npm。它结合软硬链接与新的依赖组织方式,大大提升了包管理的效率,也同时解决了 "幻影依赖" 的问题,让包管理更加规范,减少潜在风险发生的可能性。

B、安装pnpm

sh 复制代码
npm install -g pnpm

或者

sh 复制代码
npm install -g @pnpm/exe

如果安装了包管理器HomeBrew,则可以使用以下命令安装 pnpm:

sh 复制代码
brew install pnpm

C、配置pnpm workspace

根目录下创建pnpm-workspace.yaml文件

sh 复制代码
touch pnpm-workspace.yaml

输入配置

yaml 复制代码
packages:
  # 放置共用模块与应用
  - 'packages/*'
  # 放置微应用
  - 'apps/*'

  # 忽略目录
  - '!**/test/**'
  - '!**/__test__/**'

切换到根目录,初始化package.json

sh 复制代码
pnpm init

调整配置如下,private是为了防止误发布

json 复制代码
{
  "name": "microfe",
  "version": "1.0.0",
  "private": true,
  "description": "micro front end apps",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "micro front end",
    "monorepo"
  ],
  "author": "yangguangzaifei",
  "license": "MIT"
}

初始化子应用,分别在上述创建的目录中执行命令,创建package.json

sh 复制代码
cd apps/main-app
pnpm init

并调整name为@microfe/main-app,其他调整如下:

json 复制代码
{
  "name": "@microfe/main-app",
  "version": "0.0.0",
  "private": true,
  "sideEffects": false,
  "description": "book app",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "yangguangzaifei",
  "license": "ISC"
}

其他目录 apps/book-appapps/music-appapps/book-appapps/tv-apppackages/p-react-uipackages/pc-vue-ui packages/utils 重复上述动作

这里我们把应用名称,统一设置为类似 @microfe/* 格式,方便后面批量执行命令。

D、锁定pnpm

为了统一,我们锁定 pnpm 为唯一包管理工具

安装only-allow,并package.json添加 preinstall hook

sh 复制代码
pnpm add only-allow -wD
json 复制代码
{
    scripts:{
        "preinstall": "npx only-allow pnpm",
    }
}

如果尝试使用yarn安装依赖,将提示下述内容:

bash 复制代码
yarn add lodash
bash 复制代码
Use "pnpm install" for installation in this project.     
If you don't have pnpm, install it via "npm i -g pnpm". 
For more details, go to https://pnpm.js.org/

E、使用pnpm

由于我们定义了 pnpm 的workspace,安装时,需要遵循workspace规范。必须明确指明,安装依赖的位置目录,否则会出现异常警告:

sh 复制代码
ERR_PNPM_ADDING_TO_ROOT  Running this command will add the dependency to the 
workspace root, which might not be what you want - if you really meant it, 
make it explicit by running this command again with the -w flag (or --workspace-root). 
If you don't want to see this warning anymore, you may set the ignore-workspace-root-check setting to true.

这意味着,安装、卸载、升级依赖,必须带有workspace相关参数。pnpm 有关workspace的命令参数最主要是以下几个:

yml 复制代码
--recursive(-r): 所有应用目录执行,不包括根目录
--workspace-root(-w): 根目录执行
--filter(-F): 匹配目录执行

根目录下管理依赖

sh 复制代码
pnpm add xxx -w
pnpm update xxx -w
pnpm remove xxx -w

workspace中定义的子应用下管理依赖

sh 复制代码
pnpm add xxx -r
pnpm update xxx -r
pnpm remove xxx -r

仅仅apps目录下面的应用管理依赖

sh 复制代码
pnpm add xxx -F ./apps
pnpm update xxx -F ./apps
pnpm remove xxx -F ./apps

也支持glob模糊匹配应用名称

sh 复制代码
pnpm add xxx -F @microfe/*
pnpm update xxx -F @microfe/*
pnpm remove xxx -F @microfe/*

需要注意的是,上述的参数不仅仅支持依赖管理,同样支持pnpm的其他命令。比如run,可以批量执行workspace下匹配的应用下相同的命令

sh 复制代码
pnpm run dev -r
pnpm -F @microfe/* rum lint

这样我们就可以很方便的管理多个应用的安装、启动、打包、测试等等。否则手动管理,如此繁琐的命令操作,会大大降低工作效率。

五、集成远程模块(module federation)

安装配置完pnpm,我们就可以尝试集成module federation,把momorepo中的模块整合起来,跑一下整体应用了。

1、安装依赖

首先安装webpack及其相关插件,由于属于公用依赖,我们安装在根目录

sh 复制代码
pnpm add webpack webpack-cli  -wD

pnpm add webpack-manifest-plugin webpack-dev-server webpack-merge html-webpack-plugin -wD

pnpm add css-loader babel-loader style-loader sass-loader -wD

安装babel相关配置,同样安装在根目录

sh 复制代码
pnpm add @babel/core @babel/preset-env @babel/preset-react @babel/plugin-transform-runtime -wD

安装react

shell 复制代码
pnpm -F='./apps/{main,movie,book}-app' -F='./packages/{pc-react-ui,utils}' add react react-dom -D 

安装vue

shell 复制代码
pnpm -F='./apps/{tv,music}-app' -F='./packages/{utils,pc-vue-ui}' add vue

pnpm -F='./apps/{tv,music}-app' -F='./packages/{utils,pc-vue-ui}' add @vue/compiler-sfc -D

2、使用 module federation集成远程模块

以 book-app子应用为例:

A、完善子应用

App应用入口,声明跟普通应用一致。

App.js

js 复制代码
import React from 'react'

function BookApp() {
  return (
    <div>
      <h1>Welcome to book app</h1>
    </div>
  )
}

export default BookApp

App.vue

jsx 复制代码
<template>
  <div>
    <h1>{{ message }}</h1>
  </div>
</template>

<script type="module">
import { ref, defineComponent } from 'vue'

export default defineComponent({
  setup() {
    const message = ref('Welcome to music app')
    return {
      message
    }
  }
})
</script>

bootstrap是应用挂载动作,不同的是挂载的目标dom,是作为参数动态传入的,由使用场景决定。bootstrap会暴露给远程应用,用于挂载启动App应用。

react bootstrap.js

js 复制代码
import React from 'react'
import { createRoot } from 'react-dom/client'
import BookApp from './App'

export const mount = ({ rootDom }) => {
  const root = createRoot(rootDom)
  root.render(<BookApp />)
  return () => queueMicrotask(() => root.unmount())
}

vue bootstrap.js

js 复制代码
import { createApp } from 'vue'
import MusicApp from './App.vue'

export const mount = ({ rootDom }) => {
  const root = createApp(MusicApp)
  root.mount(rootDom)
  return () => queueMicrotask(() => root.unmount())
}

export default mount

B、对外暴露远程模块

book-app目录下创建build目录,并创建webpack.config.js文件

启动4002本地服务,并通过ModuleFederationPlugin插件,对外暴露本地模块microfe_book_appbootstrap

js 复制代码
{
    //....
  output: {
    publicPath: 'http://localhost:4002/'
  },
  devServer: {
    port: 4002
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'microfe_book_app',
      filename: 'remoteEntry.js',
      exposes: {
        './bootstrap': './src/bootstrap'
      }
    })
  ]
  //...
}

C、加载远程模块

main-app中同样创建build/webpack.config.js

启动4001本地服务,并通过ModuleFederationPlugin插件,microfe_book_appbookApp

js 复制代码
{
//....
entry: './src/index.js',
  output: {
    publicPath: 'http://localhost:4001/'
  },
  devServer: {
    open: true,
    port: 4001
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'microfe_main_app',
      remotes: {
        bookApp: 'microfe_book_app@http://localhost:4002/remoteEntry.js',
      }
    })
  ]
  //...
}

D、使用远程模块

在main-app/src/subApps/bookApp.js文件中,通过bookApp/bootstrap,访问到microfe_book_app暴露的bootstrap

然后,通过ref挂载到main-app

js 复制代码
import React, { useRef, useEffect } from 'react'
import { mount } from 'bookApp/bootstrap'

export default () => {
  const wrapperRef = useRef(null)
  const isFirstRunRef = useRef(true)
  const isFirstRef = useRef(true)
  const unmountRef = useRef(() => {})

  // Mount book MFE
  useEffect(() => {
    if (isFirstRunRef.current) {
      isFirstRunRef.current = false
      unmountRef.current = mount({
        rootDom: wrapperRef.current
      })
    }
  }, [])

  useEffect(() => {
    if (isFirstRef.current) {
      isFirstRef.current = false
      return unmountRef.current
    }
  }, [])

  return <div ref={wrapperRef} id="book-sub-app" />
}

其他子应用,暴露加载模块过程,基本跟上述一致,这里就不一一展示了

E、定义scripts任务

根目录,package.json中配置scripts task

--parallel忽略并发性和拓扑排序,dev server 默认启动 watch 命令,是会长时间运行监听文件变更,进程不会自动退出(除了报错或者手动退出),因此需要加上 --parallel 告诉 pnpm 运行该脚本时完全忽略并发和拓扑排序

另外,可能会出现启动服务数量将受cpu限制。

json 复制代码
  "scripts": {
    // ....
    "dev": "pnpm --parallel -r run dev",
    "build": "pnpm --parallel -r run build"
  }

apps子应用,package.json中配置scripts task

json 复制代码
"scripts": {
    "dev": "npx webpack serve --mode development --config ./build/webpack.config.js",
    "build": "npx webpack --mode production --config ./build/webpack.config.js"
}

执行下面命令,启动开发服务

shell 复制代码
pnpm dev

六、选择更合适的任务管理器

虽然,pnpm支持批量任务管理,解决了手动执行任务的低效问题。但是同时启动多个开发服务和文件实时监听编译,带来内存和线程管理压力问题,需要借助合适的monorepo工具,才能解决。

目前最流行的Monorepo工具是Lerna、Turborepo和Rush,其中lerna重新更新版本后,需要配合Nx使用。

1、对比选择

  • 性能:turborepo和Rush相当,Lerna+Nx是最好的,大概相当于前面两者的5倍;
  • 功能:Rush功能最完备齐全,其次是Lerna+Nx,但是turborepo发展趋势最迅速,赶超前面两个是迟早的事情
  • 设置:turborepo设置最简单,其次是Lerna+Nx,Rush最复杂
  • 缓存:Rush缓存功能处于实验性阶段,Lerna+Nx远程云缓存收费,turborepo远程云缓存免费,且支持开源搭建自己的远程缓存
  • 学习成本:Rush和Lerna+Nx学习曲线相对陡峭,turborepo上手比较简单

虽然turborepo不是性能最好,也不是功能最完备的monorepo工具。但是turborepo 通过「智能缓存」与「任务调度」,极大的提升了构建速度,节省了计算资源。并且 turbo 配置非常简单,侵入性小,可以渐进式的采用。可以很容易和已有的项目整合。

另外,turborepo已经被Vercel收购,而且从1.7版本开始,从go向rust迁移,其发展潜力是巨大的。功能会逐渐完备,性能也会进一步提升,再加上其无与伦比的生态优势。turborepo迟早会成为monorepo领域,最优异的选择。

2、turborepo优点

工欲善其事必先利其器。下面一起看一下,turborepo都有哪些优点。在此基础上,我们可以更加清晰直观的理解turborepo的设计思路,进而理清楚我们集成和使用turborepo的思路。

  • 增量构建: turborepo 会把每次构建的产物与日志缓存起来,下次构建时只有文件发生变动的部分才会重新构建,没有变动的直接命中缓存并打印输出日志。

  • 感知内容hash: turborepo 将构建产物及日志缓存在 node_modules/.cache/turbo/{hash} 目录下。turbo 基于包内容及依赖等诸多因素生成 hash。再次执行相同命令时 turborepo 会检查缓存中是否有匹配的 hash 值。如果匹配到,则跳过。

  • 并行处理: turborepo 拥有更智能的任务调度程序,充分利用空闲 CPU,使得整体构建速度更快。

    pnpm workspace task

    turbor task

  • 远程缓存: 通常针对于构建时产生的缓存文件,大部分时都会记录在本地硬盘中。但在多人合作首次构建中,仍然需要巨大的耗时构建生成缓存才会提升效率。turborepo 开发团队提供了一项名为"云缓存"的功能,它支持将本地 turborepo 链接到远程缓存从而实现多人合作时共享缓存。

  • 渐进式设计: turborepo 配置非常简单,可以渐进式的采用,很容易和已有的项目整合。安装turbo,根目录配置turbo.json文件,然后即可使用turbo cli。

  • 任务管道: turborepo 支持通过 pipeline 定义任务之间的关系,它会让 turborepo 在构建内容上智能化的分析模块构建串/并执行顺序,从而大大的缩小构建时间。

    json 复制代码
    {
      "$schema": "https://turbo.build/schema.json",
       "pipeline": {
         "topo": {
           "dependsOn": ["^topo"]
         },
         "your-task": {
           "dependsOn": ["topo"]
         }
       }
     }

3、安装turborepo

全局安装

sh 复制代码
pnpm install turbo --global

本地根目录安装

sh 复制代码
pnpm add turbo -wD

4、配置turborepo

根目录下创建turbo.json,输入以下内容,pipeline.<task>.outputs中指定缓存目录。不同于pnpm,task依赖关系是在dependsOn中显式指定的,如果dependsOn为空,表明任何时候都可以执行。turbo依照依赖关系,最大限度的利用cpu,调度执行task。

json 复制代码
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "deploy": {
      "dependsOn": ["build", "test", "lint"]
    },
    "test": {
      "dependsOn": ["build"],
      "inputs": ["src/**/*.jsx", "src/**/*.js", "test/**/*.js", "test/**/*.jsx"]
    },
    "lint": {},
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

5、调整scripts命令

调整根目录package.json中scripts命令

json 复制代码
{
    "dev": "npx turbo run dev",
    "build": "npx turbo run build"
}

启动开发服务

sh 复制代码
pnpm dev

初次编译

yml 复制代码
@microfe/music-app:dev: webpack 5.88.1 compiled successfully in 10567 ms

二次编译

yml 复制代码
@microfe/music-app:dev: webpack 5.88.1 compiled successfully in 520 ms

七、总结

上面我们一起阐述了微前端的概念和优势;进而讨论了微前端项目管理的架构模式,最终选择了monorepo。并且探讨了monorepo的最佳实践,着手搭建了monorepo基础架构。我们把monorepo的微模块,分成远程模块和静态模块。静态模块,本地安装打包;远程模块本身具备独立服务,需要远程加载,异步集成。我们选择module federation实现远程模块的集成。我们还讨论了monorepo本身的痛点,并且集成安装了pnpm和turborepo解决了部分痛点。

下一章,我们将讨论,静态模块的集成和管理。

相关推荐
September_ning3 小时前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人3 小时前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱0013 小时前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js
~甲壳虫5 小时前
说说webpack中常见的Plugin?解决了什么问题?
前端·webpack·node.js
Rattenking5 小时前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
Beamon__5 小时前
element-plus按需引入报错AutoImport is not a function
webpack·element-plus
CodeToGym5 小时前
Webpack性能优化指南:从构建到部署的全方位策略
前端·webpack·性能优化
~甲壳虫5 小时前
说说webpack中常见的Loader?解决了什么问题?
前端·webpack·node.js
~甲壳虫5 小时前
说说webpack proxy工作原理?为什么能解决跨域
前端·webpack·node.js
熊的猫6 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js