由于业务增长,团队拆分,我们需要将原有系统的一部分模块(Vue实现)迁移到另外一个系统(React)中。但两个系统技术栈不同,导致重构成本变大,但业务又希望在短期内看到效果,后面可以增量的重构。,要求是对用户无感知的,真正将两个系统融合到一起。,经过技术调研,我们决定用微前端的方式实现。,微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。,微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。这跟我们现在的情况是相符的。它具有如下的特点:,微前端是一种类似微服务的架构,目标是将单一的单体应用变成由多个小型应用聚合为一的应用。,经过调研,我们有以下的实现方案。,优点:,缺点:,缺点层面,暂时是无法满足业务的要求的,所以我们没有采取这种方案。,qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。,它有以下的特性:,以上基本能满足我们的要求。,webpack 5 的支持的特性。,单页应用的每个页面都是在单独的构建中从容器暴露出来的。主体应用程序(application shell)也是独立构建,会将所有页面作为远程模块来引用。通过这种方式,可以单独部署每个页面。在更新路由或添加新路由时部署主体应用程序。主体应用程序将常用库定义为共享模块,以避免在页面构建中出现重复。,优点:,缺点:,qiankun 有一个缺点就是模块共享,如果能够和 webpack module federation 一起解决这个问题是一个不错的实践。但旧项目是基于 webpack4 构建,升级存在一定的风险,固没有采用这个方案。,qiankun GitHub star 数[6]-12.4k。可以看到 qiankun 的社区也是非常活跃的,综上,我们最终选择拥抱 qiankun。,我们的主应用主技术栈是 React, 第一步是安装:,第二步是设置路由,这里的 path 需要有一个特殊的前缀,用于激活子应用,这里我们统一称为 ``/vueApp`,这个后面还会用到,大家请记住。,第三步添加渲染入口:,第四步注册微应用,通过 qiankun 的 registerMicroApps 注册,name 微应用名称(这个后面也会用到,这里我就叫 vueAppName),entry 代表的微应用入口。container ,微应用的容器节点的选择器或者 Element 实例,就是第三步中的渲染入口中声明的。activeRule 是微应用的激活规则,支持数组,这里设置的就是我们第二步上面提到的 /vueApp。,第五步 qiankun 中的 start 函数,用来启动 qiankun。它可以通过 Options 传参开启一些有用的功能,比如 prefetch 预加载,sandbox 开启沙箱等。导出 start 在 App.ts 中启动即可。这里需要注意的 start 启动函数的时机,需要在微应用入口渲染完成之后才调用。,registerMicroApps 和 start 的图示(来自网络)。,我们微应用的主技术栈是 Vue。,在主应用注册好了微应用后,我们还需要对微应用进行一系列的配置。,第一步,我们在 Vue 的入口文件 main.js 中,导出 qiankun 主应用所需要的三个生命周期钩子函数(相关功能在代码注释中说明),代码实现如下:,另外,需要注意的是,需要在 main.js 入口中 import './public-path'; 否则会导致资源加载 404,比如主应用是 http://a.com/,微应用是 http://b.com,假如不设置的话,会以 http://a.com/1.js 访问微应用静态资源,会产生错误。public-path.js 如下:,这里解释一下,因为在开发环境中,两个是不同的域名,所以需要设置 __webpack_public_path__,我们线上还是使用同一个域名(后面部署的环节会讲到),所以非开发环境不需要设置 __webpack_public_path__。,第二步,我们还需要修改一下路由,因为之前添加了一个前缀 /vueApp,所以我们在路由中设置 base(我们使用的是 Vue Router 的 history 模式,这里没试过 hash 模式):,第三步,修改 webpack 构建打包配置,使 main.js 导出的生命周期钩子函数可以被 qiankun 识别。先是 devServer,要使微应用能够被 fetch 并配置相应的跨域请求头,解决开发环境的跨域问题:,还需要配置导出方式,导出方式设置为:umd,就将我们的 library 暴露为所有的模块都可以运行的方式了(webpack 4 不支持对于 libraryTarget 设置为 module-ES Module。webpack 5 支持,但也还是实验阶段)。另外这个 library 设置的是微应用的包名,这里与主应用中注册的微应用名称一致。,至此,我们的微前端就搭建完成。,官方提供了 initGlobalState[7] 方法用于注册 MicroAppStateActions 实例用于通信。其使用的就是发布-订阅模式。,offGlobalStateChange:取观察者函数 - 该实例不再响应 globalState 变化。,在主应用中,通过 initGlobalState 和 setGlobalState 设置通信信息:,在子应用中,设置 Action 类,并将 onGlobalStateChange,setGlobalState 映射到类方法中,导出类实例。,在挂载子应用的时候,会调用 render 方法。这时可以获取到相关的 props,并传给 action 实例:,在需要使用的地方, 通过 onGlobalStateChange 监听获取:,qiankun 加载子项目 css 样式机制大体为:挂载子应用时将子应用的 css 样式以 style 标签的形式插入并做快照,卸载子应用时再将快照内的 style 样式删除。,所以在加载子应用期间,若未开启 css 沙箱隔离,后加载的这些样式,可能会对整个系统的样式产生影响,对此,qiankun 提供了两种 css 沙箱功能,可以将子应用的样式包裹在沙箱容器内部,以此来达到样式隔离的目的。,在加载子应用时,添加 strictStyleIsolation: true 属性,实现形式为将整个子应用放到 Shadow DOM 内进行嵌入,完全隔离了主子应用,缺点:,在加载子应用时,添加 experimentalStyleIsolation: true 属性,实现形式类似于 vue 中 style 标签中的 scoped 属性,qiankun 会自动为子应用所有的样式增加后缀标签,如:div[data-qiankun-microName],缺点:,在子应用中,配置 postcss 插件,给子应用添加类前缀:,还是会存在上面插入 body 中的样式没有成功的问题,需要特殊处理。,上面也提到,子应用离开的时候,会销毁子应用的 style,而处于子应用的时候,我们页面大部分是子应用的 UI,所以我们尽可能保证主应用对子应用的无影响(主应用使用 CSS Module)。假如子应用对主应用有影响,我们就进行特殊处理。,因为我们主应用和子应用使用的框架是不一样的,所以冲突还比较少,所以目前使用这种方式。,我们采用的是主应用和微应用都部署到同一个服务器(同一个 IP 和端口)的方式。将主应用部署在一级目录,微应用部署在二级目录。,需要注意:上面提到我们在路由中加了前缀 /vueApp,也是通过这个进行激活子应用。但是 activeRule 不能和微应用的真实访问路径一样,否则在主应用页面刷新会直接变成微前端应用页面。所以我们这里的二级目录名称为 microApp,跟 vueApp 区分开(只是举例说明)。,这里提到的微应用的真实访问路径就是微应用的 entry,我们设置为 ***/microApp/,然后子应用构建的时候,配置 webpack 构建时的 publicPath 为 microApp。,举例:,主应用设置 entry 和 activeRules:,子应用路由设置:,子应用 publicPath 配置为:/microApp/,随着互联网的快速发展,公司业务发展也随之增长,这个时候一个系统共存多个子应用的需求也就应运而生了。微前端作为近几年很火的架构,解决的就是这类问题。,qiankun 作为一个相对成熟的微前端解决方案,目前社区活跃,开箱即用,并且提供较为完备的功能,比如样式隔离、JS 沙箱、预加载等。,本文记录了 qiankun 在我们业务中的落地时间,整体而言,使用相对简单,能够满足我们业务需求,问题大部分能够在网上找到答案。如果跟我们有一样的业务场景,qiankun 是一个的不错选择。
© 版权声明
文章版权归作者所有,未经允许请勿转载。