# 微前端的各种方案
微前端的核心解决的问题 -- 结构巨石应用。
目前我了解到的微前端方案有:iframe、single-SPA、qiankun(基于 single-SPA)、Module Federation(Webpack 5新增的构建功能),眼花缭乱,我们该选择哪一个呢?他们之间有什么区别和优劣呢?
# iframe
为什么在微前端领域,iframe 的使用者很少呢?他明明在易用性和知名度上遥遥领先。那就要从他的优缺点来分析了。
优点:
- 浏览器原生支持硬隔离
- CSS隔离、JS隔离做的很彻底
- 各应用独立部署
缺点:
- DOM结构不共享。iframe为页面中独立的一个盒子,他里面的弹框只能在自己的盒子里显示,导致无法剧中
- URL不同步。前进后退按钮无法使用
- 性能不好。每个iframe都需要加载独立的HTML文档,可能导致额外的网络请求,页面加载时间增加
- SEO问题。搜索引擎可能不会直接索引iframe中的内容
- 上下文完全隔离,数据不共享
经过上面的分析,适用iframe的场景,我觉得是:静态页展示、一个页面中只放置iframe、上下文不需要通信,即完全隔离独立的项目来使用。其实这样也符合微前端的概念,各干各的事,互相不打扰。类似于拼积木,每个积木有自己的功能。而不是类似一个总管给手下分发任务的场景,各个积木间就更不应该有信息来往了。
所以iframe不适合用很多小积木拼成一个作品的场景,样式的隔离会使得页面的效果十分难管理和控制。
# single-SPA
single-SPA 的原理是:在主应用中注册子应用,然后通过注册时填写的 URL 异步获取子应用,然后挂载到主应用的指定 DOM 节点上。
这就像,你是个有钱人,你的府上招人,iframe是通过地址URL买了一个录像带,放在电视上,你能看能听,但想跟里面的人通信,需要打电话去联系。而single-SPA是通过地址URL直接把人带过来给你现场跳,你在府上划分个舞台给他跳。
那single-SPA有什么优缺点呢?
优点:
- CSS隔离(CSS Modules、CSS-in-JS、Scoped CSS)、JS隔离(iframe、Web Workers)
- 各应用独立部署
- 懒加载。支持按需加载子应用,只有在需要时才会加载子应用,提高应用的性能。
- 路由集成。提供了一个全局路由,用于管理整个微前端应用的导航。这样,用户在浏览不同子应用时,URL会保持同步。
缺点:
- 依赖管理。由于每个子应用都可以使用不同版本的库和框架,可能会引起依赖管理的问题。需要谨慎处理不同子应用之间的依赖关系,以避免冲突和错误。
- 子应用需要安装依赖。分独立运行和子应用运行两种模式。
如此一来,终于可以拼积木了。
# qiankun
qiankun是基于single-SPA开发的,他相当于封装了single-SPA,增加了易用性,升级了一些特性。
qiankun都做了哪些升级呢?
- 增加了应用间的通信和状态管理
- 更便捷的子应用注册和加载方式
- 子应用不需要安装依赖
- 在子应用的加载方式上,增加了Fetch HTML、Fetch Source Code 等方式
- 全局状态隔离。通过内置的 initGlobalState 和 mount 阶段的 props 参数来实现。子应用可以通过这些机制来共享和隔离状态。
- JS沙箱。有快照沙箱和代理沙箱两种。快照沙箱基于原window对象和diff算法实现,代理沙箱利用proxy实现
- CSS沙箱。主要基于 shadow DOM 和 Scoped CSS。
如此一来,可以更便捷的拼积木了。
# Module Federation
single-spa 是一种组织微前端路由的方案。Module Federation是的一种基于打包共享代码的方案。它们之间并不冲突,可以相互补充一起使用。
以下示例展示了,Host 应用通过 ModuleFederationPlugin 插件的remotes指定了从 Remote 应用要使用的模块。
Remote 应用通过exposes配置指定了要共享的模块。
在 Host 应用中,可以使用类似 import('remoteApp/Component') 的语法来异步加载 Remote 应用中的模块。
// Host 应用的 webpack 配置
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
// ...其他配置
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js',
},
shared: ['react', 'react-dom'],
}),
],
};
// Remote 应用的 webpack 配置
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
// ...其他配置
plugins: [
new ModuleFederationPlugin({
name: 'remoteApp',
library: { type: 'var', name: 'remoteApp' },
exposes: {
'./Component': './src/Component',
},
shared: ['react', 'react-dom'],
}),
],
};
但是个人觉得这种模式还有提升空间,对于项目之间共享关系的维护比较复杂,增加项目间的耦合关系。适合项目少的时候进行使用。
目前觉得抽离公共组件库和公共方法更合理,但也有代码冗余的问题,可能云函数是未来的趋势吧。
最后,又回到最开始,微前端的思想万变不离其宗,就是为了结构巨石应用,具体使用那种方案,也要根据具体场景具体分析,找到适合的技术方案。