286 lines
14 KiB
Markdown
286 lines
14 KiB
Markdown
---
|
||
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嵌入,在一定程度上,满足了 各部门各团队各业务线 独立开发独立部署的需求,只需要提供对应的页面链接就可以接入到
|
||
整个系统中。但也存在着一些问题
|
||
|
||
**安全问题**
|
||
|
||
然而,我们知道, iframe是存在安全问题的,如果攻击者使用 iframe访问一个 未知来源的链接,有可能被注入恶意脚本,从而盗取
|
||
系统的隐私信息,这需要我们去严格配置 SCP,以及配置 sandbox,尽可能的保证 iframe 的安全性。
|
||
|
||
**资源加载过多**
|
||
|
||
而由于仅需要提供链接就可以嵌入,那么对于各自的项目来说,灵活度就很高,各个项目可以随意的选择各种技术框架来实现自己的业务,
|
||
又或者即使使用了相同的技术框架,但各项目的资源相对独立,对于整个系统而言,需要加载的资源量会十分庞大,
|
||
从而导致了页面访问速度变慢,经常会出现页面部分区域白屏等待时间过长等,这也带来了体验问题。
|
||
|
||
**访问地图丢失**
|
||
|
||
由于 iframe 嵌入的站点,独享访问历史记录,与外部的历史记录是相互独立的,即通过浏览器的 前进/回退 按钮来访问历史记录并
|
||
不能得到预期的结果,这在一定程度上影响了用户的操作。
|
||
|
||
## 寻找解决方案
|
||
|
||
有没有什么其他的方案,来进一步解决这些问题呢?
|
||
|
||
|
||
首先我们明确的知道,单项目管理目前来看不是一个可行的方案,需要在多项目管理上寻求解决方案。
|
||
|
||
### 多项目公共业务组件
|
||
|
||
对于多数的大型系统项目而言,大体上都采用以下布局结构:
|
||
|
||

|
||
|
||
主体布局结构包括:
|
||
|
||
- 导航栏 (可选)
|
||
- 左侧菜单栏 (可选)
|
||
- 内容区域
|
||
- 页脚 (可选)
|
||
|
||
在这种布局结构下,各个业务板块通常通过 导航栏 或者 左侧菜单栏 进行 导航切换,在 内容区域 展示 业务板块。
|
||
即,总体上看,对于业务来说,导航栏、左侧菜单栏、页脚,这几个都是可能 共同的,主要的不同点在于 内容区域。
|
||
|
||
那么我们可以把 共同的部分,如 导航栏、左侧菜单栏、页脚 这几个部分,抽离为公共业务组件,
|
||
对于每个业务板块,独立为单独的项目进行开发维护,并在项目中引入这些 公共业务组件。
|
||
公共业务组件其中主要负责之一是提供 链接到各个业务板块。
|
||
|
||

|
||
|
||
这种方案具有如下的优点:
|
||
|
||
- 整体系统根据业务板块拆分为了多个项目;
|
||
- 实现了项目的独立性,可独立进行开发、发布;
|
||
- 通过在主项目重载渲染,实现类似 **SPA应用** 的访问体验;
|
||
|
||
但同样也带来了新的问题:
|
||
|
||
- 公共业务组件
|
||
|
||
- 公共业务组件 如何进行管理;
|
||
- 公共业务组件 如何在业务板块项目之间保持同步更新;
|
||
|
||
以及没有解决的问题:
|
||
|
||
- 资源加载过多;
|
||
|
||
各个业务板块项目重复加载公共业务组件,重复加载各种库资源。
|
||
|
||
- 项目无法实现统一管理;
|
||
|
||
### 主项目重载业务项目资源
|
||
|
||
在上一个方案中,公共业务组件的引入解决了一部分问题,也带来了一部分问题,如何把公共业务组件进行统一管理,并保持一致性?
|
||
|
||
我们回到 iframe方案,在这个 iframe方案中,有一个主项目用于管理这些 菜单栏、导航栏等。 同样的,可以借鉴这个思路,
|
||
也抽象一个主项目,用于管理这些 公共业务组件,然后寻找另一种方式来加载渲染其他的业务板块项目。
|
||
|
||
我们知道,业务板块的项目,也是通过链接去访问的,而每个链接都对应着一个 html 资源文件,通过加载这个资源,以及HTML内的 css资源、js资源等来渲染页面。那么,我们可以通过解析这个 html资源,然后将得到的 html内容、css文件、js文件,在主项目中加载后渲染到特定的区域,
|
||
那么就可以做到在主项目中加载业务板块项目。
|
||
|
||

