助力 20+ 阿里内部平台级系统,面向大型应用的开源微前端解决方案 icestark

简介: 从业务中孵化的微前端方案,帮你解决大型应用的前端架构问题。

作者 | 大果

image.png

2017 年中旬,飞冰(ICE)团队接到一个叫做「阿里创作平台」的项目,这个产品为创作者提供了入驻、帐号管理、内容管理、内容发布、粉丝运营、数据分析等等非常完备的功能,页面数 50+、项目一期有 3-6 个前端外加部分后端同学同时开发、业务未来有二方业务接入的需求……针对这些诉求,传统的单页面应用方案实在有点力不从心,因此在详细的技术评估之后我们自研了一套叫做 AppLoader 的方案,AppLoader 即应用加载器,我们将整个系统拆分成多个应用(仓库)然后通过 AppLoader 进行管理加载保证路由的正常渲染,最终实现对一个巨型系统的解耦。

AppLoader 诞生之后,先后服务过几个业务,但因为场景比较特殊因此我们一直未将这个方案对外开源,直到 2019 年年初,一方面社区中微前端的概念逐渐普及,另一方面在阿里内外也出现越来越多的类似业务场景,因此我们对 AppLoader 做了一次能力和品牌的升级,同时面向社区开源,升级之后的品牌便是我们开源在 GitHub 上的 icestark,欢迎 star/pr 或者提 issue。

技术方案对比

如前文所说,两年多之前我们接到「阿里创作平台」这个项目,这个产品承载了创作者从入驻到创作完整的生命周期,相比于普通业务有以下两个不同点:页面数非常多、频繁的多人协作与多需求并行、未来有二方业务接入的需求,针对这些差异点我们也对当下主流的一些方案做了一轮分析对比。

单/多页面应用

无论是 SPA 还是 MPA,本质上都是一个唯一的项目仓库,即便我们将一些公共组件抽离出去单独维护,最终也是需要在项目仓库中进行打包构建,这样的单项目仓库所带来的问题:

  • 代码量达到一定量的时候,单次构建时间很长,开发&发布效率极低
  • 涉及到二方/三方的依赖升级会影响整个应用,加之页面数很多导致回归成本极高,长期下去面对类似 React 版本升级这样的技术诉求很难落地
  • 对于多人协作、多需求并行非常不友好,需要花费大量的精力在协作流程上

iframe

将系统拆分成多个应用,每个应用独立开发独立部署,然后通过 iframe 的方式将这些应用嵌入到 portal 系统中,这个方案解决了很多协作、隔离之类的问题,但 iframe 的体验问题一直是个难以解决的问题:

  • iframe 体验问题:页面加载慢;双滚动条问题;内部蒙层无法遮罩到外部框架,同时布局无法居中;内部跳转后外部无响应,刷新后又会回到 iframe 首页……
  • 每个应用依然需要依赖服务部署,需要有域名,应用的部署成本偏高

关于 iframe 的体验问题,有的可以解决,有的几乎无法解决,因此结合体验的重要性我们最终放弃了 iframe 这个方案。

封装框架组件

封装一个统一的框架 UI 组件,发布到 NPM,然后每个应用自行接入框架组件,但是这个方案有以下几个问题:

  • 用户访问入口不统一,本质还是多个系统,只是看起来框架是一致的
  • 每个应用依然需要依赖服务部署,需要有域名,应用的部署成本偏高

这个方案更适合于多个业务间有统一的框架诉求,但从业务、前端、后端服务都比较独立,没有中心化管理的需求。

AppLoader 与 icestark

2017 年中旬我们还没有了解到「微前端」的概念,因此结合业务诉求自研了 AppLoader 的类微前端解决方案,再到 2019 年将整体品牌升级到 icestark。

关于 icestark

这个章节核心介绍 icestark 的设计思路以及如何使用。

架构与概念

image.png

上图即整体的设计与分层:

  • 框架应用:又叫主应用、宿主应用、底座应用,负责系统整体布局、微应用的注册加载渲染、微模块的管理
  • 微应用:又叫子应用,一般是一个 SPA 应用,可以独立运行,也可以注册到框架应用中运行
  • 微模块:针对多个微应用共享的模块或者页面上的一些二方模块,微模块与传统的业务组件类似,特殊点是微模块通过动态加载资源并渲染,而普通的业务组件一般是通过 npm 引入静态打包编译

之所以将微应用和微模块的概念独立开来,是因为两者的使用场景和表现差异都是比较大的,微应用只会同时存在一个,需要考虑路由的问题,微模块会同时存在多个因此需要核心考虑沙箱隔离、公共依赖提取等问题。

框架应用

框架应用的 API 设计我们参考了 react-router,react-router 是页面级路由,而 icestark 是应用级路由。同时为了保证开发体验,目前框架应用我们跟 React 做了耦合,让开发者可以通过 jsx 的方式声明应用路由,因此框架应用必须基于 React,未来我们也会结合用户诉求提供 vue 版本的 icestark。

