7月阅读周·前端架构:从入门到微前端 | 架构演进:演进式架构

举报
叶一一 发表于 2024/07/23 15:06:27 2024/07/23
【摘要】 背景去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。没有计划的阅读,收效甚微。新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。这个“玩法”虽然常见且板正,但是有效,已经坚持阅读六个月。已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScri...

背景

去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。

没有计划的阅读,收效甚微。

新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。

这个“玩法”虽然常见且板正,但是有效,已经坚持阅读六个月。

已读完书籍《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》

当前阅读周书籍《前端架构:从入门到微前端》

架构演进:演进式架构

更新

大部分的前端应用并非一直在开发,或者编写后不再维护。它们可能开发一段时间,然后停滞一段时间,再去开发一段时间,再休息一段时间,如此往复地循环。

在修改过程中需要不断地维护,以防止这个应用变成一个遗留的、难以维护的系统。而在不同的升级方式里,我们所要面临的挑战都是不一样的:

  • 依赖升级。
  • 框架升级。
  • 语言升级。

它们是根据各自的更新频率所产生的顺序。

依赖和框架版本升级

我们在选择依赖库的时候,通常会选择那些比较成熟的依赖。成熟的框架、组件、库,往往采用语义化版本的形式来发布。其遵循的版本号格式是:主版本号.次版本号.修订号,如1.2.3中的1是主版本号,2是次版本号,3是修订号。其版本号递增规则如下。

  • 主版本号:当你做了不兼容的API修改。
  • 次版本号:当你做了向下兼容的功能性新增。
  • 修订号:当你做了向下兼容的问题修正。

依照这种形式,在针对依赖的升级时,我们会采取相应的应对措施:

  • 当修订号发生变更时,通常只是进行一些Bug的修复,不需要我们做出响应。
  • 当次版本号发生变更时,往往可能会发生一些API变更,我们要视向下兼容情况来决定是否跟进。
  • 当主版本号变化时,可能有些API已经不存在了,我们需要大量地改动应用。

一旦我们决定对一个项目采取维护模式,就需要制定一些基本的策略,比如:

  • 确认合理的时间间隔,如三个月一次。
  • 定期检查依赖或者使用工具自动检测。
  • 为更新预留时间及精力
  • 准备文档策略,以记录过程中遇到的问题。

语言版本升级

对于前端而言,语言的版本升级,并不会带来太大问题。不论我们使用ES6还是TypeScript,到目前都没有发生严重的升级问题。其最后在浏览器上呈现的方式是一致的,都需要编译为ES5,都需要能兼容当前的主流特性。由于需要向下兼容ES5,在升级上不会出现太多的问题。这一点与后端语言形成了鲜明的对比,在后端语言里,如Python或者Ruby,一旦升级了某个版本,就需要重写应用。

当然,如果我们想从一种语言切换到另外一种语言,如从CoffeeScript切换到TypeScript,或者从ES6切换到TypeScript都不是一件容易的事,这种工作等价于重写应用。

遗留系统重搭

应用迁移可以分为如下几个步骤:

  1. 创建全新的运行环境。
  2. 准备接近线上环境的测试数据,如Staging。
  3. 执行更细粒度的版本管理控制,以方便回滚。如每一次小的变更,每一个新特性的升级,都需要版本管理工具来记录。
  4. 优先升级次要组件的版本,以方便向上兼容。
  5. 逐一升级核心框架,以查找对应的更新日志。
  6. 必要时自己编写依赖。在升级依赖的过程中,我们极有可能遇到的一个问题是,某个依赖不再更新,此时需要自己内联这个依赖。
  7. 清理掉不需要的代码和文件。
  8. 进行完整的用户验收测试。
  9. 上线前使用线上的环境进行预部署。

迁移

迁移与重构、重写最大的不同之处在于,迁移只需要改动少量的代码,就可以使旧的系统恢复生机。不需要深入理解代码中的业务逻辑,只是从架构、框架上略微对系统进行改造,就可以完成目标。此外,我们也不可能花费大量的时间和精力在重写应用上,也没有必要重构原有的代码,只需要让旧的系统集成到新的系统中继续使用即可。

架构迁移的模式

决定了迁移之后,我们可以尝试迁移应用,步骤如下:

  1. 构建和提取基础设施,如组件库、代码库。
  2. 确认用于练手的边缘应用。如果失败了、不合适了,那么可以尝试使用其他模式。
  3. 寻找合适的解耦方式,包含数据、事件等。
  4. 尝试对系统的其他部分进行拆分。
  5. 编写对应的文档及相应的培训。

迁移方式:微前端

迁移到微前端架构的几个步骤:

  • 寻找合适的微前端技术。
  • 确认可行的微前端方案。
  • 尝试使用其中的某些方案。
  • 对比、讨论几种不同方案的利弊。
  • 决定适合当前项目实施的方案。
  • 尝试在一个项目中使用新架构开发。
  • 编写架构决策记录及文档,记录实施过程中常见的问题。
  • 对项目成员进行相关培训。

迁移方式:寻找容器

