当前位置: 首页 > 后端技术 > PHP

一推前端微服务:突破传统SPA瓶颈

时间:2023-03-29 17:12:28 PHP

在目前的前端领域,单页应用(SPA)非常流行。随着时间的推移和应用功能的丰富,这些应用变得越来越庞大,也越来越难以维护。于是“微前端”的概念应运而生。“微前端”来自于2016年的ThoughtWorks技术雷达,是指将一个项目拆分成可以独立运行、独立开发、独立部署的前端微应用。这些微应用程序可以并行开发并共享组件。微前端的实现方式也有很多种:服务器路由重定向、组合多个独立的应用程序、iFrame、通过WebComponents构建等。微前端的相关概念在一些项目中也有应用(基于Vue框架)在个推前端。之所以强调前端微服务,是因为任何技术或概念都有其适用场景,微前端也不例外。对于中小型项目,使用微前端会比较复杂,因为微前端对项目开发并不友好。以个推的业务场景为例:项目A行有10-20个模块,每个模块有5-15个页面。而A项目线的所有产品都是基于这些模块自由组合的,也就是说:如果按照普通的SPA开发路线,我们可能需要很多branch或者repo来维护这些产品,因为每个产品所需要的模块版本略有不同。演进过程为了避免分支混乱、项目大、代码冲突、打包麻烦等一系列问题,我们趁着后端微服务的拆分,开始调整前端的开发部署A项目线的方法。一开始我们并没有采用前端微服务的开发部署方式,而是先将项目中的各个模块拆分成很多独立的repos,避免团队中的工程师在开发过程中需要拉取代码解决冲突开发流程(一个模块,一次迭代,一般由1-2人完成)。因此,我们的问题是:模块拆分后,如何解决项目中的开发、打包部署、公共依赖和组件复用等问题。拆分模块项目的目录结构大致如下:将项目中的main.js入口和公共组件分离到一个单独的项目中,这里称为主项目。由于每个子模块项目只有当前模块的页面代码、路由、菜单配置,无法直接开发dev子模块。所以我们开发了一个名为lego的CLI工具。开发者在开发模块时,只需要在模块根目录下运行“legodev”命令,即可启动当前模块的开发服务,开发好的模块会发布到自己的npm源进行版本管理。如果只是拆分模块,那么开发者在独立开发模块时需要为模块配置相应的运行环境,模块之间的相互调用也很麻烦。“lego”CLI解决了模块运行环境的问题。运行环境由CLI自动加载。模块开发者只需要关注模块本身的业务逻辑即可。此外,该模块还提供了一个config.js文件,可以从npm源码中配置其他依赖的模块,帮助开发者在开发过程中更方便的调用不同的模块。使用“legodev”命令还支持引入“@self/”路径,“@self/”路径指向当前模块的src/文件夹,“@/”指向当前模块的src/文件夹主工程,从而避免模块开发导入路径出现问题。通过模块的拆分和改造,解决了项目庞大、分支混乱的问题,代码冲突的情况也明显减少。但是对于单个产品的打包部署,我们还是需要从各个模块中获取源码,通过主工程打包成一个独立的产品。哪怕只修改某个模块中的一行代码,整个系统都需要重新打包,整个打包后的产品也需要进行回归测试。针对这个问题,我们想到了能不能直接把模块打包成应用程序调用。模块打包和独立部署我们的理想情况是:每个模块都可以独立开发和部署,然后由产品自己决定加载哪些模块。效果如下:因此,模块打包后,入口(index.js)可以根据需要注入到主工程中,由主工程加载(路由)。一方面,对于使用webpack打包的项目,代码是基于CommonJS规范的。由于umd规范兼容CommonJS规范,开发者可以直接在项目中使用基于umd规范打包的模块。另一方面,vue-router和vuex库都支持动态加载addRouter/registerModuleAPI。我们采用了两种方案:第一种:在主工程中初始化vue实例时,将vue-router和vuex的实例暴露在全局(window)中,在routing中存放子模块的路由前缀项目中的表。当页面跳转到匹配子模块的路由时,主工程加载子模块umd.js文件并动态注册router和vuex模块,然后渲染页面。简单DEMO如下图所示:类型2:先加载子模块umd.js文件,将子模块的路由和vuex信息暴露给全局(window)。Vue实例从窗口中获取路由信息、vuex模块、菜单信息等,形成一个独立的产品。简单的DEMO如下图所示:当然,两种方案都有一定的不足:第一种方案:首先,页面跳转后加载子模块js文件,所以404跳转和路由权限检查会有些问题实施;其次,子模块文件加载前和子模块渲染前会有很长的页面白屏时间。方案二:无论子模块用户是否会访问umd入口文件,都需要提前加载该文件。这就要求入口文件需要足够小,也就是说子模块不能使用min-chunk-size-plugin插件合并chunk,开发者需要使用手写的webpackChunkName或者使用其他工具进行合并。基于VUE-CLI3的实践,Vue-cli3.x对子模块打包提供了更好的支持。使用“vue-cli-servicebuild-target=lib”将子模块代码打包成umd规范格式。但是需要注意以下问题:“--target=lib”的初衷是供发布到npmjs的组件使用,所以打包后的文件是没有hash值的(即使配置了chunkNamevue.config.js)。我们采取的方法是先修改vue-cli-service的源码,再执行乐高脚手架的打包命令。使用“--target=lib”打包子模块时,如果没有配置css-in-js,打包后的css文件中的background-image路径有问题。基于此,我们给出两种解决方案:配置css-in-js,或者修改node_modules中vue-cli-service的源码,然后打包。以上就是一个推动前端微服务开发部署的实际情况。在实践中,我们发现微服务的接入解决了项目中遇到的维护难、产品编译部署麻烦等问题。在模块拆分的时候,我们开发的CLI工具也解决了模块分离开发运行的问题。当然,我们的微服务方案也有局限性。更适用于模块间联系紧密的大型项目,不具备微前端概念中强调的技术独立性和团队代码隔离性。在不久的将来,我们除了继续升级微服务解决方案外,还将接入新的框架来迎接新的挑战。