---
title: 谈谈微前端
createTime: 2019/08/31 05:13:33
author: pengzhanbo
permalink: /article/vpqgx0t7/
---

微前端 是最近比较新兴的一个话题，它不具体指某个库某个框架，而是一个思想，一种概念，运用这种思想，
根据自身的需求，从而实现适用于自身的 微前端 。

<!-- more -->

> 本文根据最近我在公司内部举行的 微前端技术解决方案 分享而写。
> 提供的 微前端方案 也应用于公司内部的项目，并取得了良好的反馈，获得广泛好评。
> 本文不具体谈如何实现微前端，仅讲述微前端的概念，期望能够通过本文理解微前端。

## 前言

微前端 目前在行业内是一个新兴的思想。

诞生这个思想的背景是，在公司内部，常常会有一类项目，这类项目很大、很重，
涉及的业务内容多而杂，还涉及了跨部门共同维护，积累的庞大的技术债 等各种问题。
这类项目在维护成本上、部署成本上等，都会花费巨大的开销，前端开发人员对于维护这类项目，苦不堪言，
急需找到解决这类问题的方案。

基于这样的背景下，开始探讨 解决方案的可行性， 微前端 正是基于此 开始慢慢 出现在人们的视野中。

## 现状

### 发展历程

在 Web 的发展初期，还没有所谓的前端的概念，网页的内容也相对简单，大多仅涉及文字图片信息的展示和表单内容，
这些工作可能网站负责人自己就包办了。
然后微软推出了 **Ajax** 技术，引起了网页的技术变革，从此网站开始具备了动态交互性，
能够在网页发起请求动态获取服务器的内容，这丰富了网页的可交互性，网页的开发也从UI界面和表单交互，进一步增加了
数据和逻辑的开发，前端也慢慢的被划分一个相对独立的职能。

而伴随着 nodejs 的出现，以及 angular 的出现，还包括 vue/react 等库，以及建立在 nodejs 上的生态，
包括grunt、gulp、webpack 等工具的诞生，前端进入了一个喷井式爆发的时期，也是我们所处的时期。前端越来越专业化，
包含的技术内容越来越丰富，依托于nodejs 以及众多的技术框架等，向着工程化进一步的发展，前端项目也越来越大。

### 浮现的问题

你是否维护过一个可能有着四五年以上历史的项目？是否维护过一个糅杂了各种各样的库的项目？
是否维护过一个多个公司部门参与的跨团队联合开发的项目？

对于很多人来说，入职的某个公司，最怕被安排去维护一个这样的项目。因为每一次维护迭代，就如同开盲盒一样，
永远不知道有什么惊喜在等着自己。

对于这类项目，可能存在的问题包括：

- 跨部门，夸团队联合开发，如何沟通？如何协作？
- 业务线多，页面多，如何管理？
- 如何进行代码管理？
- 如何进行分支管理？
- 多部门进行迭代，如何把控版本？
- 存在发布冲突，如何解决？
- 如何进行测试？
- 如何管理公共代码？
- ...

可能改动某一行代码，都会带来意想不到的结果，种种问题的积累，技术债的、业务债的，使得项目越来越臃肿，越来越难以维护。

亟需寻找一种方案，能够解决这些问题。

### iframe嵌入

于是，在大多数时候，我们不得不去选择通过 iframe嵌入 的方式，先把臃肿的项目一点一点的拆开给回各个部门或者团队自行维护，
再提供一个 系统门户应用，用 iframe嵌入 的方式，加上维护一个菜单列表与其他项目入口链接的映射，来糅合成一个 网站应用。

