之前我们讨论了什么是微前端,实际上使用微前端并不需要非常高深的技术。
你可以以链接的方式将两个页面连接起来,可以通过 iframe 将一个片段集成到一个页面中。
链接
使用 a标签 <a href="https://example.com/xxx"></a> 连接不同页面。
为了保证各团队间的链接不会冲突,需要在一开始做出约定,如规定前缀:
这样下来,可以一目了然 /product 前缀的的链接归属于商品团队,而 /order 前缀链接归属于订单团队。各团队新增链接也要固定使用约定的前缀。
事实上,即使后续使用更复杂的微前端技术,不同团队之间的页面 URL 也是需要进行区分的,使用前缀区分是个好方式。
可以使用 Nginx 实现服务端路由配置
upstream team_product {
server localhost:3001;
}
upstream team_order {
server localhost:3002;
}
http {
...
server {
listen 80;
...
location /product/ {
proxy_pass http://team_product;
}
location /order/ {
proxy_pass http://team_order;
}
}
}
优缺点
优点:
- 如果不需要将一个团队的页面嵌入到另一个团队的页面中,那么使用链接将页面连接起来,不仅耦合性低,稳定性(鲁棒性)也很高。
- 松散耦合:每个团队只需要关心自己的代码、部署等
- 高鲁棒性:页面之间不会互相影响
缺点:
- 不同团队之间代码冗余严重,页面之间可能都有相似的头部或者其他风格一致的模块。
- 不同页面之间互相跳转会造成页面刷新,不如单个页面(如单页应用)响应迅速。
iframe
使用 iframe 能够将一个页面内嵌到另一个页面中,并且和链接一样具有松散的耦合和高鲁棒性。
<style>
iframe {
border: 0;
width: 100%; // 和父元素一样宽
height: 750px; // 固定高度
}
</style>
<iframe src="https://example.com/product/xxx"></iframe>
如果你使用的 React,可以用 react-frame-component (原理是使用了 React.createPortal)来将 React 组件 插入到 iframe 中。
优缺点
优点:
可以在所有浏览器中工作,脚本和样式不会和主文档相互影响,还具有许多安全特性。
缺点:
- 布局约束
iframe 高度不能自动随内容撑开,可以使用 iframe-resizer 或 MutationObserver
- 性能开销
大量使用 iframe 对性能来说很糟糕,每个 iframe 都会创建一个新的浏览器上下文,会导致额外的内存和 CPU 消耗。
- 破坏无障碍性
iframe 破坏了页面语义化,这对于无障碍访问工具很难理解。
- SEO 不友好
搜索引擎会将外层页面和 iframe 作为两个不同页面进行索引。
Ajax
除了 iframe,自然的可以想到使用 Ajax 将一段代码片段插入页面中。
const element = document.querySelector('.product-recos') // 插入的元素
const url = element.getAttribute('data-url') // 从属性中拿到插入片段的 URL
window
.fetch(url)
.then(res => res.text())
.then(html => element.innerHTML = html)
不同团队的代码在一个文档中,因此我们需要各种方式来隔离样式和 JavaScript。
可以通过 css 选择器 class 添加命名空间前缀,或者使用 ShadowDOM 隔离样式。
对于 JavaScript,可以将脚本放在立即执行函数 IIFE 中,保证变量和函数不会添加到全局对象中。但是对于 cookie、storage、events 这些无法避免的全局变量,需要和 css 一样添加命名空间前缀
优缺点
优点
- 自然的文档流
- 搜索引擎和无障碍可访问性
- 渐进式增强
- 灵活的错误处理
缺点
- 异步加载
- 缺少隔离性
- 需要向服务器发送请求
- 脚本缺少生命周期
总结
链接和 iframe 都有完美的隔离性,并且不需要考虑兼容性,如果团队小并且后台系统不需要考虑 SEO,那么是非常完美的方案。
我在 2017 年时改造公司后台,将 PHP + Smarty 重构为 Vue 前后端分离时,就使用了 iframe 技术。当时还不知道“微前端”这个名词 😄。