|
||
|
||
在主项目中,实现一个 资源加载器与解析器,通过业务板块项目的访问链接,获取 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 ...
|
||
]
|
||
```
|
||
|
||
## 微前端
|
||
|
||
通过将整个系统拆分为一个个小型的项目,小型项目即为子应用,通过细化,将整个系统细化为一个个微小的应用,
|
||
从而实现了降低整个系统的复杂性。
|
||
一个小型项目可以是某个部门的业务项目,可以是某个业务项目中的某个板块,也可以是一个单独的页面。
|
||
|
||
这也是为什么将新的方案称之为 **微前端**。
|
||
|
||
微前端是指,通过将一个系统,拆分为一个个 微小的独立的子应用,通过主应用聚合为一个完整的系统。
|
||
微前端是一个与框架无强关联的概念,可以类比于服务端的微服务,是浏览器端的微服务。
|
||
|
||

|
||
|
||
由于子应用是独立的,理论上是支持使用任意的技术框架进行开发,无论是使用 jQuery开发,还是使用 Vue、React、Angular等。
|
||
然而在实际中,对于整个系统而言,技术框架的选择应该保持统一性,以保证整个系统的可维护性。
|
||
|
||
|
||
## 微前端的局限性
|
||
|
||
微前端的技术方案,更适合于 中大型的项目中使用,而对于小型项目而言,由于本身体量不大,没有必要对整个系统进行进一步的细化,
|
||
细化反而增加了项目的复杂度。
|
||
|
||
而对于中大型项目而言,如果是老系统迁移到微前端的方案,那么不可避免的,还需考虑新旧方案之间的迁移过渡的方案以及规划。
|
||
如果老系统中存在应用了多种不同的技术框架,或者同框架的不同版本,由于主应用、所有子应用均运行在同一个浏览器环境中,
|
||
不可避免的存在环境污染问题,如全局环境污染,polyfill对于原生对象的多次污染等,还包括CSS的命名污染等问题。
|
||
所以如何保证子应用的正确渲染,如何避免环境污染问题,也是亟需解决的问题。
|
||
|
||
## 微前端的未来
|
||
|
||
目前来看,微前端主要分为 主应用 和 子应用,在 **微** 上,也仅细化到页面级别,然而,对于微前端而已,还可以进一步的细化,
|
||
如,细化到页面的某一个区块,细化到某一个逻辑功能,均可以通过微前端的技术方案,共享到主应用以及子应用中使用。
|
||
整个系统愈加化整为零,将复杂度进一步的拆解,细化,令每一块功能、逻辑等都能使用通过某个项目提供,甚至独立的项目进行维护和部署。
|
||
|
||
微前端是一个与框架无关的概念,但在实现微前端时,如果允许多技术框架共存,所带来的问题的,反而比不使用微前端时所存在的问题,要更难以预料,难以解决。在实际的场景中,最好还是限定在统一的技术框架范畴中,避免由于共存不同的技术框架,而引入更为复杂的问题,
|
||
|
||
## 结语
|
||
|
||
微前端是一个相对新兴的技术概念,适用于一些前端场景,但最好是你已经考虑清楚了,微前端是解决你的场景问题的最好方案,否则,除非必要,
|
||
无需选择微前端方案。
|