![iframe嵌入](//assets.processon.com/chart_image/630f92445653bb0c5d1040ab.png)

通过 iframe嵌入，在一定程度上，满足了 各部门各团队各业务线 独立开发独立部署的需求，只需要提供对应的页面链接就可以接入到
整个系统中。但也存在着一些问题

**安全问题**

然而，我们知道， iframe是存在安全问题的，如果攻击者使用 iframe访问一个 未知来源的链接，有可能被注入恶意脚本，从而盗取
系统的隐私信息，这需要我们去严格配置 SCP，以及配置 sandbox，尽可能的保证 iframe 的安全性。

**资源加载过多**

而由于仅需要提供链接就可以嵌入，那么对于各自的项目来说，灵活度就很高，各个项目可以随意的选择各种技术框架来实现自己的业务，
又或者即使使用了相同的技术框架，但各项目的资源相对独立，对于整个系统而言，需要加载的资源量会十分庞大，
从而导致了页面访问速度变慢，经常会出现页面部分区域白屏等待时间过长等，这也带来了体验问题。

**访问地图丢失**

由于 iframe 嵌入的站点，独享访问历史记录，与外部的历史记录是相互独立的，即通过浏览器的 前进/回退 按钮来访问历史记录并
不能得到预期的结果，这在一定程度上影响了用户的操作。

## 寻找解决方案

有没有什么其他的方案，来进一步解决这些问题呢？


首先我们明确的知道，单项目管理目前来看不是一个可行的方案，需要在多项目管理上寻求解决方案。

### 多项目公共业务组件

对于多数的大型系统项目而言，大体上都采用以下布局结构：

![微前端-2](//assets.processon.com/chart_image/6311781d079129320755bec5.png)

主体布局结构包括：

- 导航栏 （可选）
- 左侧菜单栏 （可选）
- 内容区域
- 页脚 (可选)

在这种布局结构下，各个业务板块通常通过 导航栏 或者 左侧菜单栏 进行 导航切换，在 内容区域 展示 业务板块。
即，总体上看，对于业务来说，导航栏、左侧菜单栏、页脚，这几个都是可能 共同的，主要的不同点在于 内容区域。

那么我们可以把 共同的部分，如 导航栏、左侧菜单栏、页脚 这几个部分，抽离为公共业务组件，
对于每个业务板块，独立为单独的项目进行开发维护，并在项目中引入这些 公共业务组件。
公共业务组件其中主要负责之一是提供 链接到各个业务板块。

![微前端-3](//assets.processon.com/chart_image/631301e65653bb40f833b613.png)

这种方案具有如下的优点：

- 整体系统根据业务板块拆分为了多个项目；
- 实现了项目的独立性，可独立进行开发、发布；
- 通过在主项目重载渲染，实现类似 **SPA应用** 的访问体验；

但同样也带来了新的问题：

- 公共业务组件
  
  - 公共业务组件 如何进行管理；
  - 公共业务组件 如何在业务板块项目之间保持同步更新；

以及没有解决的问题：

- 资源加载过多；
  
   各个业务板块项目重复加载公共业务组件，重复加载各种库资源。

- 项目无法实现统一管理；

### 主项目重载业务项目资源

在上一个方案中，公共业务组件的引入解决了一部分问题，也带来了一部分问题，如何把公共业务组件进行统一管理，并保持一致性？

我们回到 iframe方案，在这个 iframe方案中，有一个主项目用于管理这些 菜单栏、导航栏等。 同样的，可以借鉴这个思路，
也抽象一个主项目，用于管理这些 公共业务组件，然后寻找另一种方式来加载渲染其他的业务板块项目。

我们知道，业务板块的项目，也是通过链接去访问的，而每个链接都对应着一个 html 资源文件，通过加载这个资源，以及HTML内的 css资源、js资源等来渲染页面。那么，我们可以通过解析这个 html资源，然后将得到的 html内容、css文件、js文件，在主项目中加载后渲染到特定的区域，
那么就可以做到在主项目中加载业务板块项目。

![微前端-4](//assets.processon.com/chart_image/6314ea9cf346fb55d89d4e77.png)

在主项目中，实现一个 资源加载器与解析器，通过业务板块项目的访问链接，获取 html资源文件，并解析 html 的内容，包括：

- `<head>` 标签中的 `<title>`, `<link>`, `<script>` 等;
- `<body>` 标签中的 html 内容，`<script>` 等

然后加载 解析得到的 CSS资源、JS资源，将 html内容 插入到 特定的区域中，并进行渲染。
从而呈现完整的网页内容。

这种方案，进一步解决了如下的问题：

- 公共业务组件交由 主项目进行统一管理，直接避免了同步问题；
- 业务板块均在主项目中渲染，提高了用户体验；

然而，也引入了新的问题：

- 业务板块都运行在同一个环境中，多个板块之间切换，加载的资源容易对环境产生污染，
  如污染了某个全局变量、polyfill相互污染等。
- 可能存在 加载资源跨域问题。

但是也拥有了如下的优点：

- 拆分项目，可独立开发和部署；
- 主项目统一管理 公共业务组件，更易于维护；
- 项目间的切换得到体验优化；

当方案思考到了这里，发现，主项目是通过 解析 **链接** 来加载业务板块项目， 
而  **链接** 对于现代前端来说，更多的意义是可能是 **路由**。那么我们顺着这个思路，继续优化，


## 新的方案


说起路由，我们很容易想到，像如今的 `react`, `vue`, `angular` 等主流的库/框架， 通过 路由 来实现 `SPA` 应用，
或者说， 通过 **路由分发页面**。

那么，我们可以进一步的扩展这个思路，是否可以通过 **路由分发应用** ？

### 路由

在前端的范畴中，路由指 随着浏览器的地址栏变化，而呈现不同的内容给用户。

通常使用 **hash** 或者 **history API** 实现前端路由。

``` js
// hash
`https://pengzhanbo.com/#route`

// history API
`https://pengzhanbo.com/route`
```
路由进一步细化，通过 `/` ，又可以 划分为 一级路由、二级路由、三级路由... 等多级路由。

在现代的前端框架如 `React` / `Vue` / `Angular` 等，均有通过 路由 实现 SPA应用 的技术方案。
而 SPA应用 就是 **路由分发页面** 。

### 路由分发应用

与 **路由分发页面** 类似，我们也可以通过 **路由分发应用** 。

类似于 主项目重载业务项目资源，通过 实现 路由与业务板块项目的映射关系，
在主项目中通过路由寻找业务板块项目，加载相关资源并渲染在相关区域。

### 主应用与子应用

从这里开始， 我们将 主项目 定位为 主应用， 将各个 业务板块项目 定义为 子应用。
在主应用中实现 子应用加载器，加载器通过 解析路由来获取加载对应的子应用。

**主应用：** 作为独立的项目，整个系统的入口应用，负责统一管理公共业务组件（如 菜单栏、导航栏、页脚等），负责实现子应用加载器，负责实现渲染子应用的容器。

**子应用：** 作为独立的项目，系统的各个业务板块分别独立为单独的项目，单独开发维护与部署。

### 注册子应用

主应用需要通过路由发现子应用，需要建议起 路由与子应用的映射关系，所以需要有一套机制，用于向主应用注册子应用，
并关联相关资源文件等。
``` json
[
  {
    "AppName": "Sub Application",
    "route": "/sub-app-route",
    "resource": {
      "js": [
        "https://example.com/index.js"
      ],
      "css": [
        "https://example.com/style.css"
      ]
    }
  },
  // more ...
]
```

## 微前端

通过将整个系统拆分为一个个小型的项目，小型项目即为子应用，通过细化，将整个系统细化为一个个微小的应用，
从而实现了降低整个系统的复杂性。
一个小型项目可以是某个部门的业务项目，可以是某个业务项目中的某个板块，也可以是一个单独的页面。

这也是为什么将新的方案称之为 **微前端**。

微前端是指，通过将一个系统，拆分为一个个 微小的独立的子应用，通过主应用聚合为一个完整的系统。
微前端是一个与框架无强关联的概念，可以类比于服务端的微服务，是浏览器端的微服务。

![](//assets.processon.com/chart_image/6318b7297d9c0833ec81b2cd.png)

由于子应用是独立的，理论上是支持使用任意的技术框架进行开发，无论是使用 jQuery开发，还是使用 Vue、React、Angular等。
然而在实际中，对于整个系统而言，技术框架的选择应该保持统一性，以保证整个系统的可维护性。


## 微前端的局限性

微前端的技术方案，更适合于 中大型的项目中使用，而对于小型项目而言，由于本身体量不大，没有必要对整个系统进行进一步的细化，
细化反而增加了项目的复杂度。

而对于中大型项目而言，如果是老系统迁移到微前端的方案，那么不可避免的，还需考虑新旧方案之间的迁移过渡的方案以及规划。
如果老系统中存在应用了多种不同的技术框架，或者同框架的不同版本，由于主应用、所有子应用均运行在同一个浏览器环境中，
不可避免的存在环境污染问题，如全局环境污染，polyfill对于原生对象的多次污染等，还包括CSS的命名污染等问题。
所以如何保证子应用的正确渲染，如何避免环境污染问题，也是亟需解决的问题。

## 微前端的未来

目前来看，微前端主要分为 主应用 和 子应用，在 **微** 上，也仅细化到页面级别，然而，对于微前端而已，还可以进一步的细化，
如，细化到页面的某一个区块，细化到某一个逻辑功能，均可以通过微前端的技术方案，共享到主应用以及子应用中使用。
整个系统愈加化整为零，将复杂度进一步的拆解，细化，令每一块功能、逻辑等都能使用通过某个项目提供，甚至独立的项目进行维护和部署。

微前端是一个与框架无关的概念，但在实现微前端时，如果允许多技术框架共存，所带来的问题的，反而比不使用微前端时所存在的问题，要更难以预料，难以解决。在实际的场景中，最好还是限定在统一的技术框架范畴中，避免由于共存不同的技术框架，而引入更为复杂的问题，

## 结语

微前端是一个相对新兴的技术概念，适用于一些前端场景，但最好是你已经考虑清楚了，微前端是解决你的场景问题的最好方案，否则，除非必要，
无需选择微前端方案。
