-
Notifications
You must be signed in to change notification settings - Fork 10
Description
记得几年前有段时间,周围的同学都在谈网站性能优化,那时候网上到处都在转载Yahoo前端工程师Steve Sounder归纳的那14条(称YSlow14条),似乎只要记住这14条准则就行,实际上对于前端性能优化有很多事情可以做,但是需要找到那些影响前端性能的根源,而YSlow14条实际上也是围绕下面几种情况进行优化的:
缓存策略:
- 配置Expires/Cache-Control;
- Etag;
- 缓存Ajax;
- 本地存储/离线存储相关。
HTTP协议规定,当Get请求某一页面时,如果客户端缓存已经保存一份文档副本,而服务器端文档并没有发生修改,就不必重新下载服务器端文件,而从 本地缓存中获取。我们把这叫做 条件请求,最常用的是通过If-Modified-Since/Last-Modified来判断;Etag也是一种条件请求,通过判断If-None-Match的匹配情况判断。对于第一种方式,采用的是时间戳,由于这里所显示的时间戳只能精确到秒,而且就算时间改变了,文件内容也会可能并没有改变,所以会继续请求并下载服务端资源。Etag方式可以解决这种问题,它通过对文件内容哈希散列,和服务器端文件对比来判断是否读取本地缓存文件。对于这两种方式,他们的共同点是:就算是读取本地缓存文件,也需要先向服务器端发送HTTP请求,为了解决这个问题,HTTP协议中规定了:Expries和Cache-Control,他们可以通过设置缓存的有效期来判断是否需要从服务器端获取新文件,在有效期内避免了向服务器端发送HTTP请求。不过如果用Expires需要保证本地时间和服务器时间一致。
减少HTTP请求次数
减少HTTP请求的方法很多,比如常见的合并文件 - 合并图片(CSS Sprites),合并多个Javascript和CSS文件等,对于图片还可以利用map-area来减少请求多个图片等。同时采用合理请求方案也很重要,还有就是合理的缓存返回的数据(如上面提到的Ajax缓存)。
Javascript中HTTP请求方式很多,但是大部分都是异步请求。我们可以通过基本的ajax实现,也可以利用强大的iframe实现(之所以强大是因为他不但可以用于页面布局,更可以实现跨域甚至跨协议请求,然而也是它的这些强大功能,使之可以被利用成为攻击的漏洞)。但是引用iframe需要了解它的缺点:首先引用iframe也就意味着新的页面的DOM加载渲染;其次iframe会阻塞父页面onload,同时父页面的CSS和javascript的位置也会影响着iframe的加载;另外低版本的IE默认规定iframe连接同时只能打开2个,如果想利用iframe做更多的HTTP请求,需要复用这两个iframe,同时还有一点就是利用iframe会影响页面的返回按钮。然而这种请求都有一个共同特点,就是请求/相应一对一模式,如果遇到那种需要不断请求某一接口获取最新数据的需求(如股票行情),是否可以通过一次请求,服务器端一旦数据更新就会不断的返回给客户端呢?我们可以通过建立 长连接推送 (Comet)避免客户端多次发送http请求。
减少网络传输文件大小
- 压缩组件;
- 精简Javascript,CSS;
- 避免重复脚本和CSS。
很明显在网络传输中,如果文件越小,响应就越快。在HTTP协议报文头中有这么一个字段:Accept-Encoding/Content-Encoding 用来表示返回的文本可以进行编码压缩,当设置gzip压缩时返回内容会比未压缩的小很多。有时候虽然我们设置了Accept-Encoding/Content-Encoding,但是返回并没有gzip压缩,追其原因可能就是在发送请求时客户端安全软件或者Web代理 修改了请求的报文头信息Accept-Encoding,虽然这种情况很少会出现,但是却无法避免,那么我们只能通过修改页面的代码尽量让文件越小越好。《高性能网站进阶指南》中提出了以下解决方案:
- 对于绑定在HTML代码中的事件委托给JS文件中通过事件监听
- 使用相对URL
- 移除空白
- 移除属性引号
- 避免行内样式
- 为Javascript变量设置别名
另外我们可以通过YUI Compressor/Google Closuer/UglifyJS这类工具对Javascript或者CSS文件进行压缩混淆处理,但是一旦文件中存在中文字符,就要特别注意编码问题了。关于编码问题详见之前写过的一篇文章:编码的那些事儿
浏览器内核渲染的影响
- 使用外部Javascript和CSS;
- CSS放置于页面顶部;
- Javascript文件放置页面底部;
- 避免CSS表达式。
实际上将JS和CSS代码通过内联的方式写在页面中,页面呈现的会更快,但是为什么要说把CSS和JS文件放在外部文件中呢?这主要因为放在外部文件中才能缓存,当再次访问时就不必重新加载了。关于#2,#3,#4具体的原因可参考之前的一篇文章 从浏览器渲染谈前端开发 最后一部分,这样做的目的就是为了防止页面reflow或者线程互斥带来体验上的弊端。需要额外着重说明的是在写CSS时采用合理规则:
- 避免使用通配符(包括兄弟选择符,子选择符,后代选择符,属性选择符)
- 不要限定ID选择符,如:ul#idStr
- 不要限定类选择符,如:p.className
- 规则越具体越好
- 避免使用后代选择符
- 避免使用标签-子选择符
- 质疑子选择符的所有用途
- 依靠继承
网络请求优化
- 使用CDN;
- 减少DNS查询;
- 避免重定向。
CDN的好处可想而已,虽说地球是圆都知道购物会尽量选择最近的超市。当首次访问某一Web页面时,网络会通过DNS查询URI所对应的IP然后缓存起来,如果页面中又很多不同域名的资源,那么将会耗用很多时间用来查找IP上。至于重定向不言而喻肯定会影响页面加载时间,所以要尽量避免,尤其是不要因为URL少一个结尾的斜线(/)而导致301重定向。
除了上面说的这些,还需要考虑一下几点:
- 合理分配资源的域名和资源地址。
- HTML代码语义化,优化Javascript和CSS代码。
- 防止脚本代码阻塞页面执行,如异步加载或者延迟执行。
- 负载均衡。
- 通过刷新文档输出分块加载动态内容(需要设置
Transfer-Encoding:chunked),如Facebook的Bigpipe。
参考文档
- 《高性能网站建设指南》
- 《高性能网站进阶指南》