likes
comments
collection
share

初识微前端,来让我看看它是如何实现的?

作者站长头像
站长
· 阅读数 12

什么是微前端

微前端是一种软件架构,可以将前端应用拆解成一些更小的能够独立开发部署的微型应用,然后再将这些微应用进行组合使其成为整体应用的架构模式。

微前端架构类似于组件架构,但不同的是,组件不能独立构建和发布,但是微前端中的应用是可以的。 微前端架构与框架无关,每个微应用都可以使用不同的框架。

微前端的价值

增量迁移

迁移是一项非常耗时且艰难的任务,比如有一个管理系统使用 AngularJS 开发维护已经有三年时间,但是随时间的推移和团队成员的变更,无论从开发成本还是用人需求上,AngularJS 已经不能满足要求,于是团队想要更新技术栈,想在其他框架中实现新的需求,但是现有项目怎么办?直接迁移是不可能的,在新的框架中完全重写也不太现实。

使用微前端架构就可以解决问题,在保留原有项目的同时,可以完全使用新的框架开发新的需求, 然后再使用微前端架构将旧的项目和新的项目进行整合。这样既可以使产品得到更好的用户体验, 也可以使团队成员在技术上得到进步,产品开发成本也降到的最低。

独立发布

在目前的单页应用架构中,使用组件构建用户界面,应用中的每个组件或功能开发完成或者bug修 复完成后,每次都需要对整个产品重新进行构建和发布,任务耗时操作上也比较繁琐。

在使用了微前端架构后,可以将不同的功能模块拆分成独立的应用,此时功能模块就可以单独构建 单独发布了,构建时间也会变得非常快,应用发布后不需要更改其他内容应用就会自动更新,这意 味着你可以进行频繁的构建发布操作了。

允许单个团队做出技术决策

因为微前端构架与框架无关,当一个应用由多个团队进行开发时,每个团队都可以使用自己擅长的 技术栈进行开发,也就是它允许适当的让团队决策使用哪种技术,从而使团队协作变得不再僵硬。

importmap

微前端的使用场景: 1. 拆分巨型应用,使应用变得更加可维护 2. 兼容历史应用,实现增量开发

chatgpt的回答

微前端是一种架构模式,旨在将一个大型前端应用程序拆分为多个小型、独立的部分,每个部分都可以独立开发、测试、部署和扩展,从而提高整个应用程序的可维护性和可扩展性。 微前端的主要价值在于以下几点:

  • 可维护性:微前端可以让团队更加容易地维护和修改前端应用程序,因为它可以让每个团队负责自己的一部分,从而缩小了项目范围。
  • 可扩展性:微前端可以让前端应用程序更加容易地扩展和适应变化的需求,因为它可以让团队轻松地将新功能添加到应用程序中,而不需要对整个应用程序进行大规模修改。
  • 独立性:微前端可以让团队根据不同的需求和进度独立开发和部署各自的应用程序部分,从而更好地控制开发和发布时间。
  • 可复用性:微前端可以让不同的应用程序部分更加易于重用和共享,因为它们都是独立的。 技术栈灵活性:微前端可以让团队在应用程序中使用不同的技术栈,因为它们都是独立的部分,可以根据需要选择最适合的技术栈。

综上所述,微前端可以帮助团队更好地管理和维护前端应用程序,同时提供更好的可扩展性、独立性、可复用性和技术栈灵活性。

如何实现微前端

多个微应用如何进行组合?

在微前端架构中,除了存在多个微应用以外,还存在一个容器应用,每个微应用都需要被注册到容器应用中。 微前端中的每个应用在浏览器中都是一个独立的 JavaScript 模块,通过模块化的方式被容器应用启动和运行。 使用模块化的方式运行应用可以防止不同的微应用在同时运行时发生冲突。

在微应用中如何实现路由?

在微前端架构中,当路由发生变化时,容器应用首先会拦截路由的变化,根据路由匹配微前端应用,当匹配到微应用以后,再启动微应用路由,匹配具体的页面组件。

微应用与微应用之间如何实现状态共享?

在微应用中可以通过发布订阅模式实现状态共享,比如使用 RxJS。

微应用与微应用之间如何实现框架和库的共享?

通过 import-map 和 webpack 中的 externals 属性。

chatgpt的回答

实现微前端架构需要考虑以下几个方面:

  • 拆分应用:将大型前端应用程序拆分成多个小型独立的应用程序或模块,可以使用不同的技术栈实现,例如 Angular、React、Vue 等。
  • 定义接口:定义应用程序之间的接口和通信协议,可以使用标准化技术,例如 RESTful API、GraphQL 或 WebSocket。
  • 注册中心:提供应用程序注册中心,用于注册、发现和加载应用程序。
  • 集成框架:提供一个集成框架,用于将各个独立的应用程序整合到一个完整的应用程序中,可以使用框架,例如 Single-SPA、qiankun 等。

实现应用程序的开发、测试、集成和部署,确保每个应用程序都可以独立部署,并正确地加载到应用程序注册中心中。

协调不同的应用程序之间的通信和数据流,确保整个应用程序能够协同工作。

需要注意的是,微前端架构需要考虑到通信和数据共享的复杂性,需要在设计和开发前对其进行仔细的评估和规划。

微前端框架

single-spa

在 single-spa 框架中有三种类型的微前端应用:

  • single-spa-application(为一组特定路由渲染组件的微前端) / parcel(不受路由控制,渲染组件的微前端):微前端架构中的微应用,可以使用 vue、react、angular 等框架。

  • single-spa root config:创建微前端容器应用。

  • utility modules:公共模块应用,非渲染组件,用于跨应用共享 javascript 逻辑的微应用。

