# 微前端
# 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')
- 包含生命周期的对象具有
bootstrap
、mount
、unmount
方法
- loading function需要返回一个
- 第三个参数是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
- 该函数只有调用
- 内置props
- 可以设置各个声明周期函数的超时时长
- 在应用之间transition可以参考singlespa-transitions (opens new window),应用内部transition可以参考react-transition-group (opens new window)
- 拆分应用
- 使用
SystemJS
动态加载模块
- 使用
# Concept Parcel
parcel是框架无关的组件。与应用相似,但是需要手动调用函数进行挂载。
如果应用使用了框架,那么更倾向于使用与框架相关的组件,组件之间更易于通信。
# The Recommended Setup
建议使用ES modules + import maps(或者SystemJS
),具有以下好处:
- 公共库易于管理并且只需要下载一次,如果使用
SystemJS
,可以预加载以提升启动速度 - 共享代码、函数、变量更加易于导入导出,和单体应用一样
- 易于懒加载应用,加快应用初始启动
- 每个应用可以独立开发和发布。团队可以并行开发
- 良好的开发体验:在各自的开发环境增加import map,执行本地url
# Alternatives
# In-browser versus build-time modules
in-browser模块指应用中不由打包工具编译的模块;build-time模块指由应用中node_modules
提供并编译的模块。
通过webpack externals
和rollup 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)创建项目,也可以自行按照以下规则配置项目:
- 将输出目标设置为
system
,例如webpack中设置output.libraryTarget
。 - 使用单一入口,通过动态加载拆分应用。
- 不要使用webpack的
optimization
配置选项。 - 遵循the systemjs docs for webpack (opens new window)。
- 使用systemjs-webpack-interop (opens new window)。
- 不要设置webpack的
output.library
。SystemJS需要进行一些额外配置才支持有命名的模块。 - 可以考虑关闭入口文件和懒加载文件的webpack hashing。通过CI发布时添加commit hash。
- 关闭webpack-dev-server的host checks。
- 设置webpack-dev-server的CORS头:
'Access-Control-Allow-Origin': '*'
。 - 如果在https上开发,需要配置webpack-dev-server与https相关选项。configure webpack-dev-server for HTTPS (opens new window)、trusting SSL certificates from localhost (opens new window)。
- 配置webpack externals分离共享模块。
- webpack dev server设置
sockPort
、sockPath
、sockHost
。https协议热更新 (opens new window)。
# Utility modules(styleguide, API, etc)
# Shared dependencies
对于大型库有必要作为共享依赖,而小型库如react-router可以允许在项目中重复。
两种共享依赖的方法:
- in-browser modules with import maps
- 需要配置external,并将依赖放入import-maps。
- 引入一个
shared-dependencies
的仓库进行管理,配合CI更新import-map。
- build-time modules with moudle federation
- example repo (opens new window)使用了systemjs加载微前端,使用module federation共享react、react-dom、react-router。
- talk about systemjs and federation (opens new window)。
# 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实例。
- 可以让CI通过
# Application versus parcels versus utility modules
# Inter-app communication
三种需要共享的信息:
- 函数、组件、逻辑和环境变量
- 通过导入相同的工具库实现
- API数据
- 将需要共享的接口提取成公共库,在每个子应用中导入并调用。可以设置接口缓存,也可以通过存储在前端内存中共享。
- UI状态
- 使用rxjs。
- custom events。
- 其他的pub/sub事件发射系统。
# State management
# 阅读链接
← 函数式编程 基于Plop快速生成模板文件 →