从零开始打造基于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 (1).png

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

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

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

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

三、Monorepo应用架构模型

1、monorepo概念

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

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

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

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

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

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

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

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

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

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

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

581527d3f5c2226c40a85ff6d4bbe40f.png

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

mkdir microfe && cd microfe

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

mkdir apps packages

创建微应用目录

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工具库

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

# apps/main-app
|--src


    |--index.js


    |--App.js
    |--subApps

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

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

react应用

# apps/movie-app app/book-app
|--src


    |--index.js


    |--bootstarp.js

    |--App.vue

Vue应用

# app/music-app app/tv-app
|--src


    |--index.js


    |--bootstarp.js

    |--App.vue

目录结构

microfe (3).png

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

npm install -g pnpm

或者

npm install -g @pnpm/exe

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

brew install pnpm

C、配置pnpm workspace

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

touch pnpm-workspace.yaml

输入配置

packages:
  # 放置共用模块与应用
  - 'packages/*'
  # 放置微应用
  - 'apps/*'


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

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

pnpm init

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

{





  "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

cd apps/main-app
pnpm init

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

{





  "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

pnpm add only-allow -wD
{





    scripts:{
        "preinstall": "npx only-allow pnpm",
    }
}

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

yarn add lodash
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规范。必须明确指明,安装依赖的位置目录,否则会出现异常警告:

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的命令参数最主要是以下几个:

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

根目录下管理依赖

pnpm add xxx -w
pnpm update xxx -w
pnpm remove xxx -w

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

pnpm add xxx -r
pnpm update xxx -r
pnpm remove xxx -r

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

pnpm add xxx -F ./apps
pnpm update xxx -F ./apps
pnpm remove xxx -F ./apps

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

pnpm add xxx -F @microfe/*
pnpm update xxx -F @microfe/*
pnpm remove xxx -F @microfe/*

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

pnpm run dev -r
pnpm -F @microfe/* rum lint

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

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

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

1、安装依赖

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

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相关配置,同样安装在根目录

pnpm add @babel/core @babel/preset-env @babel/preset-react @babel/plugin-transform-runtime -wD




安装react

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

安装vue

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

import React from 'react'





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


export default BookApp

App.vue

<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

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

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

{

    //....
  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

{

//....
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

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限制。

  "scripts": {
    // ....
    "dev": "pnpm --parallel -r run dev",
    "build": "pnpm --parallel -r run build"
  }

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

"scripts": {
    "dev": "npx webpack serve --mode development --config ./build/webpack.config.js",
    "build": "npx webpack --mode production --config ./build/webpack.config.js"
}

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

pnpm dev

1690381515206.jpg1690381591109.jpg

1690383948276.jpg

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

虽然,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

    yarn-workspaces-excalidraw.webp

    turbor task

    turborepo-excalidraw.webp

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

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

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

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

3、安装turborepo

全局安装

pnpm install turbo --global

本地根目录安装

pnpm add turbo -wD

4、配置turborepo

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

{





  "$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命令

{





    "dev": "npx turbo run dev",
    "build": "npx turbo run build"
}

启动开发服务

pnpm dev

1690384990378.jpg1690385129923.jpg

1690383948276.jpg

初次编译

@microfe/music-app:dev: webpack 5.88.1 compiled successfully in 10567 ms

二次编译

@microfe/music-app:dev: webpack 5.88.1 compiled successfully in 520 ms

七、总结

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

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

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYPTIFy8' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片