`
sunxboy
  • 浏览: 2877928 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

前端工程与性能优化(上):静态资源版本更新与缓存

 
阅读更多

每个参与过开发企业级web应用的前端工程师或许都曾思考过前端性能优化方面的问题。我们有雅虎14条性能优化原则,还有两本很经典的性能优化指导书:《高性能网站建设指南》、《高性能网站建设进阶指南》。经验丰富的工程师对于前端性能优化方法耳濡目染,基本都能一一列举出来。这些性能优化原则大概是在7年前提出的,对于web性能优化至今都有非常重要的指导意义。

然而,对于构建大型web应用的团队来说,要坚持贯彻这些优化原则并不是一件十分容易的事。因为优化原则中很多要求是与工程管理相违背的,比如“把css放在头部”和“把js放在尾部”这两条原则,我们不能让团队的工程师在写样式和脚本引用的时候都去修改一个相同的页面文件。这样做会严重影响团队成员间并行开发的效率,尤其是在团队有版本管理的情况下,每天要花大量的时间进行代码修改合并,这项成本是难以接受的。因此在前端工程界,总会看到周期性的性能优化工作,辛勤的前端工程师们每到月圆之夜就会倾巢出动根据优化原则做一次性能优化。

 

本文从一个全新的视角来思考web性能优化与前端工程之间的关系,通过解读百度前端集成解决方案小组(F.I.S)在打造高性能前端架构并统一百度40多条前端产品线的过程中所经历的技术尝试,揭示前端性能优化在前端架构及开发工具设计层面的实现思路。

 

性能优化原则及分类

笔者先假设本文的读者是有前端开发经验的工程师,并对企业级web应用开发及性能优化有一定的思考,因此我不会重复介绍雅虎14条性能优化原则。如果您没有这些前续知识,请移步这里来学习。

首先,我们把雅虎14条优化原则,《高性能网站建设指南》以及《高性能网站建设进阶指南》中提到的优化点做一次梳理,按照优化方向分类,可以得到这样一张表格:

优化方向

 

优化手段

请求数量

合并脚本和样式表,CSS Sprites,拆分初始化负载,划分主域

请求带宽

开启GZip,精简JavaScript,移除重复脚本,图像优化

缓存利用

使用CDN,使用外部JavaScript和CSS,添加Expires头,减少DNS查找,配置ETag,使AjaX可缓存

页面结构

将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出

代码校验

避免CSS表达式,避免重定向

 

表格1 性能优化原则分类

目前大多数前端团队可以利用yui compressor或者google closure compiler等压缩工具很容易做到“精简Javascript”这条原则;同样的,也可以使用图片压缩工具对图像进行压缩,实现“图像优化”原则。这两条原则是对单个资源的处理,因此不会引起任何工程方面的问题。很多团队也通过引入代码校验流程来确保实现“避免css表达式”和“避免重定向”原则。目前绝大多数互联网公司也已经开启了服务端的Gzip压缩,并使用CDN实现静态资源的缓存和快速访问;一些技术实力雄厚的前端团队甚至研发出了自动CSS Sprites工具,解决了CSS Sprites在工程维护方面的难题。使用“查找-替换”思路,我们似乎也可以很好的实现“划分主域”原则。

我们把以上这些已经成熟应用到实际生产中的优化手段去除掉,留下那些还没有很好实现的优化原则。再来回顾一下之前的性能优化分类:

优化方向

优化手段

请求数量

合并脚本和样式表,拆分初始化负载

请求带宽

移除重复脚本

缓存利用

添加Expires头,配置ETag,使Ajax可缓存

页面结构

将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出

 

表格2 较难实现的优化原则

现在有很多顶尖的前端团队可以将上述还剩下的优化原则也都一一解决,但业界大多数团队都还没能很好的解决这些问题。因此,本文将就这些原则的解决方案做进一步的分析与讲解,从而为那些还没有进入前端工业化开发的团队提供一些基础技术建设意见,也借此机会与业界顶尖的前端团队在工业化工程化方向上交流一下彼此的心得。

静态资源版本更新与缓存

如表格2所示,“缓存利用”分类中保留了“添加Expires头”和“配置ETag”两项。或许有些人会质疑,明明这两项只要配置了服务器的相关选项就可以实现,为什么说它们难以解决呢?确实,开启这两项很容易,但开启了缓存后,我们的项目就开始面临另一个挑战:如何更新这些缓存。

相信大多数团队也找到了类似的答案,它和《高性能网站建设指南》关于“添加Expires头”所说的原则一样——修订文件名。即:

最有效的解决方案是修改其所有链接,这样,全新的请求将从原始服务器下载最新的内容

思路没错,但要怎么改变链接呢?变成什么样的链接才能有效更新缓存,又能最大限度避免那些没有修改过的文件缓存不失效呢?

先来看看现在一般前端团队的做法:

或者

大家会采用添加query的形式修改链接。这样做是比较直观的解决方案,但在访问量较大的网站,这么做可能将面临一些新的问题。

通常一个大型的web应用几乎每天都会有迭代和更新,发布新版本也就是发布新的静态资源和页面的过程。以上述代码为例,假设现在线上运行着index.html文件,并且使用了线上的a.js资源。index.html的内容为:

这次我们更新了页面中的一些内容,得到一个index.html文件,并开发了新的与之匹配的a.js资源来完成页面交互,新的index.html文件的内容因此而变成了:

好了,现在要开始将两份新的文件发布到线上去。可以看到,index.html和a.js的资源实际上是要覆盖线上的同名文件的。不管怎样,在发布的过程中,index.html和a.js总有一个先后的顺序,从而中间出现一段或大或小的时间间隔。对于一个大型互联网应用来说即使在一个很小的时间间隔内,都有可能出现新用户访问。在这个时间间隔中,访问了网站的用户会发生什么情况呢?

  1. 如果先覆盖index.html,后覆盖a.js,用户在这个时间间隙访问,会得到新的index.html配合旧的a.js的情况,从而出现错误的页面。
  2. 如果先覆盖a.js,后覆盖index.html,用户在这个间隙访问,会得到旧的index.html配合新的a.js的情况,从而也出现了错误的页面。

这就是为什么大型web应用在版本上线的过程中经常会较集中的出现前端报错日志的原因,也是一些互联网公司选择加班到半夜等待访问低峰期再上线的原因之一。此外,由于静态资源文件版本更新是“覆盖式”的,而页面需要通过修改query来更新,对于使用CDN缓存的web产品来说,还可能面临CDN缓存攻击的问题。我们再来观察一下前面说的版本更新手段:

我们不难预测,a.js的下一个版本是“1.0.1”,那么就可以刻意构造一串这样的请求“a.js?v=1.0.1”、“a.js?v=1.0.2”、……让CDN将当前的资源缓存为“未来的版本”。这样当这个页面所用的资源有更新时,即使更改了链接地址,也会因为CDN的原因返回给用户旧版本的静态资源,从而造成页面错误。即便不是刻意制造的攻击,在上线间隙出现访问也可能导致区域性的CDN缓存错误。

此外,当版本有更新时,修改所有引用链接也是一件与工程管理相悖的事,至少我们需要一个可以“查找-替换”的工具来自动化的解决版本号修改的问题。

对付这个问题,目前来说最优方案就是基于文件内容的hash版本冗余机制了。也就是说,我们希望工程师源码是这么写的:

但是线上代码是这样的:

其中”_82244e91”这串字符是根据a.js的文件内容进行hash运算得到的,只有文件内容发生变化了才会有更改。由于版本序列是与文件名写在一起的,而不是同名文件覆盖,因此不会出现上述说的那些问题。同时,这么做还有其他的好处:

  1. 线上的a.js不是同名文件覆盖,而是文件名+hash的冗余,所以可以先上线静态资源,再上线html页面,不存在间隙问题;
  2. 遇到问题回滚版本的时候,无需回滚a.js,只须回滚页面即可;
  3. 由于静态资源版本号是文件内容的hash,因此所有静态资源可以开启永久强缓存,只有更新了内容的文件才会缓存失效,缓存利用率大增;
  4. 修改静态资源后会在线上产生新的文件,一个文件对应一个版本,因此不会受到构造CDN缓存形式的攻击

虽然这种方案是相比之下最完美的解决方案,但它无法通过手工的形式来维护,因为要依靠手工的形式来计算和替换hash值,并生成相应的文件。这将是一项非常繁琐且容易出错的工作,因此我们需要借助工具。我们下面来了解一下fis是如何完成这项工作的。

首先,之所以有这种工具需求,完全是由web应用运行的根本机制决定的:web应用所需的资源是以字面的形式通知浏览器下载而聚合在一起运行的。这种资源加载策略使得web应用从本质上区别于传统桌面应用的版本更新方式。为了实现资源定位的字面量替换操作,前端构建工具理论上需要识别所有资源定位的标记,其中包括:

  • css中的@import url(path)、background:url(path)、backgournd-image:url(path)、filter中的src
  • js中的自定义资源定位函数,在fis中我们将其规定为__uri(path)。
  • html中的<script src=”path”>、<link href=”path”>、<imgsrc=”path”>、已经embed、audio、video、object等具有资源加载功能的标签。

为了工程上的维护方便,我们希望工程师在源码中写的是相对路径,而工具可以将其替换为线上的绝对路径,从而避免相对路径定位错误的问题(比如js中需要定位图片路径时不能使用相对路径的情况)。

 

fis的资源定位设计思想

fis有一个非常棒的资源定位系统,它是根据用户自己的配置来指定资源发布后的地址,然后由fis的资源定位系统识别文件中的定位标记,计算内容hash,并根据配置替换为上线后的绝对url路径。

要想实现具备hash版本生成功能的构建工具不是“查找-替换”这么简单的。我们考虑这样一种情况:

资源引用关系

由于我们的资源版本号是通过对文件内容进行hash运算得到,如上图所示,index.html中引用的a.css文件的内容其实也包含了a.png的hash运算结果,因此我们在修改index.html中a.css的引用时,不能直接计算a.css的内容hash,而是要先计算出a.png的内容hash,替换a.css中的引用,得到了a.css的最终内容,再做hash运算,最后替换index.html中的引用。

这意味着构建工具需要具备“递归编译”的能力,这也是为什么fis团队不得不放弃gruntjs等task-based系统的根本原因。针对前端项目的构建工具必须是具备递归处理能力的。此外,由于文件之间的交叉引用等原因,fis构建工具还实现了构建缓存等机制,以提升构建速度。

在解决了基于内容hash的版本更新问题之后,我们可以将所有前端静态资源开启永久强缓存,每次版本发布都可以首先让静态资源全量上线,再进一步上线模板或者页面文件,再也不用担心各种缓存和时间间隙的问题了!

在本系列的下一部分,我们将介绍静态资源管理与模板框架的思路和用法。

 

分享到:
评论

相关推荐

    Java 大型网站性能优化实战从前端网络 CDN 到后端大促的全链路性能优化

    CDN(Content Delivery Network)是一个关键的工具,它通过在全球部署多个节点,将静态资源如图片、CSS和JavaScript文件缓存到离用户最近的服务器上,从而减少网络延迟,提高页面加载速度。使用CDN可以有效缓解源...

    web前端性能优化

    综上所述,Web前端性能优化是一个系统性工程,涉及从服务器到前端各个环节的综合考量。通过优化措施的实施,可以显著提高页面的加载速度、渲染速度和用户体验。随着技术的不断进步,前端性能优化的方法和工具也在...

    让你页面速度飞起来 Web前端性能优化(视频教程+ppt)

    ### Web前端性能优化知识点 #### 一、构建优化 **1.1 文件压缩与合并** - **文件压缩**:利用工具如UglifyJS...总之,Web前端性能优化是一个系统性的工程,需要开发人员具备全面的知识体系和实践经验才能有效实施。

    软件工程与软件性能优化技巧.pptx

    ### 软件工程与软件性能优化技巧 #### 第1章 软件性能优化概述 **软件性能优化定义:** 软件性能优化是指通过对软件系统的设计、实现和部署等方面的改进来提升系统的性能,包括但不限于响应速度、吞吐量以及并发...

    高效前端:Web高效编程与优化实践.pdf

    《高效前端:Web高效编程与优化实践》这本书深入探讨了Web前端开发中的高效编程和优化技术,涵盖了从基础概念到高级策略的广泛内容。在Web开发领域,前端性能的优化对于提升用户体验、降低服务器压力以及提高网站...

    百度前端性能监控与优化实践

    - **资源优化**:静态资源采用外链、合并与压缩技术,同时设置强缓存策略并部署至CDN。 - **图片优化**:对图片进行质量压缩及延迟加载处理。 - **CSS Sprites**:使用CSS Sprites技术减少HTTP请求次数。 - **泛域名...

    _大前端进阶之道:前端性能优化,前端经典面试题,node面试题,webpack性能优化.zip

    在IT行业中,前端开发是至关重要的领域,而前端性能优化、面试准备以及工具的高效使用则是提升开发者技能的关键。这个压缩包 "_大前端进阶之道:前端性能优化,前端经典面试题,node面试题,webpack性能优化.zip" ...

    前端大厂最新面试题-前端性能优化.docx

    "前端性能优化知识点总结" 前端性能优化是非常重要的一方面,好的性能优化可以提高网站的用户体验,而坏的性能优化则可能会带来麻烦和规则太多的问题。在这个知识点总结中,我们将介绍八个方面的前端性能优化知识点...

    网站性能优化1

    综上所述,网站性能优化是一个系统工程,涉及前端、后端和网络层面的多个环节。通过综合运用这些技术,我们可以显著提升网站的加载速度和用户体验。"高性能网站建设指南(上1).pdf"中应该会详细解析这些概念,并给出...

    存储篇 1:浏览器缓存机制介绍与缓存策略剖析(1).md

    性能优化是前端开发中不可或缺的一部分,因为良好的性能不仅能够提升用户体验,而且在现代互联网环境下,前端团队如果不注重性能优化,将缺乏竞争力。 ### 性能优化的实践过程 进行性能优化的过程中,要深入理解...

    [高性能网站建设指南——前端工程师技能精粹(英文原版)].pdf

    总的来说,《高性能网站建设指南》是一本深度探讨前端性能优化的权威著作,对于任何想要提升网站性能的前端工程师来说,都是一本不可或缺的参考书。通过学习和应用书中的原则,开发者能够创建出更快、更流畅的用户...

    网易高级前端工程师跟着他每周重点攻克一个前端面试重难点

    Webpack或Rollup这样的构建工具,可以帮助我们实现代码打包、静态资源处理、自动化测试等。理解它们的配置和工作流程,以及如何编写可维护的模块化代码,将提升项目的可扩展性和团队协作效率。 在面试中,浏览器...

    大型网站性能优化实战从前端网络CDN到后端大促的全链路性能优化

    总结,大型网站性能优化是一个系统性工程,涉及到前端、网络和后端等多个层面。通过综合运用CDN、HTTP/2、懒加载、数据库优化、缓存策略等技术手段,以及在大促期间的特殊优化措施,可以显著提升网站的性能和用户...

    web前端性能测试

    在IT行业的领域内,"web前端性能...综上所述,前端性能测试不仅是技术实践的一部分,更是用户体验优化的重要环节。通过对网页性能的持续监控和优化,可以有效提升网站的访问速度,增强用户满意度,最终促进业务增长。

    我的职业是前端工程师.pdf

    - **JavaScript**:作为前端的核心语言,其版本不断更新,ES6+引入了许多新特性,提高了开发效率。 - **TypeScript**:作为一种强类型超集语言,TypeScript可以提供更好的类型检查,适合大型项目。 - **选择合适...

    高性能网站建设指南-前端工程师技能精髓PDF

    3. 使用CDN:内容分发网络能将静态资源分发到全球各地的服务器,减少延迟,提升加载速度。 三、缓存策略 1. HTTP缓存:利用Cache-Control、Expires等头部信息设置缓存策略,减少服务器负担,提高重复访问速度。 2. ...

    彩蛋篇:CDN 的缓存与回源机制解析(1).md

    在前端性能优化的实践中,CDN不仅可以提高静态资源的传输速度,还能够减轻业务服务器的负担,因为它将资源的存储和传输任务分散到了各个CDN节点服务器上。这样不仅提高了用户的访问速度,同时也提高了整个系统的稳定...

    网站性能优化2

    网站性能优化是一个重要的...总结,网站性能优化是一个涉及多方面、多层次的系统工程,涵盖前端、后端、网络、用户体验等多个领域。通过上述方法,我们可以显著提升网站的性能,为用户提供更加流畅、快速的在线体验。

Global site tag (gtag.js) - Google Analytics