你了解微前端架构吗?如何搭建

874 阅读6分钟

微前端架构

什么是微前端

  • 微前端是一种软件架构,可以将前端应用拆解成一些更小的能够独立开发部署的微型应用,然后再将这些微应用进行组合使其成为整体应用的架构模式。
  • 微前端架构类似于组件架构,但不同的是,组件不能独立构建和发布,但是微前端中的应用是可以的。
  • 微前端架构与框架无关,每个微应用都可以使用不同的框架

微前端的价值

  1. 增量迁移
    迁移是一项非常耗时且艰难的任务,比如有一个管理系统使用AngularS开发维护已经有三年时间,但是随时间的推移和团队成员的变更,无论从开发成本还是用人需求上,AngularS已经不能满足要求,于是团队想要更新技术栈,想在其他框架中实现新的需求,但是现有项目怎么办?直接迁移是不可能的,在新的框架中完全重写也不太现实。
    使用微前端架构就可以解决问题,在保留原有项目的同时,可以完全使用新的框架开发新的需求,然后再使用微前端架构将旧的项目和新的项目进行整合。这样既可以使产品得到更好的用户体验,也可以使团队成员在技术上得到进步,产品开发成本也降到的最低。
  2. 独立发布
    在目前的单页应用架构中,使用组件构建用户界面,应用中的每个组件或功能开发完成或者bug修复完成后,每次都需要对整个产品重新进行构建和发布,任务耗时操作上也比较繁琐。
    在使用了微前端架构后,可以将不能的功能模块拆分成独立的应用,此时功能模块就可以单独构建单独发布
    了,构建时间也会变得非常快,应用发布后不需要更改其他内容应用就会自动更新,这意味着你可以进行频繁的构建发布操作了。
  3. 允许单个团队做出技术决策
    因为微前端构架与框架无关,当一个应用由多个团队进行开发时,每个团队都可以使用自己擅长的技术栈进行开发,也就是它允许适当的让团队决策使用哪种技术,从而使团队协作变得不再僵硬。

微前端的使用场景

  • 拆分巨型应用,使应用变得更加可维护
  • 兼容历史应用,实现增量开发

如何实现微前端

  1. 多个微应用如何进行组合?
    在微前端架构中,除了存在多个微应用以外,还存在一个容器应用,每个微应用都需要被注册到容器应用中。
    微前端中的每个应用在浏览器中都是一个独立的JavaScript模块,通过模块化的方式被容器应用启动和运行。
    使用模块化的方式运行应用可以防止不同的微应用在同时运行时发生冲突。
  2. 在微应用中如何实现路由?
    在微前端架构中,当路由发生变化时,容器应用首先会拦截路由的变化,根据路由匹配微前端应用,当匹配到微应用以后,再启动微应用路由,匹配具体的页面组件。
  3. 微应用与微应用之间如何实现状态共享?
    在微应用中可以通过发布订阅模式实现状态共享,比如使用RxjS库。
  4. 微应用与微应用之间如何实现框架和库的共享?
    通过import-map(允许加载网络模块)和webpack中的externals属性。

Systemjs

  • 在微前端架构中,微应用会被打包为模块,但浏览器环境是不太支持模块化的,此时就需要使用systemjs实现浏览器中的模块化。
  • systemjs是一个用于实现模块化的JavaScript库,有属于自己的模块化规范。
  • 我们可以使用ES模块规范,使用babel插件将其转换为systemjs支持的模块规范。

案例:通过webpack将react应用打包为systemjs模块,在通过systemjs在浏览器中加载模块

single-spa

  1. 概述
    single-spa是一个实现微前端架构的框架。
    在single-spa框架中有三种类型的微前端应用:
  • single-spa-application/parcel:微前端架构中的微应用,可以使用vue react angular等框架。
  • single-sparootconfig:创建微前端容器应用。
  • utilitymodules:公共模块应用,非渲染组件,用于跨应用共享javascript逻辑的微应用。
  1. 创建容器应用
  • npm install create-single-spa@2.0.3
  • npx create-single-spa
  • npm start
  1. 创建微应用
  • 创建微应用目录,初始化项目
  • 创建webpack.config.js配置
const singleSpaDefaults = require('webpack-config-single-spa');
const { merge } = require('webpack-merge');

module.exports = () => {
    const defaultConfig = singleSpaDefaults({
        orgName: 'tian', 
        projectName: 'wei'
    })
    return merge(defaultConfig, {
        devServer: {
            port: 9001,
        }
    })
}
  • 创建入口文件tian-wei.js
let container = null;

exports async function bootstrap() {
    console.log('启动')
}

exports async function mount() {
    console.log('挂载')
    container = document.createElement('div');
    container.id = 'container';
    container.innerHTML = 'hi tian';
    document.body.appendChild(container);
}

exports async function unmount() {
    console.log('卸载')
    document.body.removeChild(container);
}
  • 设置启动命令
"script": {
    "start": "webpack serve"
}
  • 在容器应用文件tian-root-config中注册微应用
registerApplication({
    name: '@tian/wei',
    app: () => System.import('@tian/wei'),
    activeWhen: ['/lagou']
})
  • 在容器应用文件index.ejs中配置模块地址
{
      "imports": {
        "single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.9.0/lib/system/single-spa.min.js",
        "@tian/wei":"//localhost:9001/tian"
      }
    }
  1. 创建基于react的微应用
  • npx create-single-spa
  • 设置启动命令 "start": "webpack serve --port 9002",
  • 注册到容器应用、配置地址、添加react、react-dom
  1. 创建公共UI:Parcer
  • 作用

  • Parcel 用来创建公共 UI,涉及到跨框架共享 UI 时需要使用 Parcel。

  • Parcel 的定义可以使用任何 single-spa 支持的框架,它也是单独的应用,需要单独启动,但是它不关联路由。

  • Parcel应用的模块访问地址也需要被添加到import-map 中,其他微应用通过 System.import 方法进行引用。

  • 使用

  • 使用npx create-single-spa创建

  • 在自己的root.component.js中进行配置

import React from "react";
import { BrowserRouter, Link } from "react-router-dom";

export default function Root(props) {
  return (
    <BrowserRouter>
      <div>
        <Link to="/">首页</Link>
        <Link to="/react">react</Link>
      </div>
    </BrowserRouter>
  )
}
  • 在react的tian-react.js中进行引用
<Parcel config={System.import("@tian/wei")} />
  1. 跨组件共享的js
  • 创建应用
  • 定义方法
export function sayHi(who){
    console.log(who);
}
  • 在react中使用
// Home.js
import React, { useState, useEffect } from 'react'

function useTool () {
    const [toolModel, setToolModel] = useState();
    useEffect(() => {
        System.import("@tian/tools").then(setToolModel)
    }, [])
    return toolModel
}

const Hpme = () => {
    const toolModel = useTool()
    if(toolModel) {
        toolModel.sayHi('tt')
    }
    return <div>react</div>
}

export default Home;
  1. 跨应用通信-RxJS状态管理
  • 在容器应用文件index.ejs中配置地址
  • 编写状态代码
import { ReplaSubject } from 'rxjs';
export const share = new ReplaSubject();
  • 在react中使用
useEffect(() => {
    let sub = null;
    if(toolModel){
        sub = toolModel.share.subscribe(console.log);
    }
    return () => sub.unsubscribe()
})
  1. 实现布局引擎
  • 允许使用组件的方式声明顶层路由,并且提供了更加便捷的路由API用来注册应用。

  • 下载引擎

  • 在容器文件构建路由

  • 获取路由信息,注册应用

模块联邦

  • 是webpack5中的新增功能,可以实现跨应用共享模块