前端架构设计是什么?百度移动端背景及问题分析
优采云 发布时间: 2021-06-04 00:09
前端架构设计是什么?百度移动端背景及问题分析
指南:前端发展迅速,从最初的静态页面到JavaScript,再从PC端到移动端。随着大前端的复杂度不断提高,很多公司已经开始前后端分离,剥离前后端架构设计。那么我们来看看,什么是前端架构设计?曾经非常简单的前端架构开发存在哪些问题,如何解决前端代码庞大、跨团队协作效率高、代码耦合、技术栈落后的问题?
一、前端架构是什么?
前端架构这个名词,相信很多人都有不同的定义;按照split这个词的解释,我理解为“前端”+“架构”。前端是指网页的前端页面,包括网页的内容、样式、脚本等。这三者通常封装在组件中,可能是模板引擎的文件模块,也可能是MVVM框架中的组件。 “架构”更好理解。建筑一词来源于建筑行业,可以理解为房屋的整体结构和框架。结合前端和架构的概念,“前端架构”可以理解为网页组件的抽象和组织。
因为每个公司的业务不同,所以每个公司的前端架构的开发也不同。在这里,我将使用百度移动端的经典搜索场景来给大家举个例子。希望从百度移动端架构的演进中,发现一些常见的问题。
二、百度移动端背景及问题
为什么以百度为例?正是因为百度是国内搜索引擎的佼佼者,一直处于行业领先地位。据statcounter前瞻产业研究院2019年中国搜索引擎行业统计,百度搜索占全球搜索引擎市场份额的12.3%,位居第二,仅次于谷歌。所以以百度为例更具代表性。
接近主题,打开百度App,你会发现百度的前端直接分为首页和搜索结果页。搜索结果页是搜索的主要入口,每天承载数十亿流量。
不仅如此,搜索结果页面还承载了很多产品线的需求以及下游模块的运行时间。每年,内部研发人员都会提供500多个产品需求,并为十多个下游模块提供基础库和运行时。甚至还有后端协作。从图1中我们可以看到结果页面的整体架构。
△图1:百度搜索结果页面整体结构
对于整体架构设计,存在以下问题:
那么,解决三个问题:
1.人员职责不明确,单个模块同时承担多个团队的职责
2. 代码耦合严重
3. 技术栈落后了。
这三个问题最终会影响到研发效率和产品质量。那么百度具体是怎么做的呢?架构优化只有两个目标,一是满足业务需求,二是能够在技术上灵活升级框架和工具(也可以持续满足业务需求)。根据“满足业务需求”的目标,百度内部制定了三个层次的方向。 (如图2)
△图2:业务需求的三个方向
三、如何解决
根据这里提到的方向和目标,如何整合百度自己的架构?首先,回顾一下百度的架构,如下图 3 所示。
△图3:百度搜索结果页面整体结构
1. 这里有两个日志,意思是同一套代码必须分两部分维护;除了重复,它们的差异还会给后续的维护带来更高的成本;
2. 底层,HHVM+PHP,社区更多拥抱 Node.js 会发生冲突。
于是,百度同学调整了如图4所示的目标结构。
△图4:结果页面的目标结构
如图 4 所示:
日志、搜索框、相关搜索、绩效管理等分离模块,有专门的同学独立维护和迭代;
前后端之间增加了渲染层;分离业务代码和后端逻辑;
在底部添加了 Node.js 机制。
目标和方向确定后,就要看如何去落实了。对于一个小型库,从头开始构建架构就足够了;但对于百度来说,实施起来也有难度。不仅要考虑平滑迁移和性能不降级,还要考虑长期的可维护性、安全性和跨平台性。
如上一篇所述,基本思路是遵循基础架构、模块拆分、组件化的步骤;基础设施是业务模块划分的关键,完善的自动化和工具链是模块化的前提;模块化 可以为业务和团队提供更好的横向扩展能力;在模块化的基础上,可以在模块内进一步构建组件化的解决方案,加速业务迭代。
基础设施需要注意的事项包括:
模块化需要注意的事项包括:
注意:
模块化拆分不是技术问题,而是业务问题。只有按照业务和产品进行垂直划分,才能达到解耦、独立迭代的目的。否则只是对耦合代码的正式拆分,会造成更大的维护和通信成本。
因为组件是业务模块的内部选择,所以组件化方案比较自由。只需要不严重影响性能,并且能够平滑过渡即可。
四、登陆计划
1.模块化
对于具体的登陆方案,我们也用一张图(图片5)来表示,可以看到分为服务器端和浏览器端两部分。
△图5:具体落地方案
2.服务器
百度将整个大模块拆分为多个独立的业务模块,最终页面由模块组成。这就需要业务模块有一个统一的接口,如上图所示的Molecule接口,它定义了模块的渲染方式以及它有哪些依赖。由于渲染过程被封装在模块内部,整个架构可以支持多语言多帧。
相信您也发现了 Molecule 与微服务非常相似。它们之间的关键区别在于微服务的服务通过IPC互操作,每个服务都可以独立扩展和独立部署;而 Molecule 的模块存在于同一进程中。尽管存在这种差异,Molecule 仍然可以实现与微服务几乎相同的功能,如图 6 所示。
△图6:Molecule与微服务对比
图7为具体业务模块的服务器入口文件,其中ToptipController实现了Molecule提供的控制器接口;这个接口需要一个渲染函数,接受一个字典类型的数据,渲染后返回页面的内容。由调用者决定如何组合页面。
△图7:具体业务模块的服务器入口文件
以上是业务模块提供者的接口。此外,Molecule 机制还为调用者(组装最终页面的一侧)提供了一个方便的接口。在需要引入子模块的地方,可以通过传入子模块名称和参数在运行时渲染它们。整个机制的原理很简单,但在实际使用中可能还需要引入命名空间并考虑模块版本。
3.客户端
那么客户端是如何运行的呢?我们还需要运行每个模块的浏览器端组件。难点在于组件之间的依赖和代码共享。这些组件可能位于不同的代码库,属于不同的业务,所以我们需要一个非常松散的依赖方法。
这里引入一个依赖注入的容器(图8),一般来说,框架逻辑和通用工具都被封装成具体的服务供业务模块使用,每个业务模块需要定义自己的依赖为哪个服务。
△图8:客户端设计
图 9 形象地描述了组件、服务和容器之间的关系。
△图9:组件、服务和容器的关系
蓝色代表具体的Service,其他颜色代表独立的业务模块。运行时容器将负责解析各个业务模块的依赖关系,并将这些业务模块组装起来,最终得到一个交互式网页。
注意:
业务模块是独立的。一个业务模块不能依赖其他业务模块,只能依赖一个通用服务。因此,如果业务模块之间存在产品逻辑耦合,则可能需要一个通用的服务作为媒介,例如在容器中提供一个作为事件总线的EventService。
图 10 是业务模块的客户端代码示例。它的依赖是通过构造函数声明的,运行时容器负责依赖的创建,业务模块只需要关心依赖的使用。正是使用和创建操作的分离,实现了业务模块、业务模块和页面框架之间的解耦,实现了独立开发和独立测试。
△图10:业务模块客户端代码示例
以上是模块拆分的整体解决方案。回顾一下:在服务端通过一个叫Molecule的接口来组合业务模块;通过浏览器端的DI容器解决依赖,启动所有业务模块。
4. 组件化
组件化方案直接影响业务开发效率。换句话说,组件化方案在一定程度上决定了商科生写什么样的代码。组件化还可以帮助解决职责不明确等问题。我们选择的组件化解决方案是 San,但您也可以根据您的业务或偏好选择 Vue 或 React。业务代码的迁移比较直观,即从Smarty模板到San组件,从HTML字符串拼接到具有业务语义的组件结构。
接下来,我们将关注组件化解决方案的两个关键技术问题,跨平台和页面性能。
1)cross-platform
我们有大量的业务代码,数千个模板,数十万行代码,这些代码需要迁移到组件化的解决方案中,我们必须确保后端从PHP迁移到Node。 js在整个过程中。业务代码无需重新开发。那么业务组件如何跨平台?关键是抽象。
做到以上几点,才能顺利完成过渡。这个过程分为三个阶段(图11).
△图11:平台转型的三个阶段
2)page 性能
前端框架的引入通常意味着量的增加和性能的下降,而性能直接影响搜索收入。因此,页面性能是项目成败的关键。如果性能比模板引擎差,那么这个项目很可能会死。如何保证页面性能?重点关注两个优化点。
介绍***。说明***的重要性,请看图12。浏览器加载页面分为四个步骤:请求页面、请求外部资源、执行脚本、渲染组件。从图中对比可以看出,CSR的前三步,用户是看不到页面的;并且引入***后,用户可以看到第二步请求返回的页面。 ***其最大的用途之一是增加首次屏幕时间。
△图12:CSR与***对比
*** 优化。引入***并不能达到预期的性能,因为相比模板引擎直接拼接字符串,***需要递归渲染组件,尤其是递归VNode更耗时。在这方面,San *** 相比 Vue/React *** 做了很多改进。
图13展示了最终的San***与改造前的Smarty模板引擎的性能对比。
△图13:最终San***与改造前Smarty模板引擎的性能对比
可以看到Smarty和San***在不同场景下的表现是不同的,因为他们的渲染方式有很大的不同。最终搜索结果页面的组件化***上线后,在线实验效果显示比Smarty快10ms左右。这已经是非常好的效果了。我们使用组件化在性能方面击败了模板引擎。
五、结语
针对百度搜索引擎在结构演进中遇到的问题,相信在其他领域会有一些共性。通过百度的解决思路,希望对做前端架构的你有所启发。
哈特尔
百度高级研发工程师,北京大学物理学士、计算机硕士。 2016年加入百度,负责并参与了百度搜索网页极速浏览框架和MIP开源项目的研发。目前负责搜索结果页面和搜索推荐业务。 LiquidJS 的作者,为 San、Realworld Apps、highlight.js、ALE、HTML5 Standard 等项目做出了贡献。