前端的性能优化是非常重要的,客户端的资源很有限,并且层次不齐,很容易造成一些性能上的问题从而影响用户的体验,为了提高用户的体感,我们必须要从各方面进行优化,这样也能节省服务器的成本。
页面优化
CSS 置顶,JS 沉底
我们应该将样式文件在 <head> 中就引入,这样不会因为 CSS 的载入而页面重新渲染;同样的,尽可能将 js 代码在 </body> 尾部引入,这样页面的渲染不会因为加载 js 而阻塞。
按需加载(Lazy load)
网页中占用资源的一般都是图片,对于单页面设计,通常我们只需要用户在刚载入页面看到第一屏的图片即可,其他的图片则可以随着用户向下滚动页面的动作异步加载。这样的话,虽然 HTTP 请求数依然是不变的,但我们的页面响应速度明显就变快了。
随着各种 js 框架的出现和越来越强大,它们的体积也变得越来越大,后来为了加载速度又将框架的核心代码与功能模块分离,这样我们只需要在刚开始加载核心代码即可,功能模块代码在需要的时候加载即可。
延迟/异步加载 JS
script
标签是支持 defer
和 async
属性的,前者会将 JS 脚本推迟到 </html>
标签关闭前才加载,而后者会进行异步加载 js 脚本,不会阻塞页面其它资源的加载。(IE10 才支持 async)
减少 HTTP 请求数
HTTP 协议是基于 TCP 连接的,我们都知道 TCP 连接要经过三次握手、四次挥手的过程才能完成数据信息的传输,所以过多的 HTTP 请求会导致网页响应过慢。虽然浏览器是支持 HTTP 请求并发的,但并发数量也是有限的。
- 合理设置 HTTP 缓存
如果我们的页面是极其复杂的,而且客户端首次加载页面所需要的所有资源是必须的;这个时候我们可以对后续加载进行优化,即合理设置 HTTP 缓存,当客户端再次加载该页面时,大多数 HTTP 请求所需要的资源都已缓存在本地,所以 HTTP 请求的数量会大幅降低。
- 资源合并压缩
我们在项目上线时,应尽可能将 JavaScript/CSS/Imagines 这些静态资源进行合并与压缩。js 与 css 代码都有其相应的合并压缩工具,同时服务器端基本都支持 Gzip 压缩;而多个小图片(图标等)则可以利用 CSS Sprites 技术合并后将它们嵌入页面。
- 内联图片(inline imagines)
图片经过转码之后,可以使用data:URL scheme
进行加载而不需要 HTTP 请求,但这种方式不会缓存,而且过大的图片也会体积变大,IE8 以下也不支持。通常背景图片将使用这种方式来进行加载。
启用 HTTP/2
我们现在使用的 HTTP/1.1 协议已经持续了十多年了,它在性能和安全方面已经显现出了不足,新的 HTTP/2 协议不仅性能上更胜一筹,信息安全方面也值得我们关注。
CDN(内容分发网络)
CDN(内容分发网络)将负载分配到不同地域的不同服务器上,可以使不同地区的用户就近获得服务器上相同的资源,可以很好的解决网络拥堵问题,提高用户访问网站的速度。
优化数据库查询
事实上,前端是需要与后台进行大量数据交互的,而后台的数据库查询相当的耗费时间,利用索引可以大幅提高数据库查询速度,这对于前端页面的体验也是很大程度上的改善。
代码优化
前端开发是一个很头疼的过程,由于不同浏览器的 js 引擎性能不一,虽然谷歌的 V8 引擎很快;CSS 兼容性的不一,在面对前端资源匮乏且复杂的环境下,代码编写的优化也是值得关注的。
HTML 优化
对于很复杂的页面,页面 HTML 结构设计也是很重要的,HTML 结构不应该嵌套过深,否则不利于 SEO 排名,我们通常建议选择扁平化的页面结构设计方案。
Css优化
-
下面则是一些 CSS 书写规则上的优化方式。
-
选择器不宜复杂
CSS 样式的选择器不应该嵌套过深,或者组合太复杂,页面无时无刻都在高速进行 CSS 计算,选择器太过复杂的话会严重影响 CSS 的计算速度的。
-
选择器计算顺序(从右到左)
浏览器对 CSS 选择器的计算顺序是从右到左的,所以我们不能以从左到右的书写顺序去思考这个问题。
JS 优化
下面先讲一些 JS 编码过程中的优化方法。
其实前端的优化方案是很细化的,也是繁多的,为了用户体验,我们应该竭力去优化一切能优化到的地方。
-
缓存 DOM 节点
DOM 操作是相当耗费性能的,因为每次查找一个节点就需要遍历整个 DOM 树,所以当我们要对同一个节点进行多项操作时最好能先用一个变量将其缓存起来;事实上,Jquery 的链式操作正是解决了这一问题。
-
不要遍历节点集合
我们通常获取的是一个节点集合,而且是实时的,如果我们对其进行遍历则是相当低效的,所以我们最好将其转换为数组后再遍历。
-
不要使用 eval()
eval()
方法是一个相当耗费性能的方法(因为它必须先将字符串进行分析转换成 js 代码,然后才去执行),而且非常不安全,我们应尽可能少的去使用它。 -
减少作用链查找
当我们在函数内部要对外部变量或者对象进行频繁操作时,我们最好先在函数内部使用一个局部变量将其保存起来,最后在退出函数时将局部变量的值再赋值给目标外部变量。这么做的原因是,当我们频繁操作一个变量时,每次都会从作用域链中去查找该变量,而外部变量查找就会更深一些。