# 微前端

# Single-SPA

# Concept Microfrontend

  • 每个微前端应该有自己的git仓库,有独立的构建流程,例如CI/CD。
  • 和微服务相比:
    • 每个微服务可能涉及数据库存取,其他服务调用等,相互之间是网络请求;而微前端之间的通信则是基于内存的。
    • 相同的是都具有独立构建和发布流程。
  • 微前端的类型:
    • application
    • pacel
    • utility
  • single-spa的角色
    • 负责编排微前端
  • 性能
    • 内置懒加载方法和其他懒加载方法

# Concept Root Config

  • root config包含
    • 应用共享的root html文件
    • 调用了singleSpa.registerApplication()的js文件
  • 并不一定要使用SystemJS,但是它可以帮助你独立发布应用
  • registerApplication参数
    • 第一个参数是application name
    • 第二个参数是loading function或者包含应用生命周期的对象
      • loading function需要返回一个Promise,该Promise实例需要resolve一个应用,例如:() => import('path/to/application.js')
      • 包含生命周期的对象具有bootstrapmountunmount方法
    • 第三个参数是activity function
      • 该函数接受window.location作为参数,并返回一个布尔值
    • 第四个参数是custom props
      • custom props会作为参数传递给子应用的生命周期函数
    • 也可以传入一个对象
      • name: string
      • app: function
      • activeWhen: string
      • customProps: object
  • start
    • start方法可以帮助更好控制何时显示应用,例如等待必须的Ajax请求完成后仔触发应用的挂载。

# Concept Application

  • 生命周期props
    • 内置props
      • name:应用名称
      • singleSpa:singleSpa实例,允许子应用或工具库直接调用singleSpa的api而无需import。
      • moutParcel
    • 自定义props
      • 一些用例:
        • 子应用共享access token
        • 传递初始化信息,例如挂载目标
        • 传递事件总线的引用,使得应用相互直接得以通信
    • Load
    • Bootstrap
      • 只会在应用第一次挂载时调用
    • Mount
      • 应用挂载时调用。应用子路由发生变化时不会调用,其变化交由应用本身处理
    • Unmount
      • 应用卸载时调用,该方法中可以清除副作用,例如DOM事件监听等
    • Unload
      • 该函数只有调用unloadApplication时才会调用。
      • 该生命周期的目的是hot-reloading
  • 可以设置各个声明周期函数的超时时长
  • 在应用之间transition可以参考singlespa-transitions (opens new window),应用内部transition可以参考react-transition-group (opens new window)
  • 拆分应用
    • 使用SystemJS动态加载模块

# Concept Parcel

parcel是框架无关的组件。与应用相似,但是需要手动调用函数进行挂载。

如果应用使用了框架,那么更倾向于使用与框架相关的组件,组件之间更易于通信。

建议使用ES modules + import maps(或者SystemJS),具有以下好处:

  • 公共库易于管理并且只需要下载一次,如果使用SystemJS,可以预加载以提升启动速度
  • 共享代码、函数、变量更加易于导入导出,和单体应用一样
  • 易于懒加载应用,加快应用初始启动
  • 每个应用可以独立开发和发布。团队可以并行开发
  • 良好的开发体验:在各自的开发环境增加import map,执行本地url

# Alternatives

# In-browser versus build-time modules

in-browser模块指应用中不由打包工具编译的模块;build-time模块指由应用中node_modules提供并编译的模块。

通过webpack externalsrollup externals将应用中部分依赖外置。

  • 每个spa应用应该是一个in-browser模块
  • 大型共享依赖,如react、vue应该是一个in-browser模块
  • 其他的应该是build-time模块

# Import Maps

Import Maps (opens new window)用于查找import specifier对应的URL。

// ./thing.js is the import specifier
import thing from './thing.js';
// react is the import specifier
import React from 'react';

上述react是一个bare specifier,其不是一个相对路径或者完整URL,import maps可以解决这个问题,找到其对应的URL。

# Module Federation

Module Federation (opens new window)是webpack用于共享build-time module的方法。

YouTube video (opens new window)讲述了在single-spa中使用module federation。

建议第三方依赖采用import map,其他模块采用module federation。

# SystemJS

使用SystemJS (opens new window)需要将webpack的output.libraryTarget配置为system

相比于es-module-shims (opens new window),systemjs性能要更好。

# Lazy loading

需要动态设置public path使得代码分割正常工作。可以使用systemjs-webpack-interop (opens new window)进行设置。

# Local development

Tutorial video (opens new window)

本地开发时应当只启动当前开发的子应用。

import-map-overrides (opens new window)可以在浏览器UI上自定义import-maps。

# Build tools(Webpack/Rollup)

可以使用create-single-spa (opens new window)创建项目,也可以自行按照以下规则配置项目:

# Utility modules(styleguide, API, etc)

# Shared dependencies

对于大型库有必要作为共享依赖,而小型库如react-router可以允许在项目中重复。

两种共享依赖的方法:

# Deployment and Continuous Integration(CI)

Tutorial video Part 1 (opens new window)

Tutorial video Part 2 (opens new window)

Example CI configuration files (opens new window)

发布微前端的两个步骤:

  • 将JS打包文件上传至服务器或者CDN。
  • 更新import map文件,资源指向最新的URL。可以通过以下两种方式:
    • 可以让CI通过curl发送HTTP请求至import-map-deployer (opens new window)实例。
      • 这种方式是并行安全的,即多个CI同时发送HTTP请求不会造成竞态的问题。
    • 可以让CI拉去import map文件并且修改其内容后重新上传。
      • 这种方式好处在于无需在服务器运行一个import-map-deployer实例。

# Application versus parcels versus utility modules

# Inter-app communication

三种需要共享的信息:

  • 函数、组件、逻辑和环境变量
    • 通过导入相同的工具库实现
  • API数据
    • 将需要共享的接口提取成公共库,在每个子应用中导入并调用。可以设置接口缓存,也可以通过存储在前端内存中共享。
  • UI状态
    • 使用rxjs。
    • custom events。
    • 其他的pub/sub事件发射系统。

# State management

# 阅读链接