文最初发布于 Bits and Pieces 博客,经原作者授权由 InfoQ 中文站翻译并分享。
微前端是一个可以追溯到多年前的新趋势。随着新方法的出现以及各种挑战被克服,它们正在慢慢地进入主流。但遗憾的是,许多非常明显的认识误区,让许多人很难理解微前端到底是什么。
简而言之,微前端就是将微服务的一些好处引入前端。除此之外,我们不应该忘记,微服务也不是什么“银弹”。
提示:要在微前端或任何其他项目之间共享 React/Angular/Vue 组件,可以使用像 Bit 这样的工具。它允许你从任何代码库中“harvest”组件,并将它们共享到 bit.dev 的一个集合中。它让团队可以在任何存储库中使用你的组件。使用它可以优化协作、加速开发和保持 UI 一致性。
示例:在 bit.dev 中查找共享 React 组件
我想列一下在过去几个月中,我最常听到的关于微前端的误解。先从从一个明显的例子开始。
目前,许多微前端解决方案都是 JavaScript 框架。这怎么可能错呢?JavaScript 不再是可选的。每个人都想要高度交互的体验,而 JS 在提供这些体验中发挥着至关重要的作用。
除了加载速度快、可访问 Web 应用的优点外,还有其他因素应该考虑。因此,许多 JavaScript 框架都提供了 isomorphic 渲染能力。最终,这让它们不仅能够在客户端进行拼接(stitch),还能在服务器上准备好一切。如果有性能要求(如第一次有意义渲染的初始时间),这个选项听起来很不错。
请记住,isomorphic 渲染有其自身的挑战。
然而,即使一个 JavaScript 解决方案没有提供 isomorphic 呈现,这也没什么问题。如果不想在构建微前端时使用 JavaScript,我们当然可以这样做。有许多模式,其中很多根本不需要 JavaScript。
考虑一种“比较旧的”模式:使用<frameset>。我听见你笑了?好吧,有一些现如今人们试图做的分割,它以前就支持了(下文有更详细的讨论)。一个页面(可能由另一个服务渲染)负责菜单,而另一个页面负责标题。
复制代码
<frameset cols="25%,*,25%"> <frame src="menu.html"> <frame src="content.html"> <frame src="sidebar.html"></frameset>
如今,我们使用更灵活(且仍然受到活跃支持)的<iframe>元素。它们提供了一些很好的特性——最重要的是使得不同的微前端相互隔离,但仍然可以通过postMessage进行通信。
在 JavaScript 认识误区之后,这是下一个层次。当然,在客户端有多种技术可以实现微前端,实际上,我们甚至不需要<iframe>或任何类似的技术。
微前端可以像服务器端“includes”一样简单。有了诸如 Edge Side Includes 之类的高级技术,这将变得更加强大。如果我们排除了在微前端功能中实现的微前端场景,那么即使是简单的链接也可以很好的工作。最终,微前端解决方案也能像小而独立的服务器端渲染器一样简单。每个渲染器可能只有一个页面那么小。
下图展示了在反向代理中发生的更高级的拼接:
通过反向代理实现服务器端拼接
当然,可能 JavaScript 有许多优点,但它仍然高度依赖于你试图通过微前端解决的问题。根据你的需要,服务器端解决方案可能仍然是最好的(或者至少是更好的)选择。
在几乎每一个关于微前端的教程中,不同的部分不仅由不同的团队开发,而且使用了不同的技术。这是假的。
适当的微前端方法可能使用不同的技术,但是,这不应该是目标。我们做微服务也不只是为了在后端拼凑技术。如果我们使用多种技术,那只是因为我们获得了一个特定的好处。
我们的目标应该始终是某种统一性。最好的方法是考虑一个新项目:我们会怎么做?如果答案是“使用单一框架”,那么我们就走上正轨了。
长远来看,有很多原因可以解释为什么应用程序中会出现多个框架。可能是遗留的,可能为了方便,也可能是一个概念验证。无论是什么原因:能够处理这种场景还是不错的,但它绝不应该是开始就希望达到的状态。
不管你的微前端框架多高效——使用多个框架总是要付出不可忽视的代价。不仅初始渲染会花费更长的时间,而且内存消耗也会朝着错误的方向发展。不能使用方便模型(例如,针对某个框架的模式库)。需要更多的重复。最终,程序的 Bug 数量、不一致行为和可感知的响应性都会受到影响。
一般来说,这没有多大意义。我还没见过微服务后端的数据处理在一个服务中而 API 在另一个服务中。通常,服务由多个层组成。虽然某些技术内容(如日志记录)肯定会引入到公共服务中,但有时也会使用诸如 Sidecar 之类的技术。此外,还需要通用服务编程技术。
对于微前端,情况也是如此。为什么一个微前端只能做菜单?每个微前端都有相应的菜单吗?拆分应该根据业务需求来做,而不是技术决策。如果你读过一些关于领域驱动设计的书,你就会知道它是关于定义这些领域的——而这个定义与任何技术要求无关。
考虑以下划分:
按布局分解成微前端
这些都是技术组件,和微前端无关。在一个真实的微前端应用程序中,屏幕可能看起来是这样的。
按领域分解成微前端
的确,这里的拼接要复杂得多,但这是一个可靠的微前端应用程序应该为你提供的!
不。你应该共享那些值得共享的东西。你绝对不应该共享所有东西(见下一条)。但要做到始终如一,你至少需要共享一套原则。至于是通过共享库、共享 URL,或者只是在构建或设计应用程序时使用的文档,那就不重要了。
对于微服务,“无共享”架构如下图所示:
微服务的“无共享”架构
在浏览器中,这将导致使用<iframe>,因为目前没有其他方法可以防止资源泄漏。使用影子 DOM,则 CSS 可能会被隔离,但脚本层面仍然能访问所有内容。
即使想遵循无共享的架构,我们也会遇到麻烦。
当然,共享的程度越深(例如,使用一个通过应用 shell 追加到 DOM 的共享库),就越会出问题。另一方面,共享程度越浅(例如,只是一个指定基本设计元素的文档),就会出现更多的不一致性。
绝对不是。如果这样想,那么单体更有意义。就性能而言,这可能已经是一个问题了。什么可以延迟加载?我们能去掉一些东西吗?但真正的问题是依赖管理。什么都不能更新,因为它可能会破坏某个东西。
共享部件的好处是一致性保证。
现在,如果我们共享所有的东西,我们就是用复杂性来换一致性。这种一致性是不可维护的,因为复杂性会在每个角落引入 Bug。
问题的根源在于“依赖地狱”。下图很好地说明了这一点。
简而言之,如果一切都相互依赖,那么我们就会有依赖问题。仅仅更新一个方框就会影响整个系统。一致吗?确实。简单的?绝不。
为什么只是 Web?诚然,到目前为止,我们接触到的主要是 Web,但其概念和想法可以应用于任何类型的应用程序(移动应用、客户端应用……甚至是 CLI 工具)。在我看来,微前端只是“插件架构”的一个花哨叫法。不过,插件接口如何设计,以及运行使用插件的应用程序需要具备什么条件,这就是另外一回事了。
下图显示了一个非常通用的插件架构,来自 Omar Elgabry :
通用插件架构
该架构并没有在哪里运行的概念。它既可以在手机上运行,也能在 Windows 上运行,甚至还能在服务器上运行。
为什么?如果解决方案超级复杂,那么我肯定会找一个简单的。有些问题需要复杂的解决方案,但好的解决方案通常是简单的。
根据场景的不同,它甚至可能不需要一个分布式团队。拥有分布式团队是采用微前端的首要原因之一,但这不是唯一原因。另一个很好的理由是特性的粒度。
如果从业务的角度来看微前端,那么你就会发现,拥有启用和关闭特定特性的能力是很有意义的。针对不同的市场,使用不同的微前端。回到一个简单的权限模式,这是有意义的。不需要编写代码来根据特定条件打开或关闭某些东西。所有这些都留给公共层,可以根据(可能是动态的)条件激活或停用。
这样,不能(或不应该)使用的代码也不会被交付。虽然这不应该是一个保护层,但它肯定是一个便捷(和性能)层。用户不会感到困惑,因为他们看到的是他们能做的。他们看不到没有交付的功能,所以没有字节浪费在不可用的代码上。
对于任何类型的实现(或供讨论的底层架构),开发经验都可能遭到削弱。应对这种情况的唯一方法是开发人员优先。实现中的第一原则应该是:使调试和开发成为可能。采用标准的工具。
有些微前端框架根本不接受这一点。有些需要在线连接、专用环境、多重服务……这不应该是标准,也绝不是常态。
解耦的模块化后端可能为解耦前端打下了一个很好的基础,但通常情况下,情况并非如此。后端单体,前端模块化,也是完全可行的,例如,为简化个性化可能就要结合授权、权限和市场。
实际上,同样,微服务后端并不能证明适合将类似的模式应用于前端。许多微服务后端都是由单用途的应用程序操作的,它们的功能没有增加,只是外观发生了改变。
我已经读到过好几次,要创建一个微前端解决方案,就需要利用单存储库,最好使用像 Lerna 这样的工具。我不认可这一点。当然,单存储库有一些优点,但也有明显的缺点。
虽然有一些微前端框架需要联合 CI/CD 构建,但大多数都不需要。联合 CI/CD 构建通常会导致单存储库,因为其设置要简单得多。但对我来说,这是单体重新打包。如果你在单存储库上进行联合构建,那么你就失去了让微前端富有吸引力的两个非常重要的优点:
不管怎样,如果你看到微前端解决方案需要单存储库:那样做就行。一个精心设计的单体系统可能会更好,它不会有分布式系统的所有问题。
微前端技术并不适合所有人。我不认为微前端是未来的发展趋势,但我也相信它们在未来会发挥重要作用。
关注我并转发此篇文章,私信我“领取资料”,即可免费获得InfoQ价值4999元迷你书,点击文末「了解更多」,即可移步InfoQ官网,获取最新资讯~
Frameset 对象
Frameset 对象代表 HTML 框架集。
HTML frameset 元素拥有两个或者更多的 frame 元素。每个frame 元素拥有一个单独的文件。/p>
HTML frameset 元素规定框架集只有几行或列会。
Frameset 对象属性
W3C: W3C 标准。
属性 | 描述 | W3C |
---|---|---|
cols | 设置或返回框架集中列的数目。 | Yes |
rows | 设置或返回框架集中行的数目。 | Yes |
Frameset 对象事件
事件 | 描述 | W3C |
---|---|---|
onload | 在页面载入完成后立即执行脚本。 | Yes |
标准属性和事件
Frameset 对象同样支持标准的 属性 和 事件。
如您还有不明白的可以在下面与我留言或是与我探讨QQ群308855039,我们一起飞!
rame 的最主要功能是用来把一个页面划分成好几个小窗口页面,每个小窗口可以显示不同html文件,这样页面也可以称为框架结构页面,每个月小窗口称作框架窗口,下来将详细介绍框架窗口。
frame 在现在已经很少使用,虽然不是必须学习的,但还是建议了解学习下frame 、iframe的知识,在某些时候非常有用。
如果想将页面分成上下两部分,各自互相独立又互相关联,用户在其中一个部分操作页面不影响其它部分的页面,这样的页面也叫多窗口页面。
1、框架窗口基本结构:
框架窗口主要包含2部分,一个是框架集,一个是具体的框架文件。
框架集就是存放框架结构的文件,也是访问框架文件的入口,如果网页由上下2个框架窗口组成,那么除了这2个窗口的html文件,还有一个总的框架集文件。
框架文件是每个显示区域对应的html文件,如下示例:
index.html
<html>
<head>
<title>框架页面</title>
</head>
<body>
<frameset>
<frame src="top.html"></frame>
<frame src="bottom.html"></frame>
</frameset>
</body>
</html>
如上index.html 就是框架集文件,在这个框架集文件中定义了页面划分成上下2部分,分别对应top.html 和 bottom.html 2个框架窗口文件。
2、框架窗口分割
框架页面分割方式是在框架集frameset通过rows 或 cols 属性定义的,一般按分割类型分为以下几种:
2.1 水平分割窗口
将页面按水平方向分割,也就是上下结构,语法:
<frameset rows="窗口1高度,窗口2高度,">
<frame src="top.html"></frame>
<frame src="bottom.html"></frame>
</frameset>
在该语法中,rows 可以设置多个值,每个值对应一个框架窗口垂直高度,它的值可以使用像素单位或百分比单位。
如下示例:
<html>
<frameset rows="25%,50%,25%">
<frame src="/example/html/frame_a.html">
<frame src="/example/html/frame_b.html">
<frame src="/example/html/frame_c.html">
</frameset>
</html>
显示效果:
比如上面这个,被从上到下分割成3个窗口,高度依次是25%,50%,25%。
2.2 垂直分割窗口
沿着页面垂直方向分割,也就是左右结构的多窗口页面。语法如下:
<frameset cols="窗口1宽度,窗口2宽度,">
<frame src="left.html"></frame>
<frame src="right.html"></frame>
</frameset>
在该语法中,cols 可以设置多个值,每个值对应一个框架窗口水平宽度,它的值可以使用像素单位或百分比单位。
示例:
<html>
<frameset cols="25%,50%,25%">
<frame src="/example/html/frame_a.html">
<frame src="/example/html/frame_b.html">
<frame src="/example/html/frame_c.html">
</frameset>
</html>
显示效果:
比如上面这个,被从左到右分割成3个窗口,宽度依次是25%,50%,25%。
2.3、嵌套分割窗口
嵌套窗口就是在一个页面既有水平分割又有垂直分割的窗口,如下示例:
先水平分割再垂直分割
<frameset rows="50%,50%">
<frame src="/example/html/frame_a.html">
<frameset cols="25%,75%">
<frame src="/example/html/frame_b.html">
<frame src="/example/html/frame_c.html">
</frameset>
</frameset>
显示效果:
先垂直分割再水平分割
<frameset cols="50%,50%">
<frame src="/example/html/frame_a.html">
<frameset rows="25%,75%">
<frame src="/example/html/frame_b.html">
<frame src="/example/html/frame_c.html">
</frameset>
</frameset>
显示效果:
理论上可以无限嵌套,是不是很好玩。
2.4、noframes
当浏览器布置frame 时会显示noframes 中的内容。如下代码:
<html>
<frameset rows="50%,50%" frameborder="1" framespacing="100" bordercolor="blue">
<frame src="/example/html/frame_a.html">
<frameset cols="25%,75%" frameborder="0" bordercolor="orange">
<frame src="/example/html/frame_b.html">
<frame src="/example/html/frame_c.html">
</frameset>
</frameset>
</html>
效果显示:
1、设置边框 —— frameborder
frameborder 等于0时不显示边框,默认显示。
2、边框宽度 —— framespacing
在html5中无效。
3、边框颜色 —— bordercolor
如下代码:
<html>
<frameset rows="50%,50%" frameborder="1" framespacing="100" bordercolor="blue">
<frame src="/example/html/frame_a.html">
<frameset cols="25%,75%" frameborder="0" bordercolor="orange">
<frame src="/example/html/frame_b.html">
<frame src="/example/html/frame_c.html">
</frameset>
</frameset>
</html>
效果:
具体可以参考这里 https://www.cnblogs.com/lavenderzh/archive/2012/04/09/2438803.html 查看这几个属性的关系。
1、页面源文件 —— src
就是每个框架窗口对应的html文件,这里可以是图片或其它文件。
<frame src="文件地址">
2、页面名称 —— name
给每个框架窗口设置名称,有助于链接或查找窗口。
<frame src="文件地址" name="页面名称">
3、禁止调整窗口尺寸 —— noresize
党员鼠标拖到框架边框时,会发现鼠标形状变成可拖动的,可以改不框架窗口宽度或高度。如果不希望改变窗口宽度或高度,可以使用noresize 属性。
如下示例:
<html>
<frameset cols="25%,50%,25%">
<frame noresize="noresize" src="/example/html/frame_a.html">
<frame src="/example/html/frame_b.html">
<frame src="/example/html/frame_c.html">
</frameset>
</html>
窗口a是不可改变宽度的。
浮动框架是一种特殊的框架,它是在主窗口中嵌套一个子窗口,也就是整个页面不是框架页面,但却包含了一个框架窗口。
示例:
<iframe name="名称" src="https://www.w3school.com.cn/" width="800px" height="600px"></iframe>
效果如下:
1、浮动框架边框 —— frameborder
frameborder 属性规定是否显示 iframe 周围的边框。设置属性值为 "0" 就可以移除边框:
<iframe src="demo_iframe.htm" frameborder="0"></iframe>
显示效果:
2、链接到框架页面
通过 a 标签的 target 属性可以跳转到框架页面,示例如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<iframe src="http://www.runoob.com/" name="iframe_a"></iframe>
<p><a href="https://mp.toutiao.com/" target="iframe_a">头条号</a></p>
<p><a href="http://www.runoob.com/" target="iframe_a">菜鸟教程</a></p>
</body>
</html>
显示效果如下,点击下面2个链接,切换窗口。
frame 和 iframe 浮动框架用途很广,比如可以实现页面局部刷新,在之前被广泛使用,但是现在基本不建议使用,它有很多问题,比如安全性,性能方面等。
当然有时也会使用iframe,比如打印pdf,下载文件等等。
上篇:前端入门——html 表单控件使用
*请认真填写需求信息,我们会在24小时内与您取得联系。