谈谈微前端
微前端 是最近比较新兴的一个话题,它不具体指某个库某个框架,而是一个思想,一种概念,运用这种思想, 根据自身的需求,从而实现适用于自身的 微前端 。
本文根据最近我在公司内部举行的 微前端技术解决方案 分享而写。 提供的 微前端方案 也应用于公司内部的项目,并取得了良好的反馈,获得广泛好评。 本文不具体谈如何实现微前端,仅讲述微前端的概念,期望能够通过本文理解微前端。
前言
微前端 目前在行业内是一个新兴的思想。
诞生这个思想的背景是,在公司内部,常常会有一类项目,这类项目很大、很重, 涉及的业务内容多而杂,还涉及了跨部门共同维护,积累的庞大的技术债 等各种问题。 这类项目在维护成本上、部署成本上等,都会花费巨大的开销,前端开发人员对于维护这类项目,苦不堪言, 急需找到解决这类问题的方案。
基于这样的背景下,开始探讨 解决方案的可行性, 微前端 正是基于此 开始慢慢 出现在人们的视野中。
现状
发展历程
在 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 实现前端路由。
// hash
;`https://pengzhanbo.com/#route` // history API
`https://pengzhanbo.com/route`
路由进一步细化,通过 /
,又可以 划分为 一级路由、二级路由、三级路由... 等多级路由。
在现代的前端框架如 React
/ Vue
/ Angular
等,均有通过 路由 实现 SPA应用 的技术方案。 而 SPA应用 就是 路由分发页面 。
路由分发应用
与 路由分发页面 类似,我们也可以通过 路由分发应用 。
类似于 主项目重载业务项目资源,通过 实现 路由与业务板块项目的映射关系, 在主项目中通过路由寻找业务板块项目,加载相关资源并渲染在相关区域。
主应用与子应用
从这里开始, 我们将 主项目 定位为 主应用, 将各个 业务板块项目 定义为 子应用。 在主应用中实现 子应用加载器,加载器通过 解析路由来获取加载对应的子应用。
主应用: 作为独立的项目,整个系统的入口应用,负责统一管理公共业务组件(如 菜单栏、导航栏、页脚等),负责实现子应用加载器,负责实现渲染子应用的容器。
子应用: 作为独立的项目,系统的各个业务板块分别独立为单独的项目,单独开发维护与部署。
注册子应用
主应用需要通过路由发现子应用,需要建议起 路由与子应用的映射关系,所以需要有一套机制,用于向主应用注册子应用, 并关联相关资源文件等。
[
{
"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的命名污染等问题。 所以如何保证子应用的正确渲染,如何避免环境污染问题,也是亟需解决的问题。
微前端的未来
目前来看,微前端主要分为 主应用 和 子应用,在 微 上,也仅细化到页面级别,然而,对于微前端而已,还可以进一步的细化, 如,细化到页面的某一个区块,细化到某一个逻辑功能,均可以通过微前端的技术方案,共享到主应用以及子应用中使用。 整个系统愈加化整为零,将复杂度进一步的拆解,细化,令每一块功能、逻辑等都能使用通过某个项目提供,甚至独立的项目进行维护和部署。
微前端是一个与框架无关的概念,但在实现微前端时,如果允许多技术框架共存,所带来的问题的,反而比不使用微前端时所存在的问题,要更难以预料,难以解决。在实际的场景中,最好还是限定在统一的技术框架范畴中,避免由于共存不同的技术框架,而引入更为复杂的问题,
结语
微前端是一个相对新兴的技术概念,适用于一些前端场景,但最好是你已经考虑清楚了,微前端是解决你的场景问题的最好方案,否则,除非必要, 无需选择微前端方案。