除了微前端的方式,对于前端项目而言还有一种迁移系统的方式:寻找更大的容器。这种容器视不同的应用场景有不同的实现,如针对浏览器是iframe,针对移动端是App的WebView等。

对于浏览器应用来说,iframe是当前用得比较广泛的前端容器,它可以极大地方便我们迁移旧的前端应用。Web Components作为一个新的组件级容器,可以帮助我们兼容不同的应用和组件。只需要经过略微的修改,便可以将前端应用嵌入其他应用中去。

重构

应用的重构是在软件开发过程中的行为,而不是维护期进行的工作。只有应用还在开发进行中,我们才能深刻体会代码的坏道,以及其带来的软件架构问题。

架构重构

对于多数项目来说,重构是一个不存在的方案,原因也在于此。只有测试才能保证重要的重构能往下进行,否则只能大量依赖于手工测试。而手工测试,也是一种容易出错的方式——作为一个开发人员,我们的测试经验往往并不丰富。因此,不得不依赖于测试人员,让他们帮助我们进行回归测试。

组件提取、函数提取、样式提取

代码的复制、粘贴是我们在前端应用中经常看到的操作。开发人员在编写逻辑的时候往往倾向于直接复制代码,而非重用代码,这两种方式各有利弊。可是如果在代码逻辑上是重复的,就需要对其进行手术,删去重复的代码,以免代码进一步复杂化。回到前端的模式来看这个问题,就是三种(HTML、JavaScript、CSS)类型代码的提取:

  • 组件提取。一个合理的前端应用,包含一个UI组件库。从组件的层面上看,可能出现代码重复的地方在复合组件和业务组件中。对于那些相似功能的组件,我们要决定是否提取出通用的模式,避免在业务变化时进行多处修改。对于复合组件来说,组件提取是在业务发展的过程中呈现的,需要在多次出现后进行模式提取。
  • 函数提取。在我们的代码中往往包含着各种通用的处理逻辑,如统一的HTTP的错误处理,它们可以集中起来进行处理。随着我们的编程经验越来越丰富,在创建一些函数的时候就会自觉地创建xxxUtil.js、xxHelper.js,或者xx.services.ts。
  • 样式提取。样式是笔者一直觉得头疼的内容。哪怕引入一个公共的样式库,我们也只能在样式上尽量统一,统一的颜色、字体大小、border等。但是,在实现业务功能的时候,非统一的样式往往会出现问题,如某一类元素的宽高、元素的定位等。由于它对我们来说并不是那么重要,所以也就不会那么在意。只是在出现需要多处修改的时候,才会重视对样式的重构。

重写

在我们决定重写之前,需要考虑几点要素:

  • 重构、迁移、升级真的不能解决问题吗?
  • 重写的时间预期是多少?重写的时间花费往往比预期更长,比技术上花费的时间更短,但理清业务的时间更长。
  • 能接受重写的成本吗?重写不会对业务带来额外的好处,反而是在浪费时间。
  • 是否整理出完整的功能列表?只有清晰的功能列表,才能保证重写不被阻碍。
  • 产品是否领先于市场?在重写期间,我们的开发速度可能会落后于其他产品。
  • 是否有能力同步维护两个产品和团队?在重写期间,需要在新旧应用里实现同样的功能。
  • 在重写完成之前,是否可能变为遗留系统?要进行合理的技术选型,以避免重写失败。

重新架构

重搭架构

重搭架构,与重构相比,是一种更高阶的应用重构。它从应用的层级上对架构进行重新设计,而非在代码层级上进行改进。比如,结合微前端架构来拆分前端应用,并使用绞杀者模式一点一点地重写应用。

在实际的过程中,是在重新应用先前学习的相应内容:

  • 重新设计构建系统,以支撑新架构。
  • 设计模块化的应用架构,以帮助我们解耦模块。
  • 抽象化组件、提取函数库,以减少重复代码。
  • 采用微前端技术来解耦前端应用。

增量改写

增量改写,即将系统拆分为多个独立模块或应用。当我们决定对应用进行重写的时候,只需要重写其中的一部分,其他功能仍然是正常运行的。而那些需要新功能的部分,又可以运行在新的应用之上。直到有一天,我们重写完所有的模块,系统相当于重写完成了。这种模式,又称为绞杀者模式,它可以在满足系统演进的情况下实现如下目标:

  • 大幅度减少对于业务的影响,降低了风险。
  • 可以随时停止重写,而不影响用户使用。
  • 用户、客户可以随时看到网站的变化,带来更多的价值。
  • 为开发人员带来更多的自由和乐趣。
  • 每个模块重写时,只关心相关部分的业务,而非所有的业务。

总结

从头写一个应用是相当有挑战的一件事。而事实是,这种挑战不一定存在,或者只是存在搭建脚手架和架构的挑战。在经历了这个过程之后,我们就会觉得项目索然无味。事实上,此时此刻,才是我们真正挑战自己的时候。见证一个系统的成长,见证它的变化——好与坏,同时不断寻找提升自己的可能性。


作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏️ | 留言📝

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。