本系列文章从一个全新的视角来思考web性能优化与前端工程之间的关系,通过解读百度前端集成解决方案小组(F.I.S)在打造高性能前端架构并统一百度40多条前端产品线的过程中所经历的技术尝试,揭示前端性能优化在前端架构及开发工具设计层面的实现思路。
静态资源管理与模板框架
让我们再来看看前面的优化原则表还剩些什么:
优化方向 |
优化手段 |
请求数量 |
合并脚本和样式表,拆分初始化负载 |
请求带宽 |
移除重复脚本 |
缓存利用 |
使Ajax可缓存 |
页面结构 |
将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出 |
很不幸,剩下的优化原则都不是使用工具就能很好实现的。或许有人会辩驳:“我用某某工具可以实现脚本和样式表合并”。嗯,必须承认,使用工具进行资源合并并替换引用或许是一个不错的办法,但在大型web应用,这种方式有一些非常严重的缺陷,来看一个很熟悉的例子:
某个web产品页面有A、B、C三个资源
工程师根据“减少HTTP请求”的优化原则合并了资源
产品经理要求C模块按需出现,此时C资源已出现多余的可能
C模块不再需要了,注释掉吧!但C资源通常不敢轻易剔除
不知不觉中,性能优化变成了性能恶化……
事实上,使用工具在线下进行静态资源合并是无法解决资源按需加载的问题的。如果解决不了按需加载,则势必会导致资源的冗余;此外,线下通过工具实现的资源合并通常会使得资源加载和使用的分离,比如在页面头部或配置文件中写资源引用及合并信息,而用到这些资源的html组件写在了页面其他地方,这种书写方式在工程上非常容易引起维护不同步的问题,导致使用资源的代码删除了,引用资源的代码却还在的情况。因此,在工业上要实现资源合并至少要满足如下需求:
- 确实能减少HTTP请求,这是基本要求(合并)
- 在使用资源的地方引用资源(就近依赖),不使用不加载(按需)
- 虽然资源引用不是集中书写的,但资源引用的代码最终还能出现在页面头部(css)或尾部(js)
- 能够避免重复加载资源(去重)
将以上要求综合考虑,不难发现,单纯依靠前端技术或者工具处理是很难达到这些理想要求的。现代大型web应用所展示的页面绝大多数都是使用服务端动态语言拼接生成的。有的产品使用模板引擎,比如smarty、velocity,有的则干脆直接使用动态语言,比如php、python。无论使用哪种方式实现,前端工程师开发的html绝大多数最终都不是以静态的html在线上运行的。
接下来我会讲述一种新的模板架构设计,用以实现前面说到那些性能优化原则,同时满足工程开发和维护的需要,这种架构设计的核心思想就是:
基于依赖关系表的静态资源管理系统与模板框架设计
考虑一段这样的页面代码:
根据资源合并需求中的第二项,我们希望资源引用与使用能尽量靠近,这样将来维护起来会更容易一些,因此,理想的源码是:
当然,把这样的页面直接送达给浏览器用户是会有严重的页面闪烁问题的,所以我们实际上仍然希望最终页面输出的结果还是如最开始的截图一样,将css放在头部输出。这就意味着,页面结构需要有一些调整,并且有能力收集资源加载需求,那么我们考虑一下这样的源码:
在页面的头部插入一个html注释“<!--[CSS LINKS PLACEHOLDER]-->”作为占位,而将原来字面书写的资源引用改成模板接口(require)调用,该接口负责收集页面所需资源。require接口实现非常简单,就是准备一个数组,收集资源引用,并且可以去重。最后在页面输出的前一刻,我们将require在运行时收集到的“A.css”、“B.css”、“C.css”三个资源拼接成html标签,替换掉注释占位“<!--[CSS LINKS PLACEHOLDER]-->”,从而得到我们需要的页面结构。
经过fis团队的总结,我们发现模板层面只要实现三个开发接口,既可以比较完美的实现目前遗留的大部分性能优化原则,这三个接口分别是:
- require(String id):收集资源加载需求的接口,参数是资源id。
- widget(String template_id):加载拆分成小组件模板的接口。你可以叫它为load、component或者pagelet之类的。总之,我们需要一个接口把一个大的页面模板拆分成一个个的小部分来维护,最后在原来的大页面以组件为单位来加载这些小部件。
- script(String code):收集写在模板中的js脚本,使之出现的页面底部,从而实现性能优化原则中的“将js放在页面底部”原则。
实现了这些接口之后,一个重构后的模板页面的源代码可能看起来就是这样的了:
而最终在模板解析的过程中,资源收集与去重、页面script收集、占位符替换操作,最终从服务端发送出来的html代码为:
不难看出,我们目前已经实现了“按需加载”,“将脚本放在底部”,“将样式表放在头部”三项优化原则。
前面讲到静态资源在上线后需要添加hash戳作为版本标识,那么这种使用模板语言来收集的静态资源该如何实现这项功能呢?答案是:静态资源依赖关系表。
假设前面讲到的模板源代码所对应的目录结构为下图所示:
那么我们可以使用工具扫描整个project目录,然后创建一张资源表,同时记录每个资源的部署路径,可以得到这样的一张表:
基于这张表,我们就很容易实现 {require name=”id”} 这个模板接口了。只须查表即可。比如执行{require name=”jquery.js”},查表得到它的url是“/jquery_9151577.js”,声明一个数组收集起来就好了。这样,整个页面执行完毕之后,收集资源加载需求,并替换页面的占位符,即可实现资源的hash定位,得到:
接下来,我们讨论如何在基于表的设计思想上是如何实现静态资源合并的。或许有些团队使用过combo服务,也就是我们在最终拼接生成页面资源引用的时候,并不是生成多个独立的link标签,而是将资源地址拼接成一个url路径,请求一种线上的动态资源合并服务,从而实现减少HTTP请求的需求,比如:
这个“/combo?files=file1,file2,file3,…”的url请求响应就是动态combo服务提供的,它的原理很简单,就是根据get请求的files参数找到对应的多个文件,合并成一个文件来响应请求,并将其缓存,以加快访问速度。
这种方法很巧妙,有些服务器甚至直接集成了这类模块来方便的开启此项服务,这种做法也是大多数大型web应用的资源合并做法。但它也存在一些缺陷:
- 浏览器有url长度限制,因此不能无限制的合并资源。
- 如果用户在网站内有公共资源的两个页面间跳转访问,由于两个页面的combo的url不一样导致用户不能利用浏览器缓存来加快对公共资源的访问速度。
对于上述第二条缺陷,可以举个例子来看说明:
- 假设网站有两个页面A和B
- A页面使用了a,b,c,d四个资源
- B页面使用了a,b,e,f四个资源
- 如果使用combo服务,我们会得:
- A页面的资源引用为:/combo?files=a,b,c,d
- B页面的资源引用为:/combo?files=a,b,e,f
- 两个页面引用的资源是不同的url,因此浏览器会请求两个合并后的资源文件,跨页面访问没能很好的利用a、b这两个资源的缓存。
很明显,如果combo服务能聪明的知道A页面使用的资源引用为“/combo?files=a,b”和“/combo?files=c,d”,而B页面使用的资源引用为“/combo?files=a,b”,“/combo?files=e,f”就好了。这样当用户在访问A页面之后再访问B页面时,只需要下载B页面的第二个combo文件即可,第一个文件已经在访问A页面时缓存好了的。
基于这样的思考,fis在资源表上新增了一个字段,取名为“pkg”,就是资源合并生成的新资源,表的结构会变成:
相比之前的表,可以看到新表中多了一个pkg字段,并且记录了打包后的文件所包含的独立资源。这样,我们重新设计一下{require name=”id”}这个模板接口:在查表的时候,如果一个静态资源有pkg字段,那么就去加载pkg字段所指向的打包文件,否则加载资源本身。比如执行{require name=”bootstrap.css”},查表得知bootstrap.css被打包在了“p0”中,因此取出p0包的url“/pkg/utils_b967346.css”,并且记录页面已加载了“bootstrap.css”和“A/A.css”两个资源。这样一来,之前的模板代码执行之后得到的html就变成了:
css资源请求数由原来的4个减少为2个。
这样的打包结果是怎么来的呢?答案是配置得到的。
我们来看一下带有打包结果的资源表的fis配置:
我们将“bootstrap.css”、“A/A.css”打包在一起,其他css另外打包,从而生成两个打包文件,当页面需要打包文件中的资源时,模块框架就会收集并计算出最优的资源加载结果,从而解决静态资源合并的问题。
这样做的原因是为了弥补combo在前面讲到的两点技术上的不足而设计的。但也不难发现这种打包策略是需要配置的,这就意味着维护成本的增加。但好在它有两个优势可以一定程度上弥补这个问题:
- 打包的资源只是原来独立资源的备份。打包与否不会导致资源的丢失,最多是没有合并的很好而已。
- 配置可以由工程师根据经验人工维护,也可以由统计日志生成,这为性能优化自适应网站设计提供了非常好的基础。
关于第二点,fis有这样辅助系统来支持自适应打包算法:
至此,我们通过基于表的静态资源管理系统和三个模板接口实现了几个重要的性能优化原则,现在我们再来回顾一下前面的性能优化原则分类表,剔除掉已经做到了的,看看还剩下哪些没做到的:
优化方向 |
优化手段 |
请求数量 |
拆分初始化负载 |
缓存利用 |
使Ajax可缓存 |
页面结构 |
尽早刷新文档的输出 |
“拆分初始化负载”的目标是将页面一开始加载时不需要执行的资源从所有资源中分离出来,等到需要的时候再加载。工程师通常没有耐心去区分资源的分类情况,但我们可以利用组件化框架接口来帮助工程师管理资源的使用。还是从例子开始思考:
模板源代码
在fis给百度内部团队开发的架构中,如果这样书写代码,页面最终的执行结果会变成:
模板运行后输出的html代码
fis系统会分析页面中require(id)函数的调用,并将依赖关系记录到资源表对应资源的deps字段中,从而在页面渲染查表时可以加载依赖的资源。但此时dialog.js是以script标签的形式同步加载的,这样会在页面初始化时出现资源的浪费。因此,fis团队提供了require.async的接口,用于异步加载一些资源,源码修改为:
这样书写之后,fis系统会在表里以async字段来标准资源依赖关系是异步的。fis提供的静态资源管理系统会将页面输出的结果修改为:
dialog.js不会在页面以script src的形式输出,而是变成了资源注册,这样,当页面点击按钮触发require.async执行的时候,async函数才会查表找到资源的url并加载它,加载完毕后触发回调函数。
到目前为止,我们又以架构的形式实现了一项优化原则(拆分初始化负载),回顾我们的优化分类表,现在仅有两项没能做到了:
优化方向 |
优化手段 |
缓存利用 |
使Ajax可缓存 |
页面结构 |
尽早刷新文档的输出 |
剩下的两项优化原则要做到并不容易,真正可缓存的Ajax在现实开发中比较少见,而尽早刷新文档的输出的情况facebook在2010年的velocity上提到过,就是BigPipe技术。当时facebook团队还讲到了Quickling和PageCache两项技术,其中的PageCache算是比较彻底的实现Ajax可缓存的优化原则了。fis团队也曾与某产品线合作基于静态资源表、模板组件化等技术实现了页面的PipeLine输出、以及Quickling和PageCache功能,但最终效果没有达到理想的性能优化预期,因此这两个方向尚在探索中,相信在不久的将来会有新的突破。
本文只是将这个领域中很小的一部分知识的展开讨论,抛砖引玉,希望能为业界相关领域的工作者提供一些不一样的思路。欢迎关注fis项目,对本文有任何意见或建议都可以在fis开源项目中进行反馈和讨论。
相关推荐
前端页面静态资源管理** 在SpringBoot应用中,静态资源(如CSS、JavaScript、图片等)通常放在`src/main/resources/static`目录下。当请求URL匹配到这些静态资源路径时,SpringBoot会自动处理并返回。在Thymeleaf...
本主题聚焦于“各类商城前端静态界面模板”,这些模板是预设计的、可重复使用的网页布局,专为电子商务平台定制,以提供吸引人的用户体验和高效的功能。以下是关于这个主题的详细知识点: 1. **前端开发基础**:...
《xadmin前端静态页面模板详解》 在当前的Web开发领域,前端框架和工具的使用极大地提升了开发效率和用户体验。其中,“xadmin”是一个基于Django管理后台的可定制、可扩展的管理界面,它提供了丰富的自定义选项,...
为了提升用户体验,静态网页后台框架模板还会进行性能优化,包括代码压缩、图片优化、异步加载等策略。例如,通过concat和uglify工具合并并压缩CSS和JS文件,减少HTTP请求数量;利用CDN(内容分发网络)加速静态资源...
【标题】"PC端电脑端商城模板前端静态页面商城模板"是一个专为个人计算机(PC)设计的在线购物平台的前端界面。这个模板是静态的,意味着它主要由HTML、CSS和JavaScript等前端技术构建,不包含服务器端的动态交互...
在“文章:前端框架——Vue中所需要的静态资源.rar”这个压缩包中,很可能是为了配合介绍Vue.js开发时所需的静态资源,包括但不限于HTML模板、CSS样式文件、JavaScript代码、图片以及其他辅助开发的工具或库。...
【标题】:“漂亮的通用后台 html模板 静态模板 后台模板” 【描述】:这个资源是一个设计精美、适用于各种...同时,也可以引入前端框架(如React、Vue、Angular等)来实现单页应用(SPA),提高开发效率和应用性能。
在前端开发中,JavaScript作为主要的开发语言,其性能优化是至关重要的,特别是在处理大量静态资源时。静态资源的压缩可以显著减少文件大小,提高页面加载速度,进而提升用户体验。本篇文章将深入探讨JavaScript静态...
10. **性能优化**:为了提升用户体验,模板设计应考虑性能优化,如懒加载、代码分割、静态资源CDN托管等。 11. **国际化支持**:多语言后台系统需要支持国际化,使用i18n库(如React-intl或vue-i18n)可实现这一点...
"COE优化前端模板"是一个专门针对前端开发的资源包,旨在提高企业网站构建的效率和性能。这个模板集合了经过优化的代码和设计元素,为开发者提供了一个基础框架,以便快速搭建功能完整且用户体验良好的网站。下面...
在本压缩包“前端前端静态模板-个人微博-学生作业毕设实训素材.zip”中,包含了一套完整的前端应用源码,特别适用于学生进行毕业设计和论文撰写时作为实训素材。这个项目是一个个人微博的实现,它展示了如何构建一个...
【雍达商城模板】是一个专为电子商务平台设计的纯静态资源模板,旨在提供一个完整的前端展示界面和后台管理界面的框架。这个模板适用于那些希望快速搭建一个功能齐全、外观美观的在线商店的企业或个人开发者。由于是...
总之,这两款后台管理系统的静态模板页面是前端开发的重要资源,它们可以极大地加速项目进度,减少重复工作,为构建专业、美观且实用的人力资源管理系统提供基础。通过合理的定制和后端集成,能够实现高效、稳定的...
4. **dist**:这是编译后的资源文件存放目录,通常包含已经处理过的CSS、JavaScript和其他静态资源,可以直接在项目中使用。 5. **js**:JavaScript文件夹包含了MUI的核心脚本和组件实现,这些文件负责实现MUI的...
这个名为“前端前端静态模板-大学-学生作业毕设实训素材.zip”的压缩包提供了一个完整的前端应用源码开发Demo,非常适合大学生进行毕业设计学习。它涵盖了从基础到进阶的前端技术,可以帮助学生深入理解和实践前端...
9. **性能优化**: 对于前端应用,加载速度和资源管理同样重要。这包括压缩和合并CSS/JS文件、利用CDN加速、减少HTTP请求、使用懒加载等技术。 10. **响应式图标**: 使用SVG图标或Font Awesome等图标字体,可以保证...
在IT行业中,静态资源管理...总之,静态资源管理是优化Web性能的关键环节,涉及资源的存储、分发、缓存等多个方面。通过合理的系统设计和工具利用,我们可以实现高效的静态资源管理,从而提升整个Web应用的效率和质量。
H+ 后台主题UI框架4.1.0(牛B的收费 的框架) H+是一个完全响应式,基于Bootstrap3.3.6最新版本开发的扁平化主题,她采用了主流的左右两栏式布局,使用了Html5+CSS3等现代技术,她提供了诸多的强大的可以重新组合的UI...