裴琳 · 微成长--web性能优化与前端工程
Posted 金智人才
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了裴琳 · 微成长--web性能优化与前端工程相关的知识,希望对你有一定的参考价值。
还记得上期那位任性的任何信息都不肯透漏的谜一样的男子鲁涛吗?
现在又来了一位谜一样的女子。。
你们都这么任性(神秘)
小编实在看不下去了╭(╯^╰)╮
为了不让这个微课程最后演变成一个“猜谜大会”
小编也要任性一回!
咳咳…下期分享者注意了,
生活照片高清无码,自我介绍详略得当,
成长历程详实有料,分享内容创意接地气!
...................任性的分割线.........................
web性能优化与前端工程
每个参与过开发企业级web应用的前端工程师或许都曾思考过前端性能优化方面的问题。我们有雅虎14条性能优化原则,还有两本很经典的性能优化指导书:《高性能网站建设指南》、《高性能网站建设进阶指南》。这些性能优化原则大概是在7年前提出的,对于web性能优化至今都有非常重要的指导意义。
然而,对于构建大型web应用的团队来说,要坚持贯彻这些优化原则并不是一件十分容易的事。因为优化原则中很多要求是与工程管理相违背的,比如 把
css
放在头部
和 把
js
放在尾部
这两条原则,我们不能让团队的工程师在写样式和脚本引用的时候都去修改一个相同的页面文件。这样做会严重影响团队成员间并行开发的效率,尤其是在团队有版本管理的情况下,每天要花大量的时间进行代码修改合并,这项成本是难以接受的。因此在前端工程界,总会看到周期性性能优化工作,辛勤的前端工程师每到月圆之夜就会倾巢出动根据优化原则做一次性能优化。
|性能优化是一个工程问题
本文将从一个全新的视角来思考web性能优化与前端工程之间的关系,揭示前端性能优化在前端架构及开发工具设计层面的实现思路。
1 性能优化原则及分类
首先,我们把雅虎14条优化原则,《高性能网站建设指南》以及《高性能网站建设进阶指南》中提到的优化点做一次梳理,按照优化方向分类,可以得到这样一张表格:
优化方向 |
优化手段 |
请求数量 |
合并脚本和样式表,CSS Sprites,拆分初始化负载,划分主域 |
请求带宽 |
开启GZip,精简javascript,移除重复脚本,图像优化 |
缓存利用 |
使用CDN,使用外部JavaScript和CSS,添加Expires头, |
页面结构 |
将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出 |
代码校验 |
避免CSS表达式,避免重定向 |
目前大多数前端团队可以利用 yui compressor 或者 google closure compiler 等压缩工具很容易做到 精简
Javascript
这条原则;同样的,也可以使用图片压缩工具对图像进行压缩,实现 图像优化
原则。这两条原则是对单个资源的处理,因此不会引起任何工程方面的问题。很多团队也通过引入代码校验流程来确保实现避免
css
表达式
和 避免重定向
原则。目前绝大多数互联网公司也已经开启了服务端的Gzip压缩,并使用CDN实现静态资源的缓存和快速访问;一些技术实力雄厚的前端团队甚至研发出了自动CSS Sprites工具,解决了CSS Sprites在工程维护方面的难题。使用“查找-替换”思路,我们似乎也可以很好的实现 划分主域
原则。
我们把以上这些已经成熟应用到实际生产中的优化手段去除掉,留下那些还没有很好实现的优化原则。再来回顾一下之前的性能优化分类:
优化方向 |
优化手段 |
请求数量 |
合并脚本和样式表,拆分初始化负载 |
请求带宽 |
移除重复脚本 |
缓存利用 |
添加Expires头减少DNS查找,配置ETag,使AjaX可缓存 |
页面结构 |
将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出 |
2 静态资源版本更新与缓存
缓存利用
分类中保留了 添加
Expires
头
和配置
ETag
两项。或许有些人会质疑,明明这两项只要配置了服务器的相关选项就可以实现,为什么说它们难以解决呢?确实,开启这两项很容易,但开启了缓存后,我们的项目就开始面临另一个挑战: 如何更新这些缓存?
许多类似的答案和《高性能网站建设指南》关于“添加Expires头”所说的原则一样——修订文件名。即:
|最有效的解决方案是修改其所有链接,这样,全新的请求将从原始服务器下载最新的内容。
思路没错,但要怎么改变链接呢?变成什么样的链接才能有效更新缓存,又能最大限度避免那些没有修改过的文件缓存不失效呢?
对于静态资源缓存更新的问题,目前来说最优方案就是 基于文件内容的
hash
版本冗余机制
了。也就是说,我们希望项目源码是这么写的:
<scripttype="text/javascript"src="a.js"></script>
发布后代码变成
<scripttype="text/javascript"src="a_8244e91.js"></script>
也就是a.js发布出来后被修改了文件名,产生一个新文件,并不是覆盖已有文件。其中”_82244e91”这串字符是根据a.js的文件内容进行hash运算得到的,只有文件内容发生变化了才会有更改。由于将文件发布为带有hash的新文件,而不是同名文件覆盖,因此不会出现其他方法存在的问题。同时,这么做还有其他的好处:
1. 上线的a.js不是同名文件覆盖,而是文件名+hash的冗余,所以可以先上线静态资源,再上线html页面,不存在间隙问题;
2. 遇到问题回滚版本的时候,无需回滚a.js,只须回滚页面即可;
3. 由于静态资源版本号是文件内容的hash,因此所有静态资源可以开启永久强缓存,只有更新了内容的文件才会缓存失效,缓存利用率大增;
|以文件内容的hash值为依据生产新文件的非覆盖式发布策略是解决静态资源缓存更新最有效的手段。
另一方面,虽然这种方案是相比之下最完美的解决方案,但它无法通过手工的形式来维护,因为要依靠手工的形式来计算和替换hash值,并生成相应的文件,将是一项非常繁琐且容易出错的工作,因此我们需要借助工具来处理。
用grunt来实现md5功能是非常困难的,因为grunt只是一个task管理器,而md5计算需要构建工具具有递归编译的能,而不是简单任务调度。考虑这样的例子:
由于我们的资源版本号是通过对文件内容进行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中的引用。
计算index.html中引用的a.css文件的url过程:
1. 压缩a.png后计算其内容的md5值
2. 将a.png的md5写入a.css,再压缩a.css,计算其内容的md5值
3. 将a.css的md5值写入到index.html中
grunt等task-based的工具是很难在task之间协作处理这样的需求的。
在解决了基于内容hash的版本更新问题之后,我们可以将所有前端静态资源开启永久强缓存,每次版本发布都可以首先让静态资源全量上线,再进一步上线模板或者页面文件,再也不用担心各种缓存和时间间隙的问题了!
3.静态资源管理与模块化框架
解决了静态资源缓存问题之后,让我们再来看看前面优化原则表还剩些什么:
优化方向 |
优化手段 |
请求数量 |
合并脚本和样式表,拆分初始化负载 |
请求带宽 |
移除重复脚本 |
缓存利用 |
使AjaX可缓存 |
页面结构 |
将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出 |
很不幸,剩下的优化原则都不是使用工具就能很好实现的。或许有人会辩驳:“我用某某工具可以实现脚本和样式表合并”。嗯,必须承认,使用工具进行资源合并并替换引用或许是一个不错的办法,但在大型web应用,这种方式有一些非常严重的缺陷。
事实上,使用工具在线下进行静态资源合并是无法解决资源按需加载的问题的。如果解决不了按需加载,则必会导致资源的冗余;此外,线下通过工具实现的资源合并通常会使得资源加载和使用的分离,比如在页面头部或配置文件中写资源引用及合并信息,而用到这些资源的html组件写在了页面其他地方,这种书写方式在工程上非常容易引起维护不同步的问题,导致使用资源的代码删除了,引用资源的代码却还在的情况。因此,在工业上要实现资源合并至少要满足如下需求:
1.确实能减少HTTP请求,这是基本要求(合并)
2.在使用资源的地方引用资源(就近依赖),不使用不加载(按需)
3. 虽然资源引用不是集中书写的,但资源引用的代码最终还能出现在页面头部(css)或尾部(js)
4.能够避免重复加载资源(去重)
将以上要求综合考虑,不难发现,单纯依靠前端技术或者工具处理是很难达到这些理想要求的。可以使用一种新的模板架构设计,用以实现前面说到那些性能优化原则,同时满足工程开发和维护的需要,这种架构设计的核心思想就是:
|基于依赖关系表的静态资源管理系统与模块化框架设计
通过这种架构,可以实现 按需加载
,将脚本放在底部
,将样式表放在头部
三项优化原则。
现在我们再来回顾一下前面的性能优化原则分类表,剔除掉已经做到了的,看看还剩下哪些没做到的:
优化方向 |
优化手段 |
请求数量 |
拆分初始化负载 |
缓存利用 |
使AjaX可缓存 |
页面结构 |
尽早刷新文档的输出 |
拆分初始化负载
的目标是将页面一开始加载时不需要执行的资源从所有资源中分离出来,等到需要的时候再加载。工程师通常没有耐心去区分资源的分类情况,但我们可以利用组件化框架接口来帮助工程师管理资源的使用。
到目前为止,我们又以架构的形式实现了一项优化原则(拆分初始化负载),回顾我们的优化分类表,现在仅有两项没能做到了:
优化方向 |
优化手段 |
缓存利用 |
使AjaX可缓存 |
页面结构 |
尽早刷新文档的输出 |
剩下的两项优化原则要做到并不容易,真正可缓存的Ajax在现实开发中比较少见,而 尽早刷新文档的输出
原则facebook在2010年的velocity上 提到过,就是BigPipe技术。当时facebook团队还讲到了Quickling和PageCache两项技术,其中的PageCache算是比较彻底的实现Ajax可缓存的优化原则了。由于篇幅关系,就不在此展开了。
以上是关于裴琳 · 微成长--web性能优化与前端工程的主要内容,如果未能解决你的问题,请参考以下文章