创建一个主应用容器用于注册微应用,其他微应用只需要在入口文件中暴露出bootstrap,mount,unmount钩子即可。

application 用于创建基于路由的微应用,用于在主应用容器中注册。

Parcel 只有在涉及到跨框架的应用之间进行组件调用时,我们才需要考虑parcel的使用。 Parcel 的定义可以使用任何 single-spa 支持的框架,它也是单独的应用,需要单独启动,但是它不关联路由。

utility modules 用于放置跨应用共享的 JavaScript 逻辑,它也是独立的应用,需要单独构建单独启动。

Layout Engine 允许使用组件的方式声明顶层路由,并且提供了更加便捷的路由API用来注册应用。来代替多次使用registerApplication注册微应用。

这里提供一个案例

模块联邦

Module Federation 即为模块联邦,是 Webpack 5 中新增的一项功能,可以实现跨应用共享模块。

chatgpt的回答 什么是模块联邦?

webpack中的模块联邦(Module Federation)是一项新功能,旨在帮助实现多个独立的webpack构建之间的代码共享与协作。它使得在不同的Webpack构建之间共享模块变得非常容易,而无需使用外部的包管理器或构建工具。

通过使用模块联邦,您可以将多个独立的webpack构建之间的代码共享。这意味着,您可以在项目中的不同部分或在不同项目之间共享代码。与传统的打包方式不同,模块联邦允许您按需加载您需要的代码,从而提高应用程序的性能。

在模块联邦中,每个Webpack构建都可以成为一个“提供者”(Provider)或“消费者”(Consumer)。提供者可以将其代码标记为可共享,并将其公开注册为可导出的模块。消费者可以按需异步加载这些模块,而无需将它们自己打包到其Webpack构建中。

模块联邦还允许您控制加载和远程执行脚本的行为。例如,您可以使用远程执行脚本来异步加载可共享代码,或在提供者上控制模块的版本和确定性。

总之,模块联邦是一个强大的Webpack功能,可大大简化复杂项目的代码共享。它是现代Web开发的众多工具和技术之一,可以使开发人员更轻松地协作和共享代码。

组合微应用?

通过一个容器应用使用模块联邦。

     new ModuleFederationPlugin({
      name: "container",
      // 配置导入模块映射
      remotes: {
        // 字符串 "products" 和被导入模块的 name 属性值对应
        // 属性 products 是映射别名, 是在当前应用中导入该模块时使用的名字
        products: "products@http://localhost:8081/remoteEntry.js",
        cart: "cart@http://localhost:8082/remoteEntry.js"
      }
    }),

如何实现库共享

我们需要将微应用中共同用到的库共享,防止容器加载微应用时加载多次相同的库。

    // 将 products 自身当做模块暴露出去
    new ModuleFederationPlugin({
      // 模块文件名称, 其他应用引入当前模块时需要加载的文件的名字
      filename: "remoteEntry.js",
      // 模块名称, 具有唯一性, 相当于 single-spa 中的组织名称
      name: "products",
      // 当前模块具体导出的内容
      exposes: {
        "./Index": "./src/bootstrap"
      },
      // 加载共享模块,需要被异步加载
      shared: {
        faker: {
          singleton: true
        }
      }
    }),

如何实现微前端路由

容器应用路由用于匹配微应用,微应用路由用于匹配组件。

我们就是通过容器项目使用的库提供的路由来进行匹配微应用。

容器应用使用 BrowserHistory 路由,微应用使用 MemoryHistory 路由。

  • 为防止容器应用和微应用同时操作 url 而产生冲突,在微前端架构中,只允许容器应用更新 url, 微应用不允许更新 url,MemoryHistory 是基于内存的路由,不会改变浏览器地址栏中的 url。
  • 如果不同的应用程序需要传达有关路由的相关信息,应该尽可能的使用同样的方式, memoryHistory 在 React 和 Vue 中都有提供。

微应用和容器应用路由沟通

  • 微应用路由变化时 url 地址没有被同步到浏览器的地址栏中,路由变化也没有被同步到浏览器的历史记录中。 当微应用路由发生变化时通知容器应用更新路由信息 (容器应用向微应用传递方法) 即页面正常跳转,url正确显示
// Container/components/MarketingApp.js
import { useHistory } from "react-router-dom"
const history = useHistory()
mount(ref.current, {
  onNavigate({ pathname: nextPathname }) {
    const { pathname } = history.location
    // 微应用监听路有变化。然后将变化的路由加入到历史记录中
    if (pathname !== nextPathname) {
      history.push(nextPathname)
   }
 }
})

微应用监听,并将其当前的路由信息传递到容器应用

// Marketing/bootstrap.js 
function mount(el, { onNavigate }) { 
    // 微应用监听,并将其当前的路由信息传递到容器应用
    if (onNavigate) history.listen(onNavigate) 
}
  • 容器应用路由发生变化时只能匹配到微应用,微应用路由并不会响应容器应用路由的变化。 当容器应用路由发生变化时需要通知微应用路由进行响应 (微应用向容器应用传递方法) 即浏览器历史记录正确跳转和切换容器应用路由时微应用正确匹配
// Marketing/bootstrap.js
function mount(el, { onNavigate }) {
  // 微应用挂在函数返回路由比较函数
  return {
    onParentNavigate({ pathname: nextPathname }) {
      const { pathname } = history.location
      if (pathname !== nextPathname) {
        history.push(nextPathname)
     }
   }
 }
}

当容器路由发生变化,将执行微应用路由匹配

    // Container/components/MarketingApp.js
    const { onParentNavigate } = mount()
    // 当容器路由发生变化,将执行微应用路由匹配
    if (onParentNavigate) {
      history.listen(onParentNavigate)
    }

模块联邦基础案例

基于模块联邦构建微服务