当前位置: 首页 > Web前端 > HTML

推开“微前端”的大门

时间:2023-03-29 11:52:45 HTML

介绍:“微前端”与“微服务”类似,是近两年被频繁提及的名词。Web开发已经从前后端在一起的单一应用发展到前后端分离的SPA。这些变化使得前后端实现了开发解耦和独立发布。解耦使得开发、调试、发布的过程更加自由灵活。但随着业务的发展,中大型SPA逐渐成为“MonolithicApplications”。渐渐地,对模块的拆解要求越来越高。全文5574字,预计阅读时间14分钟。本文主要分享两个方面:思考什么样的系统或前端需要微前端简单介绍微前端工程中需要注意的一些设计要点。《No》主要关注以下内容:对一些开源框架的深入介绍,以及如何设计一个非常通用的微前端框架的比较。非常详细地介绍了某个设计点的实现。如果你正在犹豫是否使用微前端,希望它能给你一些想法。思路。1、什么样的系统或前端团队需要微前端?如果您处于以下情况的困境,微前端可能是您的选择。假设一个SPA的前端模块包括ABCD四个模块,错综复杂地依赖于多个独立部署的后端服务,如下图所示:图1上线当天,可能需要对出一个模块依赖映射来确定当前的启动顺序。前端模块A、B、C、D都属于一个SPA,需要整体发布;模块C依赖服务2和3,模块D依赖服务1和4;因为模块C和D需要一起上线,所以和服务2没有关系模块D,服务1和4都需要等待服务2的发布。这个SPA模块会成为整个上线日的繁忙十字路口,而且上线和验证操作需要排队等候,相关同学不会厌倦。更可怕的是,大家辛辛苦苦熬夜上线后,发现问题需要紧急止损的模块往往会带来雪崩般的回滚操作。如果线上方案准备不充分,需要暂时确认冲击面,梳理回滚操作的地图序列。这种消耗的停损时间在一些系统和产品中往往是不可接受的,更不用说造成回滚反应链的模块压力了。结合本例,有以下参考点。如果它们恰好击中了你的痛点,微前端将是你的“解决方案”。并行开发的模块数量多。当你有多个功能相对解耦的模块时,需要并行开发和发布。协同开发者人数多当开发单个应用的学生人数超过一定规模(如10人以上),且每人负责的子模块相对固定时,协同效率往往会呈指数级增长人数增加。协同开发团队数量庞大。这是一个有趣的观点。与2的区别主要在于“屁股决定头”的“屁股”。如果由于各种原因不得不跨团队维护同一个项目,那么可能会面临开发权限、规范、节奏等诸多协同点,这些协作有时会因为跨团队而导致效率低下。发布频率高这个需要结合1和2,对于人数多但发布频率低的模块,协同沟通等成本就成了伪命题。当然,一个多人开发的应用很可能是一个发展中的业务,需要经常迭代。需要跨技术栈这可能是很多团队选择微前端的重要原因。你可能维护过一个运行多年的系统,系统复杂,技术栈陈旧。你希望引入一个新的技术栈,但是你没有人力一下子重写系统。先做系统微前端,再逐步改写分子模块,将是一种工作节奏可控、质量风险低的方法。不过,对于跨技术栈的提法,个人认为对于个别产品或平台来说,是有“陷阱”的。从团队长期开发效率、平台性能优化空间、一致性体验等角度来看,长期不受控的跨技术栈是有风险的。一些小粒度的抽象(组件、业务模块等)难以高效复用,一些升级难以直接全局应用。这些问题带来的效率下降,可能会掩盖自主开发发布带来的效率提升。MartinFowler在介绍微前端的好处之一时,还写了《Incrementalupgrades》。渐进更新不等于永久差异。所以,除非你做的只是一个入口,对子模块的一致性没有很高的协调要求,否则我建议跨技术栈是渐进迁移的中间状态。2、选择微前端需要注意的设计点已经通过了checklist。如果一些问题让你决定转型微前端,下面的一些应用设计要点可能对你有所帮助。2.1主模块和子模块首先你需要一个主模块,它可以是一个HTML,一个或一组topappbundle,我们称之为“APPShell”或“Nutshell”(https://martinfowler.com/arti...),你的顶层逻辑需要加载一些前置依??赖,初始化入口内容,根据路由等信息加载渲染子模块。有很多方法可以在运行时实现子应用加载。这里列举几个,就不一一展开了:subapp是一个iframe,最简单暴力的实现方式,拥有iframe实现页面的所有限制。优化空间和顶层控制有限,个人不推荐。子应用程序是一个Web组件,它实例化路由开关之后的组件。您需要考虑浏览器兼容性限制。子应用程序是独立分发的子包。sub-bundle需要定义一些生命周期钩子,比如register、mount、unmount等,这种方式应该会被更广泛的使用。2.2单实例vs多实例根据运行实例的数量,业界将APP分为“单实例”和“多实例”两种类型。“单实例”是指在运行时只有一个子模块同时被激活。下面的图2是一个常见的场景。子模块通常按照路线加载/卸载。图2中的“多个实例”意味着在运行时可能有多个子模块实例同时处于活动状态。下图3是路由和子模块不再是一对一关系,而是可能变成多对多的场景。图3中的实施方案是由业务和服务的拆解决定的。建议将功能内聚、维护团队相对收敛的模块分离,然后在运行时检查是否需要在同一个路由下出现“多个实例”。无论运行时有多少个子模块,都需要路由和页面内容之间的映射关系。对于某些APP来说,这种关系可能是一种枚举关系,在开发和编译的时候就可以预测到,所以你可能需要维护一个映射如下:static.server.co...',route2:'https://my.static.server.com/...',route3:'https://other.static.server.c...'};//Multi-instance-SignconstsubAppRoutes={route1:[{subApp:'https://your.static.server.co...',layout:{//省略布局描述信息}},{subApp:'https://my.static.server.com/...'}],route2:[{subApp:'https://your.static.server.co...'},{subApp:'https://my.static.server.com/...'}],route3:[{subApp:'https://my.static.server.com/...'}]}入口模块会基于路由及相关配置,完成资源加载,根据生命周期协议挂载子应用。如前所述,您需要一个路由器。子模块冲突可以通过一些路由前缀约束规则来避免。关于路由的设计和实现的文章很多,不是本文的重点,暂时不一一列举。如果你的系统非常灵活,页面类型和数量不收敛,那么你可能需要一个动态存储的数据结构和服务来代替浏览器中的定位路由。通过定义系统的页面结构模式,描述当前页面中的每个页面。微前端模块需要放置的位置和布局(如上文“多实例-示意图”中的route1)。模式的引入使低代码开发成为可能。以后可以使用微前端模块通过简单的数据结构配置拼凑出一个APP页面。当然,这需要额外的存储和业务逻辑,系统的复杂度也会增加。2.3子模块通信大多数系统的子模块之间还是不可避免地存在一些相互影响。假设你有一个功能模块A和一个内容推荐模块B,B的内容需要根据A的操作产生连锁反应。有几种常见的做法:有一个全局的store,用于全局的数据共享,A写数据变化到store,B监听store变化并响应。单向数据流的设计使开发和调试更加容易。当然,你需要避免子模块和单个商店内容的冲突。这个类似于解决路由冲突的方法,比如增加一些命名空间。其余内容与大家接触过的各种单体店的设计大同小异,在此不再赘述。事件通信提供了全局的EventBus能力,A派发事件,B接受并响应事件。这是一个不起眼的事件通信,但是在微前端实现中有一个需要注意的地方。子模块之间加载和实例化的过程大多是独立和异步的。当A发布一个事件时,B还没有被实例化,所以这个消息可能会被遗漏。1中的大部分问题都可以通过共享数据来解决,如果你更喜欢使用事件通信来解决,那么在设计和实现EventBus的时候就需要考虑这个功能,比如缓存事件队列。B开启事件监听后,查看缓存事件队列中是否有事件已经派发,需要响应。当然,你可能还有一些想法,比如二次包装。在A和B之上封装C,负责两个模块的调度,但是这种方式和上面两种不在一个层次。比如C的实现往往需要与A、B进行通信或数据共享,同时过度二次包装的弊端也很明显。封装的模块数量会随着服务的组合而膨胀,难以汇聚和维护。在实践的过程中,你应该“需要建立”1和2的能力。3.性能优化小技巧除了平日性能优化的常规操作,微战线下还有两个小点值得关注-端架构。3.1多实例按需渲染如果你的APP页面有很多子模块,可以考虑按需渲染。在下图4所示的场景中,您可以根据视口和模块位置来决定当前模块是否需要实例化渲染。这样可以减少首屏浏览器的脚本和资源加载时间。当然,随着窗口滚动延迟渲染模块的体验,也可以通过空闲时间预渲染来优化。图43.2重复打包优化微前端的一个问题是如何处理一些公共的能力。“微”带来自由,但过多的自由可能是重复的、难以规范的需求。比如一些基础的UI,动态请求的能力等等,每个模块都是单独打包的,线上运行的时候会成为负担。还有一些公共能力抽取的“套路”:Entry提供的全局实例。你的顶层APP可以在每个实例化的子模块中注入一个全局的能力引用,提供一些几乎每个模块都需要用到的能力,比如Ajax请求能力,业务监控能力等,好处是你可以控制一些底层的能力,但缺点是子模块和入口之间的耦合会更深。如果你有一个子模块需要应用在不同的入口APP中,那么对于每一个APP,你可能都需要一个适配层来屏蔽差异。提取公共内容包装。最常见的公共内容是polyfill。每个模块使用“usage”(https://babeljs.io/docs/en/ba...)单独打包。优点是开发和线上环境一致。缺点是polyfill内容会重复度很高,如图5所示。图5的优化思路也很明显。polyfill只以“入口”的形式引入一次,如图6所示。图6但是如何保证这个publicpolyfill也是按需优化的呢?有一些方式,例如:根据运行时环境实时加载polyfill,比如polyfill.io。对于较重的计划,个人觉得收益不一定很划算。webpack@5的“模块联合”。目前还处于beta阶段,生产环境建议谨慎(如果你的项目升级webpack到5后还能运行的话)。同意polyfill的白名单/黑名单。这是我们项目中使用的最后一个解决方案。原因在于它重量轻且安全,从工程角度来说也算“性价比高”。当然,这种方法会有问题。单独开发的模块如何保证使用的语法不会超出白名单?我们的解决方案是:为了对灵活性有一定的规范约束,我们需要使用我们封装好的devcliworkspace来开发微服务子模块,并在开发过程中完成App级的变量注入和语法校验。在发布和编译阶段也会有相应的控制,所以“开发环境工具”也是微服务改造的利器。以上是我们在微前端工程技术选型和架构设计方面的一些思考和经验,限于篇幅不再展开。3.3FreedomvsSpecification最后总结一下我个人对微前端的一些看法。灵活自由可能是很多大型项目和团队设计的期望。但是我觉得一个APP在灵活的同时,控制和规范也很重要。大家可能希望系统在数据上可以标准化,交互视觉上高度一致,体验好,性能好。那么在向微服务转型之初,就需要提前设计规范控制。否则,在微前端热潮过后不久,可能又会出现“微前端治理”相关的讨论。毕竟,从“限制”到“自由”容易,从“自由”到“限制”难。参考微前端:https://martinfowler.com/arti...@babel/preset-envBabel:https://babeljs.io/docs/en/ba...Polyfill.io:http://polyfill。io/模块联盟|webpack:https://webpack.js.org/concep...嘉宾介绍:马海娜,百度商业平台研发部高级前端研发工程师,主要负责广告托管业务的前端架构。专注于在快速迭代的业务中打造高效、可扩展、体验良好的前端业务架构。我喜欢玩代码和猫。推荐阅读:|百度商用大规模高性能全息日志检索技术揭晓|快编——助力杜卡智能编辑提升效率|短视频个性化推送工程改进之路------------END----------百度极客说,百度官方技术公众号上线啦!技术干货·行业资讯·在线沙龙·行业会议招聘信息·介绍资料·技术书籍·百度周边欢迎各位同学关注