前端性能优化

本文首发于kmac007.me

性能优化

资源压缩合并,减少HTTP请求

由于HTTP是无状态协议,意味着每次HTTP请求都需要建立通信链路、进行数据传输,而在服务器端,每个HTTP请求都需要启动独立的线程处理。这些通信和服务的开销是很昂贵的,减少HTTP请求的数目可有效提高访问性能。以下方法可以对资源进行压缩合并,减少HTTP请求:

  1. 合并CSS,并压缩
  2. 合并JavaScript,并压缩
  3. 图片压缩合并,通过CSS的操作偏移量显示不同的图片。(CSS Sprite,即俗称:雪碧图)

异步加载

异步加载的方式

  1. 动态脚本加载
    通过JS动态的创建<script>标签来动态加载js文件。
  2. defer
<script src="./a.js" defer></script>
  1. async
<script src="./a.js" async></script>

异步加载的区别

如果不设置defer或者async,那么浏览器在遇到<script>标签时,文档的解析会停止,不再构建DOM,会导致页面阻塞直到脚本加载完毕。这是非常不好的用户体验,因此我们一般把<script>标签放置在<body>标签的最尾部。而deferasync两者同样可以解决这个问题。下面是二者的区别:

  1. defer是在HTML解析完之后才会执行,如果是多个,按照加载的顺序依次执行。
  2. async是在加载完之后立即执行,如果是多个,执行顺序和加载顺序无关。

浏览器缓存

缓存的分类

强缓存

Expires Expires:Sat, 13 May 2017  14:22:34 GMT
Cache-Control Cache-Control: max-age=3600

协商缓存

Last-Modified If-Modified-Since Last-Modified: Sat, 13 May 2017  14:22:34 GMT
Etag If-None-Match

对于缓存这个部分,打算再写一篇文章来描述。

使用CDN

CDN的全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。

简单来说,CDN将内容缓存到分布各地的CDN的节点上,根据用户的访问IP,使用户可就近取得所需要的内容,提高网络响应速度。

预解析DNS

DNS Prefetch,即DNS的预获取,是前端优化的一部分。一般来说,在前端优化中与DNS有关的有两点;

  1. 减少DNS的解析次数
  2. DNS的预解析,即DNS Prefetch

一次DNS解析一般要耗费20-120毫秒,减少DNS解析事件和次数是个很好的优化方式。默认情况下浏览器会对页面中和当前域名不在同一域的域名进行预解析,并且缓存结果,这就是隐式的DNS Prefetch。如果向对页面中没有出现的域进行预解析,那么就要使用显式的DNS Prefetch

淘宝首页中的DNS Prefetch

enter image description here

使用方法:
DNS Prefetch应该尽量放在网页的前面,推荐放在<meta>后面,具体用法如下:

<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//kmac007.me">
<link rel="dns-prefetch" href="//kmac007.com">
<link rel="dns-prefetch" href="//www.kmac007.com">

需要注意的是,虽然DNS Prefetch能够加快页面的解析速度,但是不能滥用。

如果需要禁止隐式的DNS Prefetch,可以使用以下的标签:

<meta http-equiv="x-dns-prefetch-control" content="off">

事件节流

比如,写一个滚动加载组件,监听scroll事件,这时每次滚动都会执行多次回调函数,这是相当消耗性能的。因此,我们可以通过事件节流的方式,减少滚动回调函数的触发,从而提升性能。
例如:

var loadMore = document.getElementById('#loadMore')
var timer

//计时器的回调函数
function callback() {
  //距离顶部的距离
  const top = loadMore.getBoundingClientRect().top
  //视口高度
  const windowHeight = document.documentElement.clientHeight
  if(top && top < windowHeight){
    //loading...
  }
}

//监听滚动事件并对函数节流
window.addEventListener('scroll', ()=>{
  if(timer) {
    clearTimeout(timer)
  }
  timer = setTimeout(callback, 100)
})

如上代码,我们通过定时器的方式进行函数节流,有效减少了回调函数的执行。

减少DOM操作

对DOM操作的代价是高昂的,这在web应用中通常是一个性能瓶颈。

在《高性能JavaScript》中这么比喻:“把DOM看成一个岛屿,把JavaScript(ECMAScript)看成另一个岛屿,两者之间以一座收费桥连接”。所以每次访问DOM都会教一个过桥费,而访问的次数越多,交的费用也就越多。所以一般建议尽量减少过桥次数。

查询和修改DOM元素会造成页面的RepaintReflow。那么我们先了解下什么是RepaintReflow

RepaintReflow

Repaint(重绘)就是在一个元素的外观被改变,但没有改变布局(宽高)的情况下发生,如改变visibility、outline、背景色等等。

Reflow(重排) 的成本比 Repaint 的成本高得多的多。DOM Tree 里的每个结点都会有 Reflow 方法,一个结点的 Reflow 很有可能导致子结点,甚至父点以及同级结点的 Reflow。在一些高性能的电脑上也许还没什么,但是如果 Reflow 发生在手机上,那么这个过程是非常痛苦和耗电的。

每次设置style属性改变节点样式,每设置一次都会导致一次reflow,所以最好通过设置class的方式; 有动画效果的元素,它的position属性应当设为fixed或absolute,这样不会影响其它元素的布局;如果功能需求上不能设置position为fixed或absolute,那么就权衡速度的平滑性。

总之,因为 Reflow 有时确实不可避免,所以只能尽可能限制Reflow的影响范围。

缓存DOM查询

// 未缓存 DOM 查询
for(let i = 0; i < document.getElementsByTagName('p').length; i++) {
  //...
}

上面这种情况下,每次循环都要进行DOM查询,非常影响性能。
我们通过如下的方式缓存DOM,这样,就不需要每次都进行DOM查询,达到了减少DOM查询的目的。

// 缓存了 DOM 查询
var pList = document.getElementsByTagName('p')
for(let i = 0; i < pList.length; i++) {
  //...
}

合并DOM插入

在循环插入DOM时,我们可以将部分生成的DOM节点插入到一个片段中,最后统一将片段插入到HTML中。

var listNode = document.getElementById('list')

// 要插入10个li
var frag = document.createDocumentFragment()
var li
for(let i = 0; i < 10; i++) {
  li = document.createElement('li')
  li.innderHTML = "List item" + i
  frag.appendChild(li)
}

listNode.appendChild(frag)

懒加载

懒加载的原理是通过自定义属性标签存放图片原有的src属性,当img标签出现在浏览器窗口范围内再依次将原src属性填充以达到懒加载的效果。这种方法减少了开始加载网页时的请求,减少浏览器卡死的几率,减少了流量的消耗,同时提高了用户体验。

主要步骤:

  1. 判断图片是否可见(滚动高度 + 窗口高度 > 图片到页面顶部高度 && 图片到页面顶部高度 + 图片高度 > 滚动高度)
  2. 如果图片可见,将存放在data-src中原本的src属性填充src属性中。

以下为一个图片懒加载的示例:

<script async src="//jsfiddle.net/enpk4e92/embed/result,js,html,css/dark/"></script>

SSR服务端渲染

服务端渲染可以提高性能。React.js和Vue.js目前都支持服务端渲染。在此不做深究。

参考

  1. Web前端性能优化
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • AJax 优化 缓存 Ajax 请求尽量使用GET, 仅取决于cookie数量 Cookie 优化 减少Cooki...
    KeKeMars阅读 9,325评论 5 89
  • 由于前端页面绝大部分资源都是从服务器读取,而且受浏览器限制,分配的计算机资源相对较少。。页面的加载速度和性能往往受...
    Chyun阅读 309评论 0 0
  • 围绕前端的性能多如牛毛,涉及到方方面面,以我我们将围绕PC浏览器和移动端浏览器的优化策略进行罗列注意,是罗列不是展...
    流动码文阅读 672评论 0 0
  • 前言 对于前端的性能话题,从来都没有断绝过。因为这个东西没有最好,只有更好。而且往往也是业务的繁杂程度去决定优化程...
    Layzimo阅读 27,688评论 2 51
  • 网站的划分一般为二:前端和后台。我们可以理解成后台是用来实现网站的功能的,比如:实现用户注册,用户能够为文章发表评...
    ConRon阅读 730评论 0 0