不久前的一天,我开始听到关于“微前端”的一个接一个的笑话——有点像我第一次了解 Toast 的方式。在四处询问之前,我并不理解其来源,询问后发现 Cam Jackson 的这篇文章。
在这篇文章中,我们将描述一种最近的趋势,即把前端单体应用分解成许多更小、更易于管理的片段,以及这种架构如何提高处理前端代码的团队的有效性和效率。
我认为它 应该 读作“前端单体应用”和“前端代码”,但我已经离题了。
这个想法类似于微服务,但适用于前端。因此,与其使用一个大型前端架构(例如 React 应用),不如将前端的不同部分完全独立地开发,彼此之间没有任何依赖关系,并且可以独立地进行开发和部署。
这是一个你无法确定它是否真的是对未来的有趣预示,仅仅是偶然对少数大型组织有效的利基架构选择,甚至仅仅是一个理论选项。
我的第一个想法是前后端一致性和 DRY 原则。在我工作过的任何地方,这些都是大事,而且似乎整个行业在前端方面一直存在着无尽的问题,这些问题与交付从一开始就保持一致和连贯的设计有关,并且不会重复使用大量的技术债务。如果 B 团队因与 A 团队无关的事情而被阻塞,则独立的前端听起来可能是一个问题,但随后它会引入 B 团队的输出与 A 团队的输出不一致的问题。
文章本身谈到了一个浏览/搜索登陆页面、一个详情/订购页面和一个个人资料页面,所有这三个页面都由不同的独立产品/团队处理。在我看来,这听起来很酷很有趣,但也听起来像是这些团队最好在工作中彼此相邻而坐;否则,这个应用在两周内就会变成弗兰肯斯坦的怪物。样式只被轻描淡写地提及,带有“我不知道,做好就行了”的感觉。当所有团队都在同一个产品上工作时,他们都会遇到这个问题,因此我在这里会有很大的担忧。如果认真讨论此事,我想解决的第一个问题将是一个超越所有这些内容的设计系统,并且每个人都必须使用它。
如果这些微前端共存在同一个页面上怎么办?文章说,使用 <iframe>。我无法想象一个世界,在这个世界里,这会导致良好的用户体验。(iFrames 很慢,尤其是当它们都启动自己的世界时,有很多 iFrames——工具提示和菜单等可能超出边界的元素怎么办?)
其他集成选项……将它们隔离到自己的捆绑包中,甚至使用原生 Web Components 听起来好一点。但是,一个 React 组件可能与一个 Vue 组件放在同一个页面上的这种孤立开发的想法,似乎对用户来说是一个巨大的损失,仅仅是为了解决一个非常具体的组织问题。更不用说,你失去了对代码库的共享理解以及对更小工具集的更深入的技术理解的好处。
我可能没有公平地描述所有这些,特别是因为这个想法对我来说是相当新的,我以前从未这样工作过。
Nader Dabit 有一篇后续文章:使用 React、Vue 和 Single-spa 构建微前端。只是为了避免误解:这个想法确实是你可以构建一个 React 应用,我构建一个 Vue 应用,然后我们将它们放在同一个页面上。我肯定来自一个时代,当我们发现使用多个版本的 jQuery 在同一个页面上,加上一个似乎是偶然加载了所有 MooTools 和 Prototype 的东西时,我们会先笑然后感到沮丧。我们感到沮丧,因为那是一个装满 JavaScript 的桶,大部分是无缘无故地重复,导致 bug 并减慢页面速度。这看起来没什么不同。
我想指出的是,我们正处于这个想法的“不仔细审查就讨厌”阶段。完全有可能在经过合法、仔细审查后,这个想法仍然失败。但现在还为时尚早。
公平地说。
抱歉堆积了这么多。😣
— Chris Coyier (@chriscoyier) 2019年6月20日
这……就是组件。他们发明了……组件。
起初,这对我来说似乎是一个激进的想法,但它确实很有道理。但是,我不确定我是否会像一些人建议的那样走得那么远。微服务的好处之一是,您可以真正让独立的团队处理不同的服务,所有团队都可以使用最适合该团队的任何语言和/或框架。最终结果通常只是一些 JSON 响应,因此底层架构几乎无关紧要。
微前端并非总是如此。虽然您可以在一个前端使用 React,在另一个前端使用 Vue 等,但这样做实际上给用户带来了负担:当他们在整个站点中浏览时,他们必须下载所有这些不同的库。我认为出于这个原因,微服务在这里并不具备相同的灵活性。所有前端都应符合一个通用的客户端框架。
但是,除此之外,它仍然具有很大的潜在价值。例如,ASP.NET Core 具有 Razor 类库,可用于共享常见的界面元素,如页眉、页脚或各种其他组件。其他框架可能具有类似的功能。这与公共和个人 CDN 相结合,可以为每个前端提供开箱即用的通用基础,从而允许实际前端仅专注于其需要专门提供的内容。
单体前端的问题与单体后端相同,对一个区域的更改需要部署整个系统。我认为,拥有可以独立于彼此部署和更改的离散前端单元具有真正的实际价值,前提是,它们都使用公共基础来确保一致性。
现实世界中一个很好的例子可能是 CAPTCHA 或各种“以…身份登录”功能。它们基本上是一个带有可导入窗口小部件的端点,该窗口小部件连接到它。
为了使这在大型应用程序中发挥作用,我认为团队除了功能之外还需要拥有页面。
以注册为例
A 团队可能会开发一个端点、UI 组件和一个用于注册的专用页面,并了解需要满足某些准则,例如组件如何在模式中使用屏幕空间,即使它们没有以这种方式使用它。
拥有主页的 B 团队可以使用 A 团队开发的端点和 UI 组件在主页上实现注册模式。
我不知道这在现实中是否真的有效;如果设计师不了解模块化在实践中的工作原理,或者开发人员编写了脆弱的 CSS,可能会给其他团队造成很多问题。
微前端应该只使用它需要的东西,而不是像 React、Vue、jQuery 这样的单体前端工具。这些庞然大物应该被分解成可链接的部分才能有用:与其加载整个庞然大物,不如只链接使用的部分。因此,这里的问题是,对于动态解释型语言,很难知道将使用哪些部分,因此庞然大物会越来越大,并且是单体的,必须加载所有内容才能使用其中任何一部分。
如果只加载所需的部分,那么 iFrames 不会很慢。加载整个单体应用的 iFrames,一次又一次(对于每个 iFrames 而言),总是会很慢,如果每个 iFrames 使用不同的单体应用,则缓存不会有太大帮助。
当然,另一个问题是它们必须通过互联网加载(至少第一次),并且对大量小片段的单独请求比一次性加载整个单体应用要慢。因此,前端确实需要一个链接步骤,您可以在其中将所需的小片段包含到页面的源代码中:而不是引用整个单体应用,而是将所需的部分链接到页面源代码中,并为每个 iFrames 创建自己的微型单体应用……
但是,对于构成站点的整套页面,最终结果是微型单体应用的总和可能大于单体应用的大小。因此,页面速度与消耗的资源之间、站点速度与消耗的资源之间存在权衡。
就我个人而言,由于加载时间,我不使用任何单体应用,我只是用纯 JavaScript 编写代码,在适当的地方使用框架来分离关注点并没有成为性能问题。
这在某种程度上类似于人们在 CSS-in-JS 的早期获得的反应。
我确实认为这只是一个想法。它是否会发展到微服务所达到的程度,这将是值得关注的事情。就我个人而言,我不明白在一个应用程序中使用多个框架在不构建其他工具来真正隔离它们并在浏览器中防止实际应用程序中出现意外的 UI 错误的情况下,如何能带来益处。这种方法的价值是否真的比使用我们当前设计系统或以 Storybook 为驱动的开发等更传统的方法更有益?我不确定,但我确实认为它可能适用于这样的系统。也许这不是该概念的预期方式。但不知何故,多个团队在不同的 UI 和代码库上工作这个想法似乎有点神奇,我喜欢这个想法。我认为这已经可以通过一些 Git 管理和经过深思熟虑的开发周期来实现,我认为我们可以实现“微前端”的优势,这些优势在部署时会融合在一起。它是否需要彻底的架构更改、重组或团队动态变化,我不确定。但我认为我们只有在证明了足够的理由将其付诸实践之后才会知道。谁知道呢
几年前,我们在一个大型项目中采用了这种方法,它与大约 5 个 UI 团队处理大约 20 个子应用程序/页面配合得很好。当您想要过渡或使用替代/新框架时,它效果很好,因为推出可以是渐进的,并且对当前实现的影响最小。整个代码库的版本管理是关键,尽管在某些领域(例如样式、组件)难以保持一致性,但这些领域有更合适的解决方案。
它并不完美,也不适用于所有情况,但它有很多好处。
我在一门课程中学到了微服务,我对此非常兴奋。但是前端微服务是一个完全不同的概念,它让我大开眼界。我将寻找更多详细信息,谢谢……
在所有这些讨论中,我最关心的问题是为问题的范围和背景提供可行的解决方案。我讨厌某些批评的轻蔑态度;例如“这试图解决什么问题冷笑,直接使用 iframe 就行了!”或“所以这就是 Web Components 了?”或者任何“啧啧,任何优秀的开发者都知道……”之类的东西。
假设我们有一个大型应用程序,其中包含大量功能。“父”应用程序由一个团队拥有,“子”功能由不同的团队拥有。有 30 多个功能由 15 个团队拥有。不仅如此,这 30 多个功能还可能在其他“父”应用程序中重复使用。目前的方向是“将功能做成 NPM 包”,然后父应用程序使用这些依赖项,编译它们,并以不同的方式优化它们以生成一个“单体”应用程序(……即使存在分块和拆分选项,也使用此术语)。
问题在于,父应用程序团队现在成为了所有其他子功能团队的守门人。如果没有一个团队积极主动地使用另一个团队的工作,任何内容都无法交付给用户。另一个问题是版本冲突。最终可能导致所有 15 个团队都必须更新所有产品才能实现一个实际上仅由 1 个子功能需要的包更新。
这样一来,团队就会感到受阻,无法及时交付产品,而其他团队则因不属于自己的问题而感到工作量过大。您可以将其归咎于文化问题,我听说有人说过:“这试图解决项目管理和团队协作问题,也许我们应该专注于文化而不是技术解决方案”。
问题在于我们没有将开发人员视为用户。他们是希望与他们产品用户相同的人:易用性、快速完成任务、掌控感、用最少的努力获得最大的成果。
无论是微前端还是其他解决方案,我都希望看到我们像对待用户一样对待开发人员。我相信开发人员的用户体验也很重要。如果有除了微前端之外的解决方案来解决这个问题,那么让我们给出具体的例子,而不仅仅是空洞的言辞和轶事。让我们分析这些示例的差距和可用性挑战。然后让我们提供更多可用的解决方案。我担心太多人都在关注这个领域,没有遇到其他团队遇到的确切问题,并将之斥为“不是真正的问题”。
这是一个非常现实的问题。微前端可能是一个解决方案。
因此,我在一家构建 CMS 的组织工作,UI 很复杂,代码库很大。像所有大型应用程序一样,它有遗留框架,它是 Wicket 与 ExtJS、Angular.js 和现在的 Angular 相结合,现在我们应该将其与一家收购我们的公司团队编写的 React 应用程序结合起来。整合所有内容到一个框架中太大了,这需要数年时间(加上新功能开发,否则我们将完蛋),到那时,一个更好的新框架将会出现。我的意思是,在像这样的大型企业应用程序中,总是会有技术债务,多个框架共存,并且没有时间将它们合并。使用这种微前端方法将这些部分(在我们的案例中使用 iframe)分开并允许它们通过定义良好的 API 层进行通信,可以实现更好的分离和以后的重构。
但这适用于大型应用程序,谁会对单个网页这样做呢?
看看
https://single-spa.surge.sh/