// src/app.js
import React from 'react';
import { AppRouter, AppRoute } from '@ice/stark';

export default class App extends React.Component {
  render() {    
    return (      
      <BasicLayout>        
        <AppRouter>          
          <AppRoute
            path="/seller"
            title="商家平台"
            url={[              
                '//unpkg.com/icestark-child-seller/build/js/index.js',
                '//unpkg.com/icestark-child-seller/build/css/index.css',            
            ]}          
          />          
          <AppRoute
            path="/settings"
            title="设置"
            entry="//unpkg.com/icestark-child-seller/build/index.html"          
          />          
          <AppRoute
            path="/"
            title="通用页面"
            render={() => {              
              return (                
                <ReactRouter>                  
                  <Switch>                    
                    <Route path="/home" component={Home}>
                    <Route path="/about" component={About}>

                  </Switch>                
                </ReactRouter>              
              )            
            }}          
          >
        </AppRouter>      
      </BasicLayout>    
   ); 
  }
}

如上所述,每个 AppRoute 就对应一个微应用,微应用支持几种不同的注册方式:

  • url:对应渲染微应用需要的资源,针对微应用资源情况比较明确的情况
  • entry:对应微应用构建出来的 html,针对微应用资源情况不明确,比如有 external、 不同的 vendor、资源地址有 hash 等场景
  • render:可以渲染一个自定义的 React 组件,比如渲染一个 iframe,甚至框架应用自身的一个 SPA 应用

微应用加载

微应用通常是一个 SPA 应用,支持 React/Vue/Angular 等不同的应用类型,在传统 SPA 应用基础上添加相关的应用声明周期注册方法即可:

import React from 'react';
import ReactDOM from 'react-dom';
import { isInIcestark, getMountNode, registerAppEnter, registerAppLeave } from '@ice/stark-app';
import App from './App';
if (isInIcestark()) {
  registerAppEnter(() => {    
     ReactDOM.render(<App />, getMountNode());  
  });
  registerAppLeave(() => {    
     ReactDOM.unmountComponentAtNode(getMountNode());  
  });
} else {  
  ReactDOM.render(<App />, document.getElementById('ice-container'));
}

对于微应用的加载与渲染,除了将其正常渲染出来,核心要解决的是路由跳转的问题,基本思路:

  • 建立微应用和路由的映射关系,为每个微应用分配一个基准路由如 /seller ,这个微应用保证所有的路由定义在 /seller 下,那么当从其他路由跳转到 /seller 路由时我们就可以加载渲染 /seller 对应的子应用 bundle 了。
  • 通过劫持 history.pushStatehistory.replaceState 两个 API,同时监听 popstate 事件,保证能够捕获到到所有路由变化。当捕获到路由变化时,根据路由查找对应的微应用,如果对应的还是当前这个微应用则什么事情都不做,如果对应的是新的一个微应用则卸载之前的微应用,同时加载新的微应用并渲染。
  • 框架应用中包含系统 Layout,我们需要将微应用渲染到 Layout 里面,但是单页面应用都是直接通过 ReactDOM.render(<App />, document.getElementById('#root')) 的方式渲染,如果直接执行那么渲染的位置是无法被控制的,因此 icestark 提供了一个 getMountNode() 的 API 保证微应用能够渲染到正确的节点里。

微模块加载

微模块主要是针对一些跨应用的共享模块、二方接入模块等场景,此部分能力正在建设中,具体可参考文末的 issue 链接。

沙箱隔离

目前 icestark 主要服务的是二方可控的业务场景,因此从整体设计上我们优先关注与传统 SPA 应用的开发体验一致性,次要关注沙箱隔离。如果有三方接入的场景,icestark 也支持了 render + iframe 的方案。

脚本隔离

基于 Proxy 能力对 window 做了一层代理,保证每个微应用/微模块渲染时使用的都是独立的 window 对象,进而实现全局 window 的隔离,放置应用污染 window 导致系统出错,我们将这部分能力封装为 @ice/sandbox ,因此在其它类似的场景也可以独立使用。

样式隔离

  • 针对框架应用与微应用基础组件样式冲突的问题,推荐框架应用上通过工程能力将基础组件的 Class 前缀替换掉。
  • 微应用层面建议使用 CSS Modules,通过工程保障不引入全局样式。

存在的问题

当然,跟其他方案一样,微前端的方案也有自身的缺陷,因此还是需要结合业务情况来判断是否使用,而不是一股脑什么东西都往微前端上靠,这里列举两个问题:

  • 存在一定的技术复杂度,结合对社区同学的一些答疑,微前端整体还是增加了一些复杂度的,因此在落地微前端方案需要确保有一个相对资深的同学进行整体的把控,避免引入新的问题
  • 微前端的框架应用本质是一个中心化的系统,我们建议微应用尽量降低对中心化系统的依赖度,比如提供 React、各种 API 甚至一些组件,因为依赖越多意味着中心化系统的变更风险越高

业务场景与价值

大型系统

