攻略:前端发展迅猛,从最初的静态页面到JavaScript,再从PC到移动端。随着大前端的复杂度不断增加,很多公司开始将前后端架构设计分开。.我们来看看,什么是前端架构设计?曾经非常简单的前端架构开发存在哪些问题?如何解决前端代码量巨大、跨团队协作效率、代码耦合、技术栈落后等问题??Part11.什么是前端架构?我相信很多人对前端架构这个词有不同的定义;按照拆分词的解释,我理解为“前端”+“架构”。前端是指Web端的前端页面,包括网页的内容、样式、脚本等。这三者通常封装在组件中,可能是模板引擎的文件模块,也可能是MVVM框架中的组件。“建筑”更好理解。建筑一词来源于建筑行业,可以理解为房屋的整体结构和框架。结合前端和架构的概念,“前端架构”可以理解为对网页组件的抽象和组织。并且因为每个公司的业务不同,所以每个公司前端架构的开发也不一样。这里我以百度移动端的经典搜索场景为例。希望能在百度移动端架构的演进过程中找到一些共性。问题。Part2二、百度手机背景及问题为什么以百度为例?因为百度是国内搜索引擎的老大,一直处于行业领先地位。据statcounter前瞻产业研究院2019年中国搜索引擎行业数据,百度搜索占全球搜索引擎市场份额的12.3%,仅次于谷歌排名第二。所以以百度为例更具有代表性。言归正传,打开百度App,你会发现百度前端直接分为首页和搜索结果页。搜索结果页是搜索的主要入口,每天承载着数十亿的流量。不仅如此,搜索结果页面承载了很多产品线的需求和下游模块的运行时。每年内部研发人员提供500多个产品需求,为下游十几个模块提供基础库和运行时。甚至还有后端协作。从图1中,我们可以看到结果页面的整体结构。整体架构设计存在以下问题:细分业务线多,单个库代码量大;平均每月提交200+,代码30000+行;80多名开发人员在同一个代码库中进行开发;没有人能够完全掌握模块的整体技术。因此,梳理出了三个问题:1、人员职责不明确,单个模块同时承担了多个团队的职责。咖喱;其他:结果列表、用户反馈、搜索推荐、体验日志、速度日志、计费逻辑……2、代码耦合严重易错,代码逻辑脆弱;结构死板,不易增加新功能;难以重复使用。3、技术栈落后,页面没有组件化。没有Vue,没有React,仍然使用Smarty模板;没有Node.js支持。Smarty模板强烈依赖于PHP环境;工具链落后了。没有TypeScript,就没有Jest。这三个问题最终会影响研发效率和产品质量。那么百度具体是怎么做的呢?架构优化的目标只有两个,一是满足业务需求,二是能够在技术上灵活升级框架和工具(也能持续满足业务需求)。根据“满足业务需求”的目标,百度在内部制定了三个层次的方向。(如图2所示)底层的baselayer紧贴社区,因为根据内部研究,造轮子的成本并不高,但是维护这些轮子的成本极高。如果想迭代的更快,建议贴近社区,使用一些开源的东西或者贡献给开源。主要是解决技术栈落后、职责不清等问题。中间层是一个独立的模块,主要是为了解决前面提到的职责不清、交付效率低下的问题。主要目的是解决职责不清、交付效率低等问题。顶层是组件化。在独立模块的基础上进行组件化,加速业务迭代。Part33.如何解决问题?按照这里说的方向和目标,如何结合百度自己的架构去实现呢?首先回顾一下百度的架构,如下图3所示。这里有两条日志,说明同一套代码需要分两部分维护;除了重复之外,它们的差异还会为后续维护带来更高的成本;底层的HHVM+PHP和社区拥抱Node.js会有更多的冲突。于是,百度同学调整了目标架构,如图4所示。从图4中可以看出:日志、搜索框、相关搜索、绩效管理等被分离成单独的模块,并有专门的同学维护和独立迭代;前后端之间加了一层渲染;业务代码与后端逻辑分离;在底部添加了Node.js机制。目标和方向确定之后,就看如何落实了。对于小型图书馆,从头开始构建架构就足够了;但对于百度来说,执行起来也有难度。不仅要考虑平滑迁移、性能不下降,还要考虑长期可维护性、安全性、跨平台等。上面说了,基本思路是按照基础架构、模块拆分、组件化的步骤进行;基础设施是业务模块划分的关键,完善的自动化和工具链是模块化的前提;模块化拆分可以为业务与团队提供更好的水平扩展能力;在模块化的基础上,可以进一步在模块内部构建组件化的解决方案,加速业务迭代。基础设施需要注意的有:TypeScript:大型项目必备,提前发现问题;也是跨平台的基础;持续集成:确保每次变更都增加新特性和修复问题而不引入新问题;单元测试:在重构开始时引入,以帮助防止退化和辅助设计。模块化拆分需要注意的事情包括:识别和定义业务边界,将统一仓库划分为若干个独立的小仓库;构建子模块自动化机制,自主选择、开发、上线。注意:模块化拆分不是技术问题,而是业务问题。只有按照业务和产品垂直划分,才能达到解耦和独立迭代的目的。否则,只是将耦合的代码形式化地拆分,会造成更大的维护和沟通成本。由于在业务模块内选择组件,组件化方案相对自由。只需要不严重影响性能,能够平滑过渡即可。Part4四、落地方案1、模块化的具体落地方案,我们同样用一张图(图5)来表示。可以看到分为服务器端和浏览器端两部分。服务器端关注的是业务模块的划分和运行时的结合;浏览器端关注的是依赖关系的解决以及如何支持组件化方案。2、在服务器端,百度将整个大模块拆分成多个独立的业务模块,最终页面由模块组成。这就需要业务模块有统一的接口,即上图所示的Molecule接口,定义了模块如何渲染,有哪些依赖等信息。因为渲染过程被封装在模块内部,所以整个架构可以支持多种语言和多种框架。相信你也发现了Molecule和微服务非常相似。它们的关键区别在于,微服务的服务之间通过IPC进行互操作,每个服务都可以独立伸缩,独立部署;而Molecule的模块存在于同一个进程中。尽管存在这种差异,分子仍然可以实现与微服务几乎相同的功能,如图6所示。图7是具体业务模块的服务器入口文件,其中ToptipController实现了Molecule提供的控制器接口;该接口需要渲染函数,接受字典类型的数据,返回渲染后的页面内容。由调用者决定如何组装页面。以上是业务模块提供者的接口。此外,Molecule机制还为调用者(组装最终页面的一方)提供了一个方便的接口,通过传入子模块名称和参数,可以在运行时引入和渲染子模块。整个机制的原理很简单,但是在实际使用中,可能需要引入命名空间,考虑模块版本等问题。3.客户端如何运行客户端?我们还需要运行每个模块的浏览器端组件。难点在于组件之间的依赖关系和代码共享。这些组件可能在不同的代码库中,属于不同的业务,所以我们需要一种非常松散的依赖方式。这里我们介绍一个依赖注入容器(图8)。一般情况下,将框架逻辑和通用工具封装成具体的服务,供业务模块使用。每个业务模块都需要定义它所依赖的服务。图9形象地描述了组件、服务和容器之间的关系。其中,蓝色代表一个具体的Service,其他颜色代表独立的业务模块。运行时容器会负责解析各个业务模块的依赖关系,将这些业务模块组装起来,最终得到一个可交互的网页。注意:业务模块是独立的,一个业务模块不能依赖其他业务模块,只能依赖通用Service。因此,如果业务模块之间存在产品逻辑耦合,可能需要一个通用的Service作为媒介,比如在容器中提供一个EventService作为事件总线。图10是业务模块的客户端代码示例。它的依赖通过构造函数声明,运行时容器负责依赖的创建,业务模块只需要关心依赖的使用即可。正是使用和创建操作的分离,实现了业务模块之间、业务模块与页面框架之间的解耦,实现了独立的开发和测试。以上就是模块拆分的整体方案。回顾一下:在服务端,通过一个叫做Molecule的接口来组合业务模块;在浏览器端,通过一个DI容器来解决依赖,启动所有的业务模块。4、组件化组件化方案直接影响业务开发的效率。也就是说,组件化方案在某种程度上决定了业务同学写什么代码。组件化还可以帮助解决责任不明确等问题。我们选择的组件化方案是San,你也可以根据业务或者喜好选择Vue或者React。业务代码的迁移比较直观,就是从Smarty模板到San组件,从HTML字符串拼接到具有业务语义的组件结构。接下来重点关注组件化方案的两个关键技术问题,跨平台和页面性能。1.跨平台我们有很多业务代码,几千个模板,几十万行代码。这些代码需要迁移到组件解决方案中,并且我们必须确保整个过程中后端从PHP迁移到Node.js,业务代码不需要重新开发。那么业务组件如何跨平台呢?关键在于抽象。高级语言:我们的业务代码需要使用足够高级的语言。这里我们使用TypeScript,可以翻译到多个平台;依赖倒置:我们的高层业务模块不应该依赖于具体的底层模块,而是只依赖于接口,这样就可以在不同的平台上替换不同的底层实现;抽象接口:最后,Molecule接口的设计要足够简单;Molecule接口不依赖于底层实现,比如PHPAPI的具体实现。做到以上几点就可以完成平稳过渡。这个过程分为三个阶段(图11)。2、页面性能前端框架的引入通常意味着体量的增加和性能的下降,而性能直接影响搜索收入,因此页面性能是项目成败的关键。如果性能会比模板引擎的性能差,那么这个项目很可能会死掉。如何保证页面性能?关注两个优化点。引入***:引入服务端渲染,首屏性能可显着提升;***优化:传统的***性能还需要进一步优化。介绍***。为了说明***的重要性,见图12。浏览器加载页面分四个步骤:请求页面、请求外部链接资源、执行脚本、渲染组件。从图中的对比可以看出,在CSR的前三个步骤中,用户是看不到页面的;引入***后,用户可以在第二步看到请求的页面。***它最大的用途之一是增加首屏时间。***优化。仅仅引入***并不能使性能达到预期,因为相比于模板引擎直接拼接字符串,***需要递归渲染组件,尤其是递归VNode非常耗时。在这方面,San***相比Vue/React***做了很多改进。转到VNode:编译时递归VNode,运行时只做HTML拼接;编译时计算:尽可能将工作移至编译时,减少运行时开销;图13显示了最终San***和Smarty模板在改造前的引擎性能对比。可以看到Smarty和San***在不同场景下的表现是不一样的,因为他们渲染的非常不一样。最终搜索结果页组件化***上线后,在线实验结果显示,比Smarty快10ms左右。这已经是很好的效果了。我们使用组件化在性能方面击败了模板引擎。Part5五、结语针对百度搜索引擎在架构演进过程中遇到的问题,相信在其他领域也会有一些共性。通过百度的解决思路,希望对做前端架构的你有所启发。
