一、網路最佳化
YSlow有23條規則,中文可以參考這裡。這幾十條規則最主要是在做消除或減少不必要的網路延遲,將需要傳輸的資料壓縮至最少。
1)合併壓縮CSS、JavaScript、圖片,靜態資源CDN快取
透過建置工具Gulp,可以在開發的時候就將合併壓縮的事情一起做掉。
之所以要做合併壓縮是因為:HTTP 1.x不允許一個連接上的多個響應資料交錯到達(多路復用),因而一個響應必須完全返回後,下一個響應才會開始傳輸。
也就是說即使客戶端同時發送了兩個請求,而且CSS資源先準備就緒,伺服器也會先發送HTML回應,然後再交付CSS。
使用CDN是為了讓使用者造訪的時候能用最近的資源,減少來回傳輸時間。
HTTP2.0改進了HTTP1.x很多方面。
2)CSS放頂部,JavaScript放底部
CSS可以並行下載,而JavaScript載入之後會造成阻塞。
但凡事還是會有例外,如果把行內腳本放在樣式表之後,會明顯地延遲資源的下載(結果是樣式表下載完成並且行內腳本執行完畢時,後續資源才能開始下載)。
這是因為行內腳本可能含有依賴樣式表中樣式的程式碼,例如document.getElementsByClassName()。
<head> <link rel="stylesheet" href="css/all-normal.css" type="text/css" /> </head> <body> <div id="content"></div> <script> var content = ''; for(i=1; i<1000000; i++) content += '写入页面'; document.getElementById('content').innerHTML = content; </script> <img src="images/ui.png" / alt="JavaScript優化以及前段開發小技巧" > </body>
下面透過Chrome的工具檢視下:
3)優化DNS解析,減少重定向
在做一個「女神評選活動」的時候,需要在微信中訪問能夠獲取用戶的openid,微信取得使用者基本資訊是需要經過幾個步驟的:
先取得code,再透過code取得openid,最後再跳轉訪問靜態頁面。
由於公司將業務分成了多個小組,所以短短的三步其實需要三個小組配合,需要重定向多個域名。
下圖是未優化前的瀑布圖,但不是最壞情況,有時候訪問到靜態頁面需要經過10多秒,完全不能接受,下圖中會跳轉4個網域:
後面不跳index那個域名,直接跳到微信操作域名,減少一個域名的跳轉,各小組代碼再做優化,但效果還是不理想,僅僅快了幾秒鐘。
最後發現其實是在與微信的伺服器做互動的時候,DNS解析花了太多時間!不得已,在伺服器的host中新增一筆記錄,直接透過IP指向。
下圖是最終優化結果,雖然達不到秒開,但至少可以接受了:
二、JavaScript優化
1)圖片預先加載
在做一個「秋名山活動」的時候,使用了圖片預先載入。這個活動中有120多張圖片。
流程很簡單,就是答題,最後給評論結果,再分享出去。
如果一下子加載那麼多圖片,一定是愚蠢的想法,最後決定,在頁面載入的時候先加載一些通用圖片。
在答題的時候當前頁面,預先加載後面頁面中的圖片,防止訪問頁面的時候直接不展示圖片,圖片也做了適當的合併。
將網站地址放在gtmetrix.com測試,下面是最終的瀑布圖,可以發現圖片都在其他靜態資源的後面,這樣能儘早的展現頁面給用戶:
优化还远远没有结束,在Chrome中分别模拟了good 2G、good 3G以及4G后,有结果的情况并不理想。
good 2G:
good 3G:
4G:
还有很大的优化空间可以做,关于这个预加载的原理,可以参考《图片预加载与懒加载》
2)减少分支
在写业务逻辑的时候,经常会用到if else,switch之类的逻辑判断,如果每次都做这么多判断,很容易影响性能。
所以可以通过多种方式来避免过多的判断。
1. 惰性模式
这是在看《JavaScript设计模式》的时候看到的。
减少每次代码执行时的重复性分支判断,通过对对象重定义来屏蔽原对象中的分支判断。
惰性模式分为两种:第一种文件加载后立即执行对象方法来重定义,第二种是当第一次使用方法对象时来重定义。
公司有个页面要提供给第三方APP,但是最终发现第三方APP不能使用localStorage缓存,最终只得做兼容的方式。
但为了避免每次引用方法的时候都做判断,就使用加载后立即重定义:
var getFn = function() { if (sore.enabled) return sore.get; return cookie.get; }(); var setFn = function() { if (sore.enabled) return sore.set; return cookie.set; }();
2. 建立映射关系
页面中经常需要弹出框提示,后面就自己做了一个,但弹出框会有很多款式。
如果用简单工厂模式创建的话,免不了switch分支判断,后面就直接用赋不同的key,还能缓存起来,只初始化一次。
/** * 弹出框单例模式 */ var factories = {}; var DialogFactory = function(type, options) { if (factories[type]) return factories[type]; return factories[type] = new iDialog(options); }; /** * 提示框 */ var Alert = function(content, options) { var d = DialogFactory('alert', options); //其他逻辑省略 return d; }; /** * 确认框 */ var Confirm = function(content, options) { var d = DialogFactory('confirm', options); //其他逻辑省略 return d; };
3)第三方代码异步加载
第三方代码,例如百度统计、微信SDK等,这些完全可以在将业务资源加载完后再添加。
/** * 百度统计设置 */ util.baidu = function(key) { global._hmt = global._hmt || []; (function() { var hm = document.createElement("script"); hm.src = "//m.sbmmt.com/hm.js?" + key; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); };
4)cookie与localStorage缓存
有了缓存后,就能减少与服务器的通信,在本地操作。
公司有个查违章的业务,在本地添加好车辆后,再次进入页面的时候就需要能直接选择事先添加好的车辆。
最理想的方式就是添加好后,就在本地缓存起来,下次进入直接调取缓存。
我会优先使用localStorage,下面的表格就是对比:
cookie
localStorage
数据生命周期
可设置失效时间除非被清除,否则永久保存
数据大
大约4KB 大约5M
与服务器通信
每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题
不参与和服务器的通信
本地存储,之前的历史大概如下图所示:
localStorage在浏览器兼容方面,IE8居然也支持了。
5)事件委托
使用事件委托技术能让你避免对特定的每个节点添加事件监听器。
事件监听器是被添加到它们的父元素上,通过事件冒泡,触发执行。
在开发的时候,经常会出现动态添加元素的情况。
如果每次都重新绑定一次事件,那会有很多多余操作,而绑定在此元素的父级,就只需绑定一次即可。
document.getElementById('ul').onclick = function(e) { var e = e || window.event, tar = e.target || e.srcElement; if (tar.nodeName.toLowerCase() == 'li') { tar.style.background = 'black'; } }
6)节流与去抖动
节流(throttle):预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。
例如mousemove 事件、window对象的resize和scroll事件。
去抖动(debounce):当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。
例如文本输入keydown 事件,keyup 事件,做autocomplete等。
节流与去抖动最大的不同的地方就是在计算最后执行时间的方式上。著名的开源工具库underscore中有内置了两个方法。
在做公司内部的一个系统的时候,需要方希望在左右滚动表格的时候,能将第一列固定在最左边,方便查看。
为了让操作能更流畅,我再这里用了节流,有些浏览器会出现卡顿,就得需要增加周期时间。
三、小技巧
1)在手机中打印变量
在移动页面的时候经常需要调试字段,又不能用console.log,每次alert的话,碰到对象就看不到内容了。
只能自己写个小方法来打印出来,JSON.stringify,通过这个方法能够方便的实现功能。
var print = function(obj, space) { space = space || 4; var html = JSON.stringify(obj, null, space); html = html.replace(/\n/g, '<br>').replace(/\s/g, ' '); var pre = document.createElement('pre'); var div = document.createElement('code'); pre.style.cssText = 'border:1px solid #000;padding:10px;background:#FFF;margin-bottom:20px;'; div.innerHTML = html; pre.appendChild(div); var body = document.querySelector('body'); body.insertBefore(pre, body.children[0]); }; print({a:1, b:'demo', c:{text:'content'}});
2)chrome插件JSON-handle
服务器返回的很多都是JSON格式的数据,通常写好后给你个接口,顺便给你几个demo参数。
在浏览器中打开后,就是一串字符串,但要给人看的话,就得格式化一下了,这个插件就是用来让人看的。
更多JavaScript優化以及前段開發小技巧相关文章请关注PHP中文网!