面向文章开头说的大型系统,微前端带来的价值:

  • 多人协作成本:按照业务域区分成不同的应用,每个应用独立开发独立部署,保证开发效率
  • 稳定性:单个应用可升级,不需要依赖全局
  • 技术选择灵活性:新的应用可以选择新版本的技术体系(比如 Vue@3.0),甚至某些简单的应用也可以选择可视化搭建的方式
  • 二方开放能力:二方业务可按照规范开发微应用

portal 系统

很多中小公司里会有很多小系统,这些系统很多时候都是独立开发部署的,系统间没有任何复用的能力,如果引入微前端:

  • 部署成本:单个应用只需要发布资源,不需要考虑域名、Nginx 托管之类的问题
  • 通用能力的复用:诸如通用的登录、鉴权等逻辑,不需要每个应用重复实现
  • 可监控可管理:能够收集到所有应用的信息、技术框架、版本等信息,进而可以做到更好的管控

业务落地

icestark 目前在阿里内部落地了 20 个左右的平台型系统,不同系统可能有 5、10 甚至数量更多的微应用。

阿里创作者平台
包含 20+ 子应用,其中 5-8 个子应用由二方业务开发。

image.png

阿里健康-熙牛医疗云医院信息系统

image.png

淘系小二工作台
面向淘系运营小二的后台都将已子应用的方式接入小二工作台,打造面向运营小二的操作系统。

image.png

未来

icestark 目前整体的能力已经趋于完善,接下来我们会结合业务诉求持续完善解决方案,比如更好的权限方案、埋点方案等等,帮助业务更加简单的落地微前端方案。欢迎大家关注 icestark ,关注我们的进展。

相关链接


image.png
关注「Alibaba F2E」
把握阿里巴巴前端新动向

相关文章
|
16天前
|
前端开发 JavaScript 关系型数据库
从前端到后端:构建现代化Web应用的技术探索
在当今互联网时代,Web应用的开发已成为了各行各业不可或缺的一部分。从前端到后端,这篇文章将带你深入探索如何构建现代化的Web应用。我们将介绍多种技术,包括前端开发、后端开发以及各种编程语言(如Java、Python、C、PHP、Go)和数据库,帮助你了解如何利用这些技术构建出高效、安全和可扩展的Web应用。
|
17天前
|
Web App开发 前端开发 JavaScript
前端应用实现 image lazy loading 的原理介绍
前端应用实现 image lazy loading 的原理介绍
26 0
|
21天前
|
前端开发 编解码 数据格式
浅谈响应式编程在企业级前端应用 UI 开发中的实践
浅谈响应式编程在企业级前端应用 UI 开发中的实践
20 0
浅谈响应式编程在企业级前端应用 UI 开发中的实践
|
27天前
|
JSON 前端开发 安全
前端开发中的跨域问题及解决方案
在前端开发中,跨域是一个常见但又令人头疼的问题。本文将深入探讨跨域产生的原因以及一些常见的解决方案,帮助开发者更好地理解和处理跨域情况。
|
2天前
|
缓存 安全 JavaScript
前端安全:Vue应用中防范XSS和CSRF攻击
【4月更文挑战第23天】本文探讨了在Vue应用中防范XSS和CSRF攻击的重要性。XSS攻击通过注入恶意脚本威胁用户数据,而CSRF则利用用户身份发起非授权请求。防范措施包括:对输入内容转义、使用CSP、选择安全的库;采用Anti-CSRF令牌、同源策略和POST请求对抗CSRF;并实施代码审查、更新依赖及教育团队成员。通过这些实践,可提升Vue应用的安全性,抵御潜在攻击。
|
2天前
|
前端开发 测试技术 开发工具
探索前端框架React Hooks的优势与应用
本文将深入探讨前端框架React Hooks的优势与应用。通过分析React Hooks的特性以及实际应用案例,帮助读者更好地理解和运用这一现代化的前端开发工具。
|
13天前
|
Web App开发 移动开发 运维
跨域解决方案[前端+后端]
跨域解决方案[前端+后端]
25 0
|
26天前
|
编解码 前端开发 JavaScript
探索前端开发中的新趋势:WebAssembly 技术应用与展望
本文将深入探讨前端开发中的新趋势——WebAssembly 技术,介绍其在前端领域的应用场景和优势,并展望未来在前端开发中的潜在影响。通过对 WebAssembly 技术的原理解析和实际案例分析,帮助读者更好地了解并应用这一新兴技术。
|
28天前
|
前端开发 JavaScript NoSQL
从前端到后端:构建全栈应用的技术挑战与解决方案
在当今互联网时代,全栈开发成为越来越受欢迎的技术趋势。本文将深入探讨从前端到后端的全栈开发过程中所面临的技术挑战,并提出相应的解决方案,涵盖前端框架选择、后端技术架构、数据库设计以及跨平台兼容性等关键问题。
|
8月前
|
Web App开发 前端开发 JavaScript
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-fiber解决了什么问题
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-fiber解决了什么问题
95 0