助力 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」
把握阿里巴巴前端新动向

相关文章
|
2月前
|
缓存 移动开发 Rust
前端构建工具 Mako 开源了
Hi,我是 sorrycc,Mako 的主要负责人之一,也是 Umi、Dva、Father 等库的作者。 很开心,Mako 终于开源了! Github 地址:https://github.com/umijs/mako/ 今天和大家正式介绍下他。
107 3
|
14天前
|
监控 JavaScript 前端开发
前端的混合之路Meteor篇(六):发布订阅示例代码及如何将Meteor的响应数据映射到vue3的reactive系统
本文介绍了 Meteor 3.0 中的发布-订阅模型,详细讲解了如何在服务器端通过 `Meteor.publish` 发布数据,包括简单发布和自定义发布。客户端则通过 `Meteor.subscribe` 订阅数据,并使用 MiniMongo 实现实时数据同步。此外,还展示了如何在 Vue 3 中将 MiniMongo 的 `cursor` 转化为响应式数组,实现数据的自动更新。
|
21天前
|
前端开发 JavaScript
乾坤qiankun(微前端)样式隔离解决方案--使用插件替换前缀
乾坤qiankun(微前端)样式隔离解决方案--使用插件替换前缀
203 8
|
26天前
|
设计模式 前端开发 JavaScript
前端编程的异步解决方案有哪些?
本文首发于微信公众号“前端徐徐”,介绍了异步编程的背景和几种常见方案,包括回调、事件监听、发布订阅、Promise、Generator、async/await和响应式编程。每种方案都有详细的例子和优缺点分析,帮助开发者根据具体需求选择最合适的异步编程方式。
57 1
|
14天前
|
前端开发 安全 API
前端全栈之路Deno篇(三):一次性搞懂和学会用Deno 2.0 的权限系统详解和多种权限配置权限声明方式
本文深入解析了 Deno 2.0 的权限系统,涵盖主包和第三方包的权限控制机制,探讨了通过命令行参数、权限 API 和配置文件等多种权限授予方式,并提供了代码示例和运行指导,帮助开发者有效管理权限,提升应用安全性。
|
23天前
|
JavaScript 前端开发
前端js,vue系统使用iframe嵌入第三方系统的父子系统的通信
前端js,vue系统使用iframe嵌入第三方系统的父子系统的通信
|
27天前
|
前端开发 JavaScript 小程序
前端uni开发后端用PHP的圈子系统该 如何做源码?
圈子系统系统基于TP6+Uni-app框架开发;客户移动端采用uni-app开发,管理后台TH6开发。系统支持微信公众号端、微信小程序端、H5端、PC端多端账号同步,可快速打包生成APP
|
2月前
|
移动开发 缓存 前端开发
构建高效的前端路由系统:从原理到实践
在现代Web开发中,前端路由系统已成为构建单页面应用(SPA)不可或缺的核心技术之一。不同于传统服务器渲染的多页面应用,SPA通过前端路由技术实现了页面的局部刷新与无缝导航,极大地提升了用户体验。本文将深入剖析前端路由的工作原理,包括Hash模式与History模式的实现差异,并通过实战演示如何在Vue.js框架中构建一个高效、可维护的前端路由系统。我们还将探讨如何优化路由加载性能,确保应用在不同网络环境下的流畅运行。本文不仅适合前端开发者深入了解前端路由的奥秘,也为后端转前端或初学者提供了从零到一的实战指南。
|
2月前
|
机器学习/深度学习 数据采集 JavaScript
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
ADR药品不良反应监测系统是一款智能化工具,用于监测和分析药品不良反应。该系统通过收集和分析病历、处方及实验室数据,快速识别潜在不良反应,提升用药安全性。系统采用Java开发,基于SpringBoot框架,前端使用Vue,具备数据采集、清洗、分析等功能模块,并能生成监测报告辅助医务人员决策。通过集成多种数据源并运用机器学习算法,系统可自动预警药品不良反应,有效减少药害事故,保障公众健康。
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
|
3月前
|
API Java 数据库连接
从平凡到卓越:Hibernate Criteria API 让你的数据库查询瞬间高大上,彻底告别复杂SQL!
【8月更文挑战第31天】构建复杂查询是数据库应用开发中的常见需求。Hibernate 的 Criteria API 以其强大和灵活的特点,允许开发者以面向对象的方式构建查询逻辑,同时具备 SQL 的表达力。本文将介绍 Criteria API 的基本用法并通过示例展示其实际应用。此 API 通过 API 构建查询条件而非直接编写查询语句,提高了代码的可读性和安全性。无论是简单的条件过滤还是复杂的分页和连接查询,Criteria API 均能胜任,有助于提升开发效率和应用的健壮性。
102 0