什么是微前端

微前端是一种由独立交付的多个前端应用组成整体的架构风格,将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的小块,而在用户看来仍然是内聚的单个产品,如果你了解过后端的微服务,那么理解起微前端来更加容易。

image-20210606085830286

传统的SPA页面会一次性将所有的模块全部加载,而微前端不同,它会在路由命中后动态的加载某一模块。

为什么要用微前端

如果你的web应用只有几个简单的页面组成,那么你当然不需要微前端架构,但是如果你的应用是一个大型应用,你可能会需要微前端。在使用微前端之前,你的大型应用可能有成百个页面,上千个组件,而且不同的业务线都是在同一个项目中,不论是维护(分支容易混乱)还是发布之后的性能(首屏加载,SPA需要一次将所有的内容全部加载完成),都会存在问题。

像我实习所在的公司,他们只维护一个平台,但这个平台涉及到了多个领域的复杂业务,如果使用普通SPA将会非常不方便,好在他们在我去之前已经升级到了微前端架构,使用的也是想在的主流方案single-spa。

使用微前端之后,不同业务线的开发者只需要维护自己的代码,不会像以前一样所有的人都在同一个git中提交,一不小心容易出错。每个应用独立部署,某一条业务线出现问题也不会影响其他业务线。

怎么使用微前端

目前微前端方向主要有两大框架single-spaqiankun,qiankun是基于single-spa的,个人认为qiankun最大的优势是解决了样式冲突

微前端子应用可以独立构建,与容器完全解耦,技术栈可以是Vue、React、Angular……但是并不是完全没有关系,子应用在打包时必须导出bootstrap、mount和unmount方法用于加载、挂载以及卸载子应用

还需要注意到的一个问题是各个子应用之间的通信,不同模块的通信是不可避免的,我们有以下方案来实现应用之间的通信

  • 使用URL传递传递数据
  • 使用CustomEvent传递数据
  • 基于props进行容器和子应用之间的通信
  • 使用Redux、全局变量进行通信

下面我们就以single-spa为例来说一下微前端的搭建过程

创建single-sap项目

创建single-spa时有两种方式,一种是全局安装到本地,然后使用CSS(create-single-spa)命令创建,另一种是直接使用npm初始化项目

# 全局安装
npm install -g cerate-single-spa
create-single-spa
# npm安装
npm init single-spa

通过这两种方式创建之后会进入选择创建那种项目

image-20210607135133809

或者可以通过指令来指定创建项目类型

npm init single-spa --framework vue #使用框架类型
npm init single-spa --dir my-dir #创建项目文件夹
npm init single-spa --moduleType app-parcel #子应用,root-config是基座

子应用的创建过程与容器稍有不同,会让你选择使用的框架,然后会调用vue-cli生成项目或者下载react的示例代码

image-20210607135610921

等所有的依赖安装完之后启动服务(基座和子应用),基座默认的端口是在9000,打开浏览器访问,可以看到内容如下

image-20210607151417445

更改配置

然后修改基座src下的这两个文件

image-20210607151511041

默认的配置文件给出了新增子应用的配置示例,根据提示修改即可

import { registerApplication, start } from "single-spa";

registerApplication({
  name: "@single-spa/welcome", // 应用名称
  app: () =>
    System.import(
      "https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js"
    ), // 导入的模块
  activeWhen: ["/"], // 匹配的路由
});

registerApplication({
  name: "@feng/micro-app1",
  app: () => System.import("@feng/micro-app1"),
  activeWhen: ["/app1"]
});

start({
  urlRerouteOnly: true,
});

然后在ejs文件中找到下面这段代码,根据实际情况做出修改

<% if (isLocal) { %>
<script type="systemjs-importmap">
    {
      "imports": {
        "@feng/root-config": "//localhost:9000/feng-root-config.js",
        "@feng/micro-app1": "//localhost:8080/js/app.js",
        "@feng/micro-app2": "//localhost:8500/js/app.js"
      }
    }
</script>
<% } %>

如果不确定app.js是哪个文件,可以直接访问子应用所在的端口,会有提示

image-20210607153626183

配置基本完成,然后到浏览器中去看一下效果,此时应该是会存在一个问题的

image-20210607154017454

在匹配/app1的时候会匹配到跟路由/,这时要将配置文件中的根目录修改为如下

registerApplication(
  "@single-spa/welcome",
  () =>
    System.import(
      "https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js"
    ),
  (location) => location.pathname === "/"
);

如果你的子应用有react应用,需要在ejs中添加以下代码(在刚才添加子应用地址的上边),可以看下github的issues

<script type="systemjs-importmap">
  {
    "imports": {
      "single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.9.0/lib/system/single-spa.min.js",
      "react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js",
      "react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js"
    }
  }
</script>

现在的效果如下

image-20210607160004414

齐活,当然single-spa中还有很多坑,需要慢慢踩


前端小白