摘要:前端性能優(yōu)化指南優(yōu)化緩存異步并不等于即時(shí)。操作性能問(wèn)題主要有以下原因。發(fā)生在之前,所以相對(duì)來(lái)說(shuō)會(huì)造成更多性能損耗。新引擎還對(duì)對(duì)象屬性訪問(wèn)做了優(yōu)化,解決方案叫,簡(jiǎn)稱。代價(jià)是前置的掃描類型編譯優(yōu)化。數(shù)組,,閉包變量不在優(yōu)化范疇之列。
前端性能優(yōu)化指南 AJAX優(yōu)化
COOKIE專題
緩存AJAX:
異步并不等于即時(shí)。
請(qǐng)求使用GET:
當(dāng)使用XMLHttpRequest時(shí),而URL長(zhǎng)度不到2K,可以使用GET請(qǐng)求數(shù)據(jù),GET相比POST更快速。
POST類型請(qǐng)求要發(fā)送兩個(gè)TCP數(shù)據(jù)包。
先發(fā)送文件頭。
再發(fā)送數(shù)據(jù)。
GET類型請(qǐng)求只需要發(fā)送一個(gè)TCP數(shù)據(jù)包。
取決于你的cookie數(shù)量。
DOM優(yōu)化減少COOKIE的大小。
使用無(wú)COOKIE的域。
比如圖片CSS等靜態(tài)文件放在靜態(tài)資源服務(wù)器上并配置多帶帶域名,客戶端請(qǐng)求靜態(tài)文件的時(shí)候,減少COOKIE反復(fù)傳輸時(shí)對(duì)主域名的影響。
eval優(yōu)化
優(yōu)化節(jié)點(diǎn)修改。
使用cloneNode在外部更新節(jié)點(diǎn)然后再通過(guò)replace與原始節(jié)點(diǎn)互換。
var orig = document.getElementById("container");
var clone = orig.cloneNode(true);
var list = ["foo", "bar", "baz"];
var content;
for (var i = 0; i < list.length; i++) {
content = document.createTextNode(list[i]);
clone.appendChild(content);
}
orig.parentNode.replaceChild(clone, orig);
優(yōu)化節(jié)點(diǎn)添加
多個(gè)節(jié)點(diǎn)插入操作,即使在外面設(shè)置節(jié)點(diǎn)的元素和風(fēng)格再插入,由于多個(gè)節(jié)點(diǎn)還是會(huì)引發(fā)多次reflow。
優(yōu)化的方法是創(chuàng)建DocumentFragment,在其中插入節(jié)點(diǎn)后再添加到頁(yè)面。
如JQuery中所有的添加節(jié)點(diǎn)的操作如append,都是最終調(diào)用DocumentFragment來(lái)實(shí)現(xiàn)的,
createSafeFragment(document) {var list = nodeNames.split( "|" ),
safeFrag = document.createDocumentFragment();if (safeFrag.createElement) {
while (list.length) { safeFrag.createElement( list.pop(); ); };};
return safeFrag;};
優(yōu)化CSS樣式轉(zhuǎn)換。
如果需要?jiǎng)討B(tài)更改CSS樣式,盡量采用觸發(fā)reflow次數(shù)較少的方式。
如以下代碼逐條更改元素的幾何屬性,理論上會(huì)觸發(fā)多次reflow。
element.style.fontWeight = "bold" ; element.style.marginLeft= "30px" ; element.style.marginRight = "30px" ;
可以通過(guò)直接設(shè)置元素的className直接設(shè)置,只會(huì)觸發(fā)一次reflow。
element.className = "selectedAnchor" ;
減少DOM元素?cái)?shù)量
在console中執(zhí)行命令查看DOM元素?cái)?shù)量。
`document.getElementsByTagName( "*" ).length`正常頁(yè)面的DOM元素?cái)?shù)量一般不應(yīng)該超過(guò)1000。
DOM元素過(guò)多會(huì)使DOM元素查詢效率,樣式表匹配效率降低,是頁(yè)面性能最主要的瓶頸之一。
DOM操作優(yōu)化。
DOM操作性能問(wèn)題主要有以下原因。
DOM元素過(guò)多導(dǎo)致元素定位緩慢。
大量的DOM接口調(diào)用。
JAVASCRIPT和DOM之間的交互需要通過(guò)函數(shù)API接口來(lái)完成,造成延時(shí),尤其是在循環(huán)語(yǔ)句中。
DOM操作觸發(fā)頻繁的reflow(layout)和repaint。
layout發(fā)生在repaint之前,所以layout相對(duì)來(lái)說(shuō)會(huì)造成更多性能損耗。
reflow(layout)就是計(jì)算頁(yè)面元素的幾何信息。
repaint就是繪制頁(yè)面元素。
對(duì)DOM進(jìn)行操作會(huì)導(dǎo)致瀏覽器執(zhí)行回流reflow。
解決方案。
純JAVASCRIPT執(zhí)行時(shí)間是很短的。
最小化DOM訪問(wèn)次數(shù),盡可能在js端執(zhí)行。
如果需要多次訪問(wèn)某個(gè)DOM節(jié)點(diǎn),請(qǐng)使用局部變量存儲(chǔ)對(duì)它的引用。
謹(jǐn)慎處理HTML集合(HTML集合實(shí)時(shí)連系底層文檔),把集合的長(zhǎng)度緩存到一個(gè)變量中,并在迭代中使用它,如果需要經(jīng)常操作集合,建議把它拷貝到一個(gè)數(shù)組中。
如果可能的話,使用速度更快的API,比如querySelectorAll和firstElementChild。
要留意重繪和重排。
批量修改樣式時(shí),離線操作DOM樹(shù)。
使用緩存,并減少訪問(wèn)布局的次數(shù)。
動(dòng)畫(huà)中使用絕對(duì)定位,使用拖放代理。
使用事件委托來(lái)減少事件處理器的數(shù)量。
優(yōu)化DOM交互
在JAVASCRIPT中,DOM操作和交互要消耗大量時(shí)間,因?yàn)樗鼈兺枰匦落秩菊麄€(gè)頁(yè)面或者某一個(gè)部分。
最小化現(xiàn)場(chǎng)更新。
當(dāng)需要訪問(wèn)的DOM部分已經(jīng)已經(jīng)被渲染為頁(yè)面中的一部分,那么DOM操作和交互的過(guò)程就是再進(jìn)行一次現(xiàn)場(chǎng)更新。
現(xiàn)場(chǎng)更新是需要針對(duì)現(xiàn)場(chǎng)(相關(guān)顯示頁(yè)面的部分結(jié)構(gòu))立即進(jìn)行更新,每一個(gè)更改(不管是插入單個(gè)字符還是移除整個(gè)片段),都有一個(gè)性能損耗。
現(xiàn)場(chǎng)更新進(jìn)行的越多,代碼完成執(zhí)行所花的時(shí)間也越長(zhǎng)。
多使用innerHTML。
有兩種在頁(yè)面上創(chuàng)建DOM節(jié)點(diǎn)的方法:
使用諸如createElement()和appendChild()之類的DOM方法。
使用innerHTML。
當(dāng)使用innerHTML設(shè)置為某個(gè)值時(shí),后臺(tái)會(huì)創(chuàng)建一個(gè)HTML解釋器,然后使用內(nèi)部的DOM調(diào)用來(lái)創(chuàng)建DOM結(jié)構(gòu),而非基于JAVASCRIPT的DOM調(diào)用。由于內(nèi)部方法是編譯好的而非解釋執(zhí)行,故執(zhí)行的更快。
對(duì)于小的DOM更改,兩者效率差不多,但對(duì)于大的DOM更改,innerHTML要比標(biāo)準(zhǔn)的DOM方法創(chuàng)建同樣的DOM結(jié)構(gòu)快得多。
回流reflow。
發(fā)生場(chǎng)景。
改變窗體大小。
更改字體。
添加移除stylesheet塊。
內(nèi)容改變哪怕是輸入框輸入文字。
CSS虛類被觸發(fā)如 :hover。
更改元素的className。
當(dāng)對(duì)DOM節(jié)點(diǎn)執(zhí)行新增或者刪除操作或內(nèi)容更改時(shí)。
動(dòng)態(tài)設(shè)置一個(gè)style樣式時(shí)(比如element.style.width="10px")。
當(dāng)獲取一個(gè)必須經(jīng)過(guò)計(jì)算的尺寸值時(shí),比如訪問(wèn)offsetWidth、clientHeight或者其他需要經(jīng)過(guò)計(jì)算的CSS值。
解決問(wèn)題的關(guān)鍵,就是限制通過(guò)DOM操作所引發(fā)回流的次數(shù)。
在對(duì)當(dāng)前DOM進(jìn)行操作之前,盡可能多的做一些準(zhǔn)備工作,保證N次創(chuàng)建,1次寫(xiě)入。
在對(duì)DOM操作之前,把要操作的元素,先從當(dāng)前DOM結(jié)構(gòu)中刪除:
通過(guò)removeChild()或者replaceChild()實(shí)現(xiàn)真正意義上的刪除。
設(shè)置該元素的display樣式為“none”。
每次修改元素的style屬性都會(huì)觸發(fā)回流操作。
element.style.backgroundColor = "blue";
使用更改className的方式替換style.xxx=xxx的方式。
使用style.cssText = "";一次寫(xiě)入樣式。
避免設(shè)置過(guò)多的行內(nèi)樣式。
添加的結(jié)構(gòu)外元素盡量設(shè)置它們的位置為fixed或absolute。
避免使用表格來(lái)布局。
避免在CSS中使用JavaScript expressions(IE only)。
將獲取的DOM數(shù)據(jù)緩存起來(lái)。這種方法,對(duì)獲取那些會(huì)觸發(fā)回流操作的屬性(比如offsetWidth等)尤為重要。
當(dāng)對(duì)HTMLCollection對(duì)象進(jìn)行操作時(shí),應(yīng)該將訪問(wèn)的次數(shù)盡可能的降至最低,最簡(jiǎn)單的,你可以將length屬性緩存在一個(gè)本地變量中,這樣就能大幅度的提高循環(huán)的效率。
HTML優(yōu)化
避免eval:
eval會(huì)在時(shí)間方面帶來(lái)一些效率,但也有很多缺點(diǎn)。
eval會(huì)導(dǎo)致代碼看起來(lái)更臟。
eval會(huì)需要消耗大量時(shí)間。
eval會(huì)逃過(guò)大多數(shù)壓縮工具的壓縮。
JIT與GC優(yōu)化
插入HTML。
JavaScript中使用document.write生成頁(yè)面內(nèi)容會(huì)效率較低,可以找一個(gè)容器元素,比如指定一個(gè)div,并使用innerHTML來(lái)將HTML代碼插入到頁(yè)面中。
避免空的src和href。
當(dāng)link標(biāo)簽的href屬性為空、script標(biāo)簽的src屬性為空的時(shí)候,瀏覽器渲染的時(shí)候會(huì)把當(dāng)前頁(yè)面的URL作為它們的屬性值,從而把頁(yè)面的內(nèi)容加載進(jìn)來(lái)作為它們的值。
為文件頭指定Expires。
使內(nèi)容具有緩存性,避免了接下來(lái)的頁(yè)面訪問(wèn)中不必要的HTTP請(qǐng)求。
重構(gòu)HTML,把重要內(nèi)容的優(yōu)先級(jí)提高。
Post-load(次要加載)不是必須的資源。
利用預(yù)加載優(yōu)化資源。
合理架構(gòu),使DOM結(jié)構(gòu)盡量簡(jiǎn)單。
利用LocalStorage合理緩存資源。
盡量避免CSS表達(dá)式和濾鏡。
嘗試使用defer方式加載Js腳本。
新特性:will-change,把即將發(fā)生的改變預(yù)先告訴瀏覽器。
新特性Beacon,不堵塞隊(duì)列的異步數(shù)據(jù)發(fā)送。
不同之處:網(wǎng)絡(luò)緩慢,緩存更小,不令人滿意的瀏覽器處理機(jī)制。
盡量多地緩存文件。
使用HTML5 Web Workers來(lái)允許多線程工作。
為不同的Viewports設(shè)置不同大小的Content。
正確設(shè)置可Tap的目標(biāo)的大小。
使用響應(yīng)式圖片。
支持新接口協(xié)議(如HTTP2)。
未來(lái)的緩存離線機(jī)制:Service Workers。
未來(lái)的資源優(yōu)化Resource Hints(preconnect, preload, 和prerender)。
使用Server-sent Events。
設(shè)置一個(gè)Meta Viewport。
js載入優(yōu)化
untyped(無(wú)類型)。
JAVASCRIPT是個(gè)無(wú)類型的語(yǔ)言,這導(dǎo)致了如x=y+z這種表達(dá)式可以有很多含義。
y,z是數(shù)字,則+表示加法。
y,z是字符串,則+表示字符串連接。
而JS引擎內(nèi)部則使用“細(xì)粒度”的類型,比如:
32-bit* integer。
64-bit* floating-point。
這就要求js類型-js引擎類型,需要做“boxed/unboxed(裝箱/解箱)”,在處理一次x=y+z這種計(jì)算,需要經(jīng)過(guò)的步驟如下。
從內(nèi)存,讀取x=y+z的操作符。
從內(nèi)存,讀取y,z。
檢查y,z類型,確定操作的行為。
unbox y,z。
執(zhí)行操作符的行為。
box x。
把x寫(xiě)入內(nèi)存。
只有第5步驟是真正有效的操作,其他步驟都是為第5步驟做準(zhǔn)備/收尾,JAVASCRIPT的untyped特性很好用,但也為此付出了很大的性能代價(jià)。
JIT。
先看看JIT對(duì)untyped的優(yōu)化,在JIT下,執(zhí)行x=y+z流程。
從內(nèi)存,讀取x=y+z的操作符。
從內(nèi)存,讀取 y,z。
檢查y,z類型,確定操作的行為。
unbox y,z。
執(zhí)行 操作符 的行為。
box x。
把x寫(xiě)入內(nèi)存。
其中1,2步驟由CPU負(fù)責(zé),7步驟JIT把結(jié)果保存在寄存器里。但可惜不是所有情況都能使用JIT,當(dāng)number+number,string+string 等等可以使用JIT,但特殊情況,如:number+undefined就不行了,只能走舊解析器。
新引擎還對(duì)“對(duì)象屬性”訪問(wèn)做了優(yōu)化,解決方案叫inline caching,簡(jiǎn)稱:IC。簡(jiǎn)單的說(shuō),就是做cache。但如果當(dāng)list很大時(shí),這種方案反而影響效率。
Type-specializing JIT
Type-specializing JIT引擎用來(lái)處理typed類型(聲明類型)變量,但JAVASCRIPT都是untype類型的。
Type-specializing JIT的解決方案是:
先通過(guò)掃描,監(jiān)測(cè)類型。
通過(guò)編譯優(yōu)化(優(yōu)化對(duì)象不僅僅只是“類型”,還包括對(duì)JS代碼的優(yōu)化,但核心是類型優(yōu)化),生成類型變量。
再做后續(xù)計(jì)算。
Type-specializing JIT的執(zhí)行x=y+z流程:
從內(nèi)存,讀取x=y+z的操作符。
從內(nèi)存,讀取y,z。
檢查y,z類型,確定操作的行為。
unbox y,z。
執(zhí)行操作符的行為。
box x。
把x寫(xiě)入內(nèi)存。
代價(jià)是:
前置的掃描類型
編譯優(yōu)化。
所以·Type-specializing JIT·的應(yīng)用是有選擇性,選擇使用這個(gè)引擎的場(chǎng)景包括:
熱點(diǎn)代碼。
通過(guò)啟發(fā)式算法估算出來(lái)的有價(jià)值的代碼。
另外,有2點(diǎn)也需要注意:
當(dāng)變量類型 發(fā)生變化時(shí),引擎有2種處理方式:
少量變更,重編譯,再執(zhí)行。
大量變更,交給JIT執(zhí)行。
數(shù)組,object properties, 閉包變量 不在優(yōu)化范疇之列。
with優(yōu)化
加快JavaScript裝入速度的工具:
Lab.js
借助LAB.js(裝入和阻止JavaScript),你就可以并行裝入JavaScript文件,加快總的裝入過(guò)程。此外,你還可以為需要裝入的腳本設(shè)置某個(gè)順序,那樣就能確保依賴關(guān)系的完整性。此外,開(kāi)發(fā)者聲稱其網(wǎng)站上的速度提升了2倍。
使用適當(dāng)?shù)腃DN:
現(xiàn)在許多網(wǎng)頁(yè)使用內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN)。它可以改進(jìn)你的緩存機(jī)制,因?yàn)槊總€(gè)人都可以使用它。它還能為你節(jié)省一些帶寬。你很容易使用ping檢測(cè)或使用Firebug調(diào)試那些服務(wù)器,以便搞清可以從哪些方面加快數(shù)據(jù)的速度。選擇CDN時(shí),要照顧到你網(wǎng)站那些訪客的位置。記得盡可能使用公共存儲(chǔ)庫(kù)。
網(wǎng)頁(yè)末尾裝入JavaScript:
也可以在頭部分放置需要裝入的一些JavaScript,但是前提是它以異步方式裝入。
異步裝入跟蹤代碼:
腳本加載與解析會(huì)阻塞HTML渲染,可以通過(guò)異步加載方式來(lái)避免渲染阻塞,步加載的方式很多,比較通用的方法如下。
var _gaq = _gaq || []; _gaq.push(["_setAccount", "UA-XXXXXXX-XX"]); _gaq.push(["_trackPageview"]); (function() { var ga = document.createElement("script"); ga.type = "text/JavaScript"; ga.async = true; ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s); })();或者
function loadjs (script_filename){ var script = document.createElement( "script" ); script.setAttribute( "type" , "text/javascript" ); script.setAttribute( "src" , script_filename); script.setAttribute( "id" , "script-id" ); scriptElement = document.getElementById( "script-id" ); if (scriptElement){ document.getElementsByTagName( "head" )[0].removeChild(scriptElement); } document.getElementsByTagName( "head" )[0].appendChild(script); } var script = "scripts/alert.js" ; loadjs(script);
把你的JavaScript打包成PNG文件
將JavaScript/css數(shù)據(jù)打包成PNG文件。之后進(jìn)行拆包,只要使用畫(huà)布API的getImageData()??梢栽诓豢s小數(shù)據(jù)的情況下,多壓縮35%左右。而且是無(wú)損壓縮,對(duì)比較龐大的腳本來(lái)說(shuō),在圖片指向畫(huà)布、讀取像素的過(guò)程中,你會(huì)覺(jué)得有“一段”裝入時(shí)間。
設(shè)置Cache-Control和Expires頭
通過(guò)Cache-Control和Expires頭可以將腳本文件緩存在客戶端或者代理服務(wù)器上,可以減少腳本下載的時(shí)間。
Expires格式:
Expires = "Expires" ":" HTTP-date Expires: Thu, 01 Dec 1994 16:00:00 GMT Note: if a response includes a Cache-Control field with the max-age directive that directive overrides the Expires field.Cache-Control格式:
Cache-Control = "Cache-Control" ":" 1#cache-directive Cache-Control: public具體的標(biāo)準(zhǔn)定義可以參考http1.1中的定義,簡(jiǎn)單來(lái)說(shuō)Expires控制過(guò)期時(shí)間是多久,Cache-Control控制什么地方可以緩存 。
變量專題盡可能地少用with語(yǔ)句,因?yàn)樗鼤?huì)增加with語(yǔ)句以外的數(shù)據(jù)的訪問(wèn)代價(jià)。
避免使用with
> `with`語(yǔ)句將一個(gè)新的可變對(duì)象推入作用域鏈的頭部,函數(shù)的所有局部變量現(xiàn)在處于第二個(gè)作用域鏈對(duì)象中,從而使局部變量的訪問(wèn)代價(jià)提高。var person = {
name: “Nicholas", age: 30}
function displayInfo() {var count = 5; with (person) { alert(name + " is " + age); alert( "count is " + count); }}
常規(guī)優(yōu)化
全局變量
當(dāng)一個(gè)變量被定義在全局作用域中,默認(rèn)情況下JAVASCRIPT引擎就不會(huì)將其回收銷毀。如此該變量就會(huì)一直存在于老生代堆內(nèi)存中,直到頁(yè)面被關(guān)閉。
全局變量缺點(diǎn)。
使變量不易被回收。
多人協(xié)作時(shí)容易產(chǎn)生混淆。
在作用域鏈中容易被干擾。
可以通過(guò)包裝函數(shù)來(lái)處理全局變量。
局部變量。
盡量選用局部變量而不是全局變量。
局部變量的訪問(wèn)速度要比全局變量的訪問(wèn)速度更快,因?yàn)槿肿兞科鋵?shí)是window對(duì)象的成員,而局部變量是放在函數(shù)的棧里的。
手工解除變量引用
在業(yè)務(wù)代碼中,一個(gè)變量已經(jīng)確定不再需要了,那么就可以手工解除變量引用,以使其被回收。
var data = { / some big data / };
// ...
data = null;
變量查找優(yōu)化。
變量聲明帶上var,如果聲明變量忘記了var,那么JAVASCRIPT引擎將會(huì)遍歷整個(gè)作用域查找這個(gè)變量,結(jié)果不管找到與否,都會(huì)造成性能損耗。
如果在上級(jí)作用域找到了這個(gè)變量,上級(jí)作用域變量的內(nèi)容將被無(wú)聲的改寫(xiě),導(dǎo)致莫名奇妙的錯(cuò)誤發(fā)生。
如果在上級(jí)作用域沒(méi)有找到該變量,這個(gè)變量將自動(dòng)被聲明為全局變量,然而卻都找不到這個(gè)全局變量的定義。
慎用全局變量。
全局變量需要搜索更長(zhǎng)的作用域鏈。
全局變量的生命周期比局部變量長(zhǎng),不利于內(nèi)存釋放。
過(guò)多的全局變量容易造成混淆,增大產(chǎn)生bug的可能性。
具有相同作用域變量通過(guò)一個(gè)var聲明。
jQuery.extend = jQuery.fn.extend = function () { var options, name, src, copy, copyIsArray, clone,target = arguments[0] || {}, i = 1, length = arguments.length, deep = false ; }
緩存重復(fù)使用的全局變量。
全局變量要比局部變量需要搜索的作用域長(zhǎng)
重復(fù)調(diào)用的方法也可以通過(guò)局部緩存來(lái)提速
該項(xiàng)優(yōu)化在IE上體現(xiàn)比較明顯
var docElem = window.document.documentElement,
selector_hasDuplicate, matches = docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector ||docElem.msMatchesSelector, selector_sortOrder = function ( a, b ) { // Flag for duplicate removal if ( a === b ) { selector_hasDuplicate = true ; return 0; } }
善用回調(diào)。
除了使用閉包進(jìn)行內(nèi)部變量訪問(wèn),我們還可以使用現(xiàn)在十分流行的回調(diào)函數(shù)來(lái)進(jìn)行業(yè)務(wù)處理。
function getData(callback) { var data = "some big data"; callback(null, data); } getData(function(err, data) { console.log(data); });
回調(diào)函數(shù)是一種后續(xù)傳遞風(fēng)格(Continuation Passing Style, CPS)的技術(shù),這種風(fēng)格的程序編寫(xiě)將函數(shù)的業(yè)務(wù)重點(diǎn)從返回值轉(zhuǎn)移到回調(diào)函數(shù)中去。而且其相比閉包的好處也有很多。
如果傳入的參數(shù)是基礎(chǔ)類型(如字符串、數(shù)值),回調(diào)函數(shù)中傳入的形參就會(huì)是復(fù)制值,業(yè)務(wù)代碼使用完畢以后,更容易被回收。
通過(guò)回調(diào),我們除了可以完成同步的請(qǐng)求外,還可以用在異步編程中,這也就是現(xiàn)在非常流行的一種編寫(xiě)風(fēng)格。
回調(diào)函數(shù)自身通常也是臨時(shí)的匿名函數(shù),一旦請(qǐng)求函數(shù)執(zhí)行完畢,回調(diào)函數(shù)自身的引用就會(huì)被解除,自身也得到回收。
代碼壓縮
傳遞方法取代方法字符串
一些方法例如setTimeout()、setInterval(),接受字符串或者方法實(shí)例作為參數(shù)。直接傳遞方法對(duì)象作為參數(shù)來(lái)避免對(duì)字符串的二次解析。
傳遞方法
setTimeout(test, 1);
傳遞方法字符串
setTimeout("test()", 1);
使用原始操作代替方法調(diào)用
方法調(diào)用一般封裝了原始操作,在性能要求高的邏輯中,可以使用原始操作代替方法調(diào)用來(lái)提高性能。
原始操作
var min = a
方法實(shí)例
var min = Math.min(a, b);
定時(shí)器
如果針對(duì)的是不斷運(yùn)行的代碼,不應(yīng)該使用setTimeout,而應(yīng)該是用setInterval。setTimeout每次要重新設(shè)置一個(gè)定時(shí)器。
避免雙重解釋
當(dāng)JAVASCRIPT代碼想解析JAVASCRIPT代碼時(shí)就會(huì)存在雙重解釋?xiě)土P,雙重解釋一般在使用eval函數(shù)、new Function構(gòu)造函數(shù)和setTimeout傳一個(gè)字符串時(shí)等情況下會(huì)遇到,如。
eval("alert("hello world");"); var sayHi = new Function("alert("hello world");"); setTimeout("alert("hello world");", 100);上述alert("hello world");語(yǔ)句包含在字符串中,即在JS代碼運(yùn)行的同時(shí)必須新啟運(yùn)一個(gè)解析器來(lái)解析新的代碼,而實(shí)例化一個(gè)新的解析器有很大的性能損耗。
我們看看下面的例子: var sum, num1 = 1, num2 = 2; /**效率低**/ for(var i = 0; i < 10000; i++){ var func = new Function("sum+=num1;num1+=num2;num2++;"); func(); //eval("sum+=num1;num1+=num2;num2++;"); } /**效率高**/ for(var i = 0; i < 10000; i++){ sum+=num1; num1+=num2; num2++; }第一種情況我們是使用了new Function來(lái)進(jìn)行雙重解釋,而第二種是避免了雙重解釋。
原生方法更快
只要有可能,使用原生方法而不是自已用JS重寫(xiě)。原生方法是用諸如C/C++之類的編譯型語(yǔ)言寫(xiě)出來(lái)的,要比JS的快多了。
最小化語(yǔ)句數(shù)
JS代碼中的語(yǔ)句數(shù)量也會(huì)影響所執(zhí)行的操作的速度,完成多個(gè)操作的單個(gè)語(yǔ)句要比完成單個(gè)操作的多個(gè)語(yǔ)句塊快。故要找出可以組合在一起的語(yǔ)句,以減來(lái)整體的執(zhí)行時(shí)間。這里列舉幾種模式
多個(gè)變量聲明
/不提倡/
var i = 1;
var j = "hello";
var arr = [1,2,3];
var now = new Date();
/提倡/
var i = 1,j = "hello", arr = [1,2,3], now = new Date();
插入迭代值
/不提倡/
var name = values[i];
i++;
/提倡/
var name = values[i++];
使用數(shù)組和對(duì)象字面量,避免使用構(gòu)造函數(shù)Array(),Object()
/不提倡/
var a = new Array();
a[0] = 1;
a[1] = "hello";
a[2] = 45;
var o = new Obejct();
o.name = "bill";
o.age = 13;
/提倡/
var a = [1, "hello", 45];
var o = {name : "bill", age : 13};
避免使用屬性訪問(wèn)方法
JavaScript不需要屬性訪問(wèn)方法,因?yàn)樗械膶傩远际峭獠靠梢?jiàn)的。
添加屬性訪問(wèn)方法只是增加了一層重定向 ,對(duì)于訪問(wèn)控制沒(méi)有意義。
使用屬性訪問(wèn)方法示例function Car() {
this .m_tireSize = 17;
this .m_maxSpeed = 250;
this .GetTireSize = Car_get_tireSize;
this .SetTireSize = Car_put_tireSize;
}function Car_get_tireSize() {
return this .m_tireSize;
}function Car_put_tireSize(value) {
this .m_tireSize = value;
}
var ooCar = new Car();
var iTireSize = ooCar.GetTireSize();
ooCar.SetTireSize(iTireSize + 1);直接訪問(wèn)屬性示例function Car() {
this .m_tireSize = 17;
this .m_maxSpeed = 250;
}
var perfCar = new Car();
var iTireSize = perfCar.m_tireSize;
perfCar.m_tireSize = iTireSize + 1;
減少使用元素位置操作
一般瀏覽器都會(huì)使用增量reflow的方式將需要reflow的操作積累到一定程度然后再一起觸發(fā),但是如果腳本中要獲取以下屬性,那么積累的reflow將會(huì)馬上執(zhí)行,已得到準(zhǔn)確的位置信息。
offsetLeft offsetTop offsetHeight offsetWidth scrollTop/Left/Width/Height clientTop/Left/Width/Height getComputedStyle()
代碼優(yōu)化
代碼壓縮工具
精簡(jiǎn)代碼就是將代碼中的空格和注釋去除,也有更進(jìn)一步的會(huì)對(duì)變量名稱混淆、精簡(jiǎn)。根據(jù)統(tǒng)計(jì)精簡(jiǎn)后文件大小會(huì)平均減少21%,即使Gzip之后文件也會(huì)減少5%。
YUICompressor
Dean Edwards Packer
JSMin
GZip壓縮
GZip縮短在瀏覽器和服務(wù)器之間傳送數(shù)據(jù)的時(shí)間,縮短時(shí)間后得到標(biāo)題是Accept-Encoding: gzip,deflate的一個(gè)文件。不過(guò)這種壓縮方法同樣也有缺點(diǎn)。
它在服務(wù)器端和客戶端都要占用處理器資源(以便壓縮和解壓縮)。
占用磁盤空間。
Gzip通常可以減少70%網(wǎng)頁(yè)內(nèi)容的大小,包括腳本、樣式表、圖片等任何一個(gè)文本類型的響應(yīng),包括XML和JSON。Gzip比deflate更高效,主流服務(wù)器都有相應(yīng)的壓縮支持模塊。
Gzip的工作流程為
客戶端在請(qǐng)求Accept-Encoding中聲明可以支持Gzip。
服務(wù)器將請(qǐng)求文檔壓縮,并在Content-Encoding中聲明該回復(fù)為Gzip格式。
客戶端收到之后按照Gzip解壓縮。
Closure compiler
動(dòng)畫(huà)優(yōu)化
優(yōu)化原則:
JS與其他語(yǔ)言不同在于它的執(zhí)行效率很大程度是取決于JS engine的效率。除了引擎實(shí)現(xiàn)的優(yōu)劣外,引擎自己也會(huì)為一些特殊的代碼模式采取一些優(yōu)化的策略。例如FF、Opera和Safari的JAVASCRIPT引擎,都對(duì)字符串的拼接運(yùn)算(+)做了特別優(yōu)化。所以應(yīng)該根據(jù)不同引擎進(jìn)行不同優(yōu)化。
而如果做跨瀏覽器的web編程,則最大的問(wèn)題是在于IE6(JScript 5.6),因?yàn)樵诓淮騢otfix的情況下,JScript引擎的垃圾回收的bug,會(huì)導(dǎo)致其在真實(shí)應(yīng)用中的performance跟其他瀏覽器根本不在一個(gè)數(shù)量級(jí)上。因此在這種場(chǎng)合做優(yōu)化,實(shí)際上就是為JScript做優(yōu)化,所以第一原則就是只需要為IE6(未打補(bǔ)丁的JScript 5.6或更早版本)做優(yōu)化。
JS優(yōu)化總是出現(xiàn)在大規(guī)模循環(huán)的地方:
這倒不是說(shuō)循環(huán)本身有性能問(wèn)題,而是循環(huán)會(huì)迅速放大可能存在的性能問(wèn)題,所以第二原則就是以大規(guī)模循環(huán)體為最主要優(yōu)化對(duì)象。以下的優(yōu)化原則,只在大規(guī)模循環(huán)中才有意義,在循環(huán)體之外做此類優(yōu)化基本上是沒(méi)有意義的。
目前絕大多數(shù)JS引擎都是解釋執(zhí)行的,而解釋執(zhí)行的情況下,在所有操作中,函數(shù)調(diào)用的效率是較低的。此外,過(guò)深的prototype繼承鏈或者多級(jí)引用也會(huì)降低效率。JScript中,10級(jí)引用的開(kāi)銷大體是一次空函數(shù)調(diào)用開(kāi)銷的1/2。這兩者的開(kāi)銷都遠(yuǎn)遠(yuǎn)大于簡(jiǎn)單操作(如四則運(yùn)算)。
盡量避免過(guò)多的引用層級(jí)和不必要的多次方法調(diào)用:
特別要注意的是,有些情況下看似是屬性訪問(wèn),實(shí)際上是方法調(diào)用。例如所有DOM的屬性,實(shí)際上都是方法。在遍歷一個(gè)NodeList的時(shí)候,循環(huán) 條件對(duì)于nodes.length的訪問(wèn),看似屬性讀取,實(shí)際上是等價(jià)于函數(shù)調(diào)用的。而且IE DOM的實(shí)現(xiàn)上,childNodes.length每次是要通過(guò)內(nèi)部遍歷重新計(jì)數(shù)的。(My god,但是這是真的!因?yàn)槲覝y(cè)過(guò),childNodes.length的訪問(wèn)時(shí)間與childNodes.length的值成正比!)這非常耗費(fèi)。所以 預(yù)先把nodes.length保存到j(luò)s變量,當(dāng)然可以提高遍歷的性能。同樣是函數(shù)調(diào)用,用戶自定義函數(shù)的效率又遠(yuǎn)遠(yuǎn)低于語(yǔ)言內(nèi)建函數(shù),因?yàn)楹笳呤菍?duì)引擎本地方法的包裝,而引擎通常是c,c++,java寫(xiě)的。進(jìn)一步,同樣的功能,語(yǔ)言內(nèi)建構(gòu)造的開(kāi)銷通常又比內(nèi)建函數(shù)調(diào)用要效率高,因?yàn)榍罢咴贘S代碼的parse階段就可以確定和優(yōu)化。
盡量使用語(yǔ)言本身的構(gòu)造和內(nèi)建函數(shù):
這里有一個(gè)例子是高性能的String.format方法。 String.format傳統(tǒng)的實(shí)現(xiàn)方式是用String.replace(regex, func),在pattern包含n個(gè)占位符(包括重復(fù)的)時(shí),自定義函數(shù)func就被調(diào)用n次。而這個(gè)高性能實(shí)現(xiàn)中,每次format調(diào)用所作的只是一次Array.join然后一次String.replace(regex, string)的操作,兩者都是引擎內(nèi)建方法,而不會(huì)有任何自定義函數(shù)調(diào)用。兩次內(nèi)建方法調(diào)用和n次的自定義方法調(diào)用,這就是性能上的差別。同樣是內(nèi)建特性,性能上也還是有差別的。例如在JScript中對(duì)于arguments的訪問(wèn)性能就很差,幾乎趕上一次函數(shù)調(diào)用了。因此如果一個(gè) 可變參數(shù)的簡(jiǎn)單函數(shù)成為性能瓶頸的時(shí)候,可以將其內(nèi)部做一些改變,不要訪問(wèn)arguments,而是通過(guò)對(duì)參數(shù)的顯式判斷來(lái)處理,比如:
對(duì)象專題
動(dòng)畫(huà)效果在缺少硬件加速支持的情況下反應(yīng)緩慢,例如手機(jī)客戶端。
特效應(yīng)該只在確實(shí)能改善用戶體驗(yàn)時(shí)才使用,而不應(yīng)用于炫耀或者彌補(bǔ)功能與可用性上的缺陷。
至少要給用戶一個(gè)選擇可以禁用動(dòng)畫(huà)效果。
設(shè)置動(dòng)畫(huà)元素為absolute或fixed。
position: static或position: relative元素應(yīng)用動(dòng)畫(huà)效果會(huì)造成頻繁的reflow。
position: absolute或position: fixed的元素應(yīng)用動(dòng)畫(huà)效果只需要repaint。
使用一個(gè)timer完成多個(gè)元素動(dòng)畫(huà)。
setInterval和setTimeout是兩個(gè)常用的實(shí)現(xiàn)動(dòng)畫(huà)的接口,用以間隔更新元素的風(fēng)格與布局。。
動(dòng)畫(huà)效果的幀率最優(yōu)化的情況是使用一個(gè)timer完成多個(gè)對(duì)象的動(dòng)畫(huà)效果,其原因在于多個(gè)timer的調(diào)用本身就會(huì)損耗一定性能。
setInterval(function() { animateFirst(""); }, 10); setInterval(function() { animateSecond(""); }, 10);使用同一個(gè)timer。
setInterval(function() { animateFirst(""); animateSecond(""); }, 10);以腳本為基礎(chǔ)的動(dòng)畫(huà),由瀏覽器控制動(dòng)畫(huà)的更新頻率。
服務(wù)端優(yōu)化
減少不必要的對(duì)象創(chuàng)建:
創(chuàng)建對(duì)象本身對(duì)性能影響并不大,但由于JAVASCRIPT的垃圾回收調(diào)度算法,導(dǎo)致隨著對(duì)象個(gè)數(shù)的增加,性能會(huì)開(kāi)始嚴(yán)重下降(復(fù)雜度O(n^2))。
如常見(jiàn)的字符串拼接問(wèn)題,單純的多次創(chuàng)建字符串對(duì)象其實(shí)根本不是降低性能的主要原因,而是是在對(duì)象創(chuàng)建期間的無(wú)謂的垃圾回收的開(kāi)銷。而Array.join的方式,不會(huì)創(chuàng)建中間字符串對(duì)象,因此就減少了垃圾回收的開(kāi)銷。
復(fù)雜的JAVASCRIPT對(duì)象,其創(chuàng)建時(shí)時(shí)間和空間的開(kāi)銷都很大,應(yīng)該盡量考慮采用緩存。
盡量作用JSON格式來(lái)創(chuàng)建對(duì)象,而不是var obj=new Object()方法。前者是直接復(fù)制,而后者需要調(diào)用構(gòu)造器。
對(duì)象查找
避免對(duì)象的嵌套查詢,因?yàn)?b>JAVASCRIPT的解釋性,a.b.c.d.e嵌套對(duì)象,需要進(jìn)行4次查詢,嵌套的對(duì)象成員會(huì)明顯影響性能。
如果出現(xiàn)嵌套對(duì)象,可以利用局部變量,把它放入一個(gè)臨時(shí)的地方進(jìn)行查詢。
對(duì)象屬性
訪問(wèn)對(duì)象屬性消耗性能過(guò)程(JAVASCRIPT對(duì)象存儲(chǔ))。
先從本地變量表找到對(duì)象。
然后遍歷屬性。
如果在當(dāng)前對(duì)象的屬性列表里沒(méi)找到。
繼續(xù)從prototype向上查找。
且不能直接索引,只能遍歷。
function f(obj) {
return obj.a + 1;}
類型轉(zhuǎn)換專題
避免404。
更改404錯(cuò)誤響應(yīng)頁(yè)面可以改進(jìn)用戶體驗(yàn),但是同樣也會(huì)浪費(fèi)服務(wù)器資源。
指向外部JAVASCRIPT的鏈接出現(xiàn)問(wèn)題并返回404代碼。
這種加載會(huì)破壞并行加載。
其次瀏覽器會(huì)把試圖在返回的404響應(yīng)內(nèi)容中找到可能有用的部分當(dāng)作JavaScript代碼來(lái)執(zhí)行。
刪除重復(fù)的JAVASCRIPT和CSS。
重復(fù)調(diào)用腳本缺點(diǎn)。
增加額外的HTTP請(qǐng)求。
多次運(yùn)算也會(huì)浪費(fèi)時(shí)間。在IE和Firefox中不管腳本是否可緩存,它們都存在重復(fù)運(yùn)算JAVASCRIPT的問(wèn)題。
ETags配置Entity標(biāo)簽。
ETags用來(lái)判斷瀏覽器緩存里的元素是否和原來(lái)服務(wù)器上的一致。
與last-modified date相比更靈活。
>如某個(gè)文件在1秒內(nèi)修改了10次,`ETags`可以綜合`Inode`(文件的索引節(jié)點(diǎn)`inode`數(shù)),`MTime`(修改時(shí)間)和`Size`來(lái)精準(zhǔn)的進(jìn)行判斷,避開(kāi)`UNIX`記錄`MTime`只能精確到秒的問(wèn)題。服務(wù)器集群使用,可取后兩個(gè)參數(shù)。使用`ETags`減少`Web`應(yīng)用帶寬和負(fù)載
權(quán)衡DNS查找次數(shù)
減少主機(jī)名可以節(jié)省響應(yīng)時(shí)間。但同時(shí)也會(huì)減少頁(yè)面中并行下載的數(shù)量。
IE瀏覽器在同一時(shí)刻只能從同一域名下載兩個(gè)文件。當(dāng)在一個(gè)頁(yè)面顯示多張圖片時(shí),IE用戶的圖片下載速度就會(huì)受到影響。
通過(guò)Keep-alive機(jī)制減少TCP連接。
通過(guò)CDN減少延時(shí)。
平行處理請(qǐng)求(參考BigPipe)。
通過(guò)合并文件或者Image Sprites減少HTTP請(qǐng)求。
減少重定向( HTTP 301和40x/50x)。
邏輯判斷優(yōu)化
把數(shù)字轉(zhuǎn)換成字符串。
應(yīng)用""+1,效率是最高。
性能上來(lái)說(shuō):""+字符串>String()>.toString()>new String()。
String()屬于內(nèi)部函數(shù),所以速度很快。
.toString()要查詢?cè)椭械暮瘮?shù),所以速度略慢。
new String()最慢。
浮點(diǎn)數(shù)轉(zhuǎn)換成整型。
錯(cuò)誤使用使用parseInt()。
parseInt()是用于將字符串轉(zhuǎn)換成數(shù)字,而不是浮點(diǎn)數(shù)和整型之間的轉(zhuǎn)換。
應(yīng)該使用Math.floor()或者Math.round()。
Math是內(nèi)部對(duì)象,所以Math.floor()其實(shí)并沒(méi)有多少查詢方法和調(diào)用的時(shí)間,速度是最快的。
內(nèi)存專題
switch語(yǔ)句。
若有一系列復(fù)雜的if-else語(yǔ)句,可以轉(zhuǎn)換成單個(gè)switch語(yǔ)句則可以得到更快的代碼,還可以通過(guò)將case語(yǔ)句按照最可能的到最不可能的順序進(jìn)行組織,來(lái)進(jìn)一步優(yōu)化。
事件優(yōu)化
JAVASCRIPT的內(nèi)存回收機(jī)制
以Google的V8引擎為例,在V8引擎中所有的JAVASCRIPT對(duì)象都是通過(guò)堆來(lái)進(jìn)行內(nèi)存分配的。當(dāng)我們?cè)诖a中聲明變量并賦值時(shí),V8引擎就會(huì)在堆內(nèi)存中分配一部分給這個(gè)變量。如果已申請(qǐng)的內(nèi)存不足以存儲(chǔ)這個(gè)變量時(shí),V8引擎就會(huì)繼續(xù)申請(qǐng)內(nèi)存,直到堆的大小達(dá)到了V8引擎的內(nèi)存上限為止(默認(rèn)情況下,V8引擎的堆內(nèi)存的大小上限在64位系統(tǒng)中為1464MB,在32位系統(tǒng)中則為732MB)。
另外,V8引擎對(duì)堆內(nèi)存中的JAVASCRIPT對(duì)象進(jìn)行分代管理。
新生代。
新生代即存活周期較短的JAVASCRIPT對(duì)象,如臨時(shí)變量、字符串等
老生代。
老生代則為經(jīng)過(guò)多次垃圾回收仍然存活,存活周期較長(zhǎng)的對(duì)象,如主控制器、服務(wù)器對(duì)象等。
垃圾回收算法。
垃圾回收算法一直是編程語(yǔ)言的研發(fā)中是否重要的??一環(huán),而V8引擎所使用的垃圾回收算法主要有以下幾種。
Scavange算法:通過(guò)復(fù)制的方式進(jìn)行內(nèi)存空間管理,主要用于新生代的內(nèi)存空間;
Mark-Sweep算法和Mark-Compact算法:通過(guò)標(biāo)記來(lái)對(duì)堆內(nèi)存進(jìn)行整理和回收,主要用于老生代對(duì)象的檢查和回收。
對(duì)象進(jìn)行回收。
引用。
當(dāng)函數(shù)執(zhí)行完畢時(shí),在函數(shù)內(nèi)部所聲明的對(duì)象不一定就會(huì)被銷毀。
引用(Reference)是JAVASCRIPT編程中十分重要的一個(gè)機(jī)制。
是指代碼對(duì)對(duì)象的訪問(wèn)這一抽象關(guān)系,它與C/C++的指針有點(diǎn)相似,但并非同物。引用同時(shí)也是JAVASCRIPT引擎在進(jìn)行垃圾回收中最關(guān)鍵的一個(gè)機(jī)制。
var val = "hello world";
function foo() {
return function() {return val;};
}
global.bar = foo();
當(dāng)代碼執(zhí)行完畢時(shí),對(duì)象val和bar()并沒(méi)有被回收釋放,JAVASCRIPT代碼中,每個(gè)變量作為多帶帶一行而不做任何操作,JAVASCRIPT引擎都會(huì)認(rèn)為這是對(duì)對(duì)象的訪問(wèn)行為,存在了對(duì)對(duì)象的引用。為了保證垃圾回收的行為不影響程序邏輯的運(yùn)行,JAVASCRIPT引擎不會(huì)把正在使用的對(duì)象進(jìn)行回收。所以判斷對(duì)象是否正在使用中的標(biāo)準(zhǔn),就是是否仍然存在對(duì)該對(duì)象的引用。
JAVASCRIPT的引用是可以進(jìn)行轉(zhuǎn)移的,那么就有可能出現(xiàn)某些引用被帶到了全局作用域,但事實(shí)上在業(yè)務(wù)邏輯里已經(jīng)不需要對(duì)其進(jìn)行訪問(wèn)了,這個(gè)時(shí)候就應(yīng)該被回收,但是JAVASCRIPT引擎仍會(huì)認(rèn)為程序仍然需要它。
IE下閉包引起跨頁(yè)面內(nèi)存泄露。
JAVASCRIPT的內(nèi)存泄露處理
給DOM對(duì)象添加的屬性是一個(gè)對(duì)象的引用。
var MyObject = {};
document.getElementByIdx_x("myDiv").myProp = MyObject;解決方法:在window.onunload事件中寫(xiě)上:document.getElementByIdx_x("myDiv").myProp = null;
DOM對(duì)象與JS對(duì)象相互引用。
function Encapsulator(element) {
this.elementReference = element; element.myProp = this;}
new Encapsulator(document.getElementByIdx_x("myDiv"));解決方法:在onunload事件中寫(xiě)上:document.getElementByIdx_x("myDiv").myProp = null;
給DOM對(duì)象用attachEvent綁定事件。
function doClick() {}
element.attachEvent("onclick", doClick);解決方法:在onunload事件中寫(xiě)上:element.detachEvent("onclick", doClick);
從外到內(nèi)執(zhí)行appendChild。這時(shí)即使調(diào)用removeChild也無(wú)法釋放。
var parentDiv = document.createElement_x("div");
var childDiv = document.createElement_x("div");
document.body.appendChild(parentDiv);
parentDiv.appendChild(childDiv);解決方法:從內(nèi)到外執(zhí)行appendChild:var parentDiv = document.createElement_x("div");
var childDiv = document.createElement_x("div");
parentDiv.appendChild(childDiv);
document.body.appendChild(parentDiv);
反復(fù)重寫(xiě)同一個(gè)屬性會(huì)造成內(nèi)存大量占用(但關(guān)閉IE后內(nèi)存會(huì)被釋放)。
for(i = 0; i < 5000; i++) {
hostElement.text = "asdfasdfasdf";}
這種方式相當(dāng)于定義了5000個(gè)屬性,解決方法:無(wú)。
內(nèi)存不是緩存。
不要輕易將內(nèi)存當(dāng)作緩存使用。
如果是很重要的資源,請(qǐng)不要直接放在內(nèi)存中,或者制定過(guò)期機(jī)制,自動(dòng)銷毀過(guò)期緩存。
CollectGarbage。
CollectGarbage是IE的一個(gè)特有屬性,用于釋放內(nèi)存的使用方法,將該變量或引用對(duì)象設(shè)置為null或delete然后在進(jìn)行釋放動(dòng)作,在做CollectGarbage前,要必需清楚的兩個(gè)必備條件:(引用)。
一個(gè)對(duì)象在其生存的上下文環(huán)境之外,即會(huì)失效。
一個(gè)全局的對(duì)象在沒(méi)有被執(zhí)用(引用)的情況下,即會(huì)失效
使用事件代理
當(dāng)存在多個(gè)元素需要注冊(cè)事件時(shí),在每個(gè)元素上綁定事件本身就會(huì)對(duì)性能有一定損耗。
由于DOM Level2事件模 型中所有事件默認(rèn)會(huì)傳播到上層文檔對(duì)象,可以借助這個(gè)機(jī)制在上層元素注冊(cè)一個(gè)統(tǒng)一事件對(duì)不同子元素進(jìn)行相應(yīng)處理。
捕獲型事件先發(fā)生。兩種事件流會(huì)觸發(fā)DOM中的所有對(duì)象,從document對(duì)象開(kāi)始,也在document對(duì)象結(jié)束。
同域跨域
當(dāng)需要使用數(shù)組時(shí),可使用JSON格式的語(yǔ)法
即直接使用如下語(yǔ)法定義數(shù)組:[parrm,param,param...],而不是采用new Array(parrm,param,param...)這種語(yǔ)法。使用JSON格式的語(yǔ)法是引擎直接解釋。而后者則需要調(diào)用Array的構(gòu)造器。
如果需要遍歷數(shù)組,應(yīng)該先緩存數(shù)組長(zhǎng)度,將數(shù)組長(zhǎng)度放入局部變量中,避免多次查詢數(shù)組長(zhǎng)度。
根據(jù)字符串、數(shù)組的長(zhǎng)度進(jìn)行循環(huán),而通常這個(gè)長(zhǎng)度是不變的,比如每次查詢a.length,就要額外進(jìn)行一個(gè)操作,而預(yù)先把var len=a.length,則每次循環(huán)就少了一次查詢。
性能測(cè)試工具
避免跳轉(zhuǎn)
同域:注意避免反斜杠 “/” 的跳轉(zhuǎn);
跨域:使用Alias或者mod_rewirte建立CNAME(保存域名與域名之間關(guān)系的DNS記錄)
循環(huán)專題js性能優(yōu)化和內(nèi)存泄露問(wèn)題及檢測(cè)分析工具
性能優(yōu)化ajax工具diviefirebug
[web性能分析工具YSlow]
performance性能評(píng)估打分,右擊箭頭可看到改進(jìn)建議。
stats緩存狀態(tài)分析,傳輸內(nèi)容分析。
components所有加載內(nèi)容分析,可以查看傳輸速度,找出頁(yè)面訪問(wèn)慢的瓶頸。
tools可以查看js和css,并打印頁(yè)面評(píng)估報(bào)告。
內(nèi)存泄露檢測(cè)工具sIEve
sIEve是基于IE的內(nèi)存泄露檢測(cè)工具,需要下載運(yùn)行,可以查看dom孤立節(jié)點(diǎn)和內(nèi)存泄露及內(nèi)存使用情況。
列出當(dāng)前頁(yè)面內(nèi)所有dom節(jié)點(diǎn)的基本信息(html id style 等)
頁(yè)面內(nèi)所有dom節(jié)點(diǎn)的高級(jí)信息 (內(nèi)存占用,數(shù)量,節(jié)點(diǎn)的引用)
可以查找出頁(yè)面中的孤立節(jié)點(diǎn)
可以查找出頁(yè)面中的循環(huán)引用
可以查找出頁(yè)面中產(chǎn)生內(nèi)存泄露的節(jié)點(diǎn)
內(nèi)存泄露提示工具leak monitor
leak monitor在安裝后,當(dāng)離開(kāi)一個(gè)頁(yè)面時(shí),比如關(guān)閉窗口,如果頁(yè)面有內(nèi)存泄露,會(huì)彈出一個(gè)文本框進(jìn)行即時(shí)提示。
代碼壓縮工具
YUI壓縮工具
Dean Edwards Packer
JSMin
Blink/Webkit瀏覽器
在Blink/Webkit瀏覽器中(Chrome, Safari, Opera),我們可以借助其中的Developer Tools的Profiles工具來(lái)對(duì)我們的程序進(jìn)行內(nèi)存檢查。
Developer Tools - Profiles
Node.js中的內(nèi)存檢查
在Node.js中,我們可以使用node-heapdump和node-memwatch模塊進(jìn)??行內(nèi)存檢查。
var heapdump = require("heapdump");
var fs = require("fs");
var path = require("path");
fs.writeFileSync(path.join(__dirname, "app.pid"), process.pid);在業(yè)務(wù)代碼中引入node-heapdump之后,我們需要在某個(gè)運(yùn)行時(shí)期,向Node.js進(jìn)程發(fā)送SIGUSR2信號(hào),讓node-heapdump抓拍一份堆內(nèi)存的快照。
$ kill -USR2 (cat app.pid) 這樣在文件目錄下會(huì)有一個(gè)以`heapdump-. .heapsnapshot`格式命名的快照文件,我們可以使用瀏覽器的`Developer Tools`中的`Profiles`工具將其打開(kāi),并進(jìn)行檢查。 分析瀏覽器提供的Waterfall圖片來(lái)思考優(yōu)化入口。
新的測(cè)試手段(Navigation, Resource, 和User timing。
原型優(yōu)化
循環(huán)是一種常用的流程控制。
JAVASCRIPT提供了三種循環(huán)。
for(;;)。
推薦使用for循環(huán),如果循環(huán)變量遞增或遞減,不要多帶帶對(duì)循環(huán)變量賦值,而應(yīng)該使用嵌套的++或–-運(yùn)算符。
代碼的可讀性對(duì)于for循環(huán)的優(yōu)化。
用-=1。
從大到小的方式循環(huán)(這樣缺點(diǎn)是降低代碼的可讀性)。
/效率低/
var divs = document.getElementsByTagName("div");
for(var i = 0; i < divs.length; i++){...}
/效率高,適用于獲取DOM集合,如果純數(shù)組則兩種情況區(qū)別不到/
var divs = document.getElementsByTagName("div");
for(var i = 0, len = divs.length; i < len; i++){...}
/在IE6.0下,for(;;)循環(huán)在執(zhí)行中,第一種情況會(huì)每次都計(jì)算一下長(zhǎng)度,而第二種情況卻是在開(kāi)始的時(shí)候計(jì)算長(zhǎng)度,并把其保存到一個(gè)變量中,所以其執(zhí)行效率要高點(diǎn),所以在我們使用for(;;)循環(huán)的時(shí)候,特別是需要計(jì)算長(zhǎng)度的情況,我們應(yīng)該開(kāi)始將其保存到一個(gè)變量中。/
while()。
for(;;)、while()循環(huán)的性能基本持平。
for(in)。
在這三種循環(huán)中for(in)內(nèi)部實(shí)現(xiàn)是構(gòu)造一個(gè)所有元素的列表,包括array繼承的屬性,然后再開(kāi)始循環(huán),并且需要查詢hasOwnProperty。所以for(in)相對(duì)for(;;)循環(huán)性能要慢。
選擇正確的方法
避免不必要的屬性查找。
訪問(wèn)變量或數(shù)組是O(1)操作。
訪問(wèn)對(duì)象上的屬性是一個(gè)O(n)操作。
對(duì)象上的任何屬性查找都要比訪問(wèn)變量或數(shù)組花費(fèi)更長(zhǎng)時(shí)間,因?yàn)楸仨氃谠玩溨袑?duì)擁有該名稱的屬性進(jìn)行一次搜索,即屬性查找越多,執(zhí)行時(shí)間越長(zhǎng)。所以針對(duì)需要多次用到對(duì)象屬性,應(yīng)將其存儲(chǔ)在局部變量。
優(yōu)化循環(huán)。
減值迭代。
大多數(shù)循環(huán)使用一個(gè)從0開(kāi)始,增加到某個(gè)特定值的迭代器。在很多情況下,從最大值開(kāi)始,在循環(huán)中不斷減值的迭代器更加有效。
簡(jiǎn)化終止條件。
由于每次循環(huán)過(guò)程都會(huì)計(jì)算終止條件,故必須保證它盡可能快,即避免屬性查找或其它O(n)的操作。
簡(jiǎn)化循環(huán)體。
循環(huán)體是執(zhí)行最多的,故要確保其被最大限度地優(yōu)化。確保沒(méi)有某些可以被很容易移出循環(huán)的密集計(jì)算。
使用后測(cè)試循環(huán)。
最常用的for和while循環(huán)都是前測(cè)試循環(huán),而如do-while循環(huán)可以避免最初終止條件的計(jì)算,因些計(jì)算更快。
for(var i = 0; i < values.length; i++) { process(values[i]); }優(yōu)化1:簡(jiǎn)化終止條件
for(var i = 0, len = values.length; i < len; i++) { process(values[i]); }優(yōu)化2:使用后測(cè)試循環(huán)(注意:使用后測(cè)試循環(huán)需要確保要處理的值至少有一個(gè))
展開(kāi)循環(huán)。
當(dāng)循環(huán)的次數(shù)確定時(shí),消除循環(huán)并使用多次函數(shù)調(diào)用往往更快。
當(dāng)循環(huán)的次數(shù)不確定時(shí),可以使用Duff裝置來(lái)優(yōu)化。
Duff裝置的基本概念是通過(guò)計(jì)算迭代的次數(shù)是否為8的倍數(shù)將一個(gè)循環(huán)展開(kāi)為一系列語(yǔ)句。
// Jeff Greenberg for JS implementation of Duff"s Device // 假設(shè):values.length 0 function process(v) { alert(v); } var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.ceil(values.length / 8); var startAt = values.length % 8; var i = 0; do { switch(startAt) { case 0 : process(values[i++]); case 7 : process(values[i++]); case 6 : process(values[i++]); case 5 : process(values[i++]); case 4 : process(values[i++]); case 3 : process(values[i++]); case 2 : process(values[i++]); case 1 : process(values[i++]); } startAt = 0; }while(--iterations 0);如上展開(kāi)循環(huán)可以提升大數(shù)據(jù)集的處理速度。接下來(lái)給出更快的Duff裝置技術(shù),將do-while循環(huán)分成2個(gè)多帶帶的循環(huán)。(注:這種方法幾乎比原始的Duff裝置實(shí)現(xiàn)快上40%。)
// Speed Up Your Site(New Riders, 2003) function process(v) { alert(v); } var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.floor(values.length / 8); var leftover = values.length % 8; var i = 0; if(leftover 0) { do { process(values[i++]); }while(--leftover 0); } do { process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); }while(--iterations 0);針對(duì)大數(shù)據(jù)集使用展開(kāi)循環(huán)可以節(jié)省很多時(shí)間,但對(duì)于小數(shù)據(jù)集,額外的開(kāi)銷則可能得不償失。
避免在循環(huán)中使用try-catch。
try-catch-finally語(yǔ)句在catch語(yǔ)句被執(zhí)行的過(guò)程中會(huì)動(dòng)態(tài)構(gòu)造變量插入到當(dāng)前域中,對(duì)性能有一定影響。
如果需要異常處理機(jī)制,可以將其放在循環(huán)外層使用。
循環(huán)中使用try-catch
for ( var i = 0; i < 200; i++) {
try {} catch (e) {}
}
循環(huán)外使用try-catch
try { for ( var i = 0; i < 200; i++) {} } catch (e) {}
避免遍歷大量元素:
避免對(duì)全局DOM元素進(jìn)行遍歷,如果parent已知可以指定parent在特定范圍查詢。
var elements = document.getElementsByTagName( "*" );
for (i = 0; i < elements.length; i++) {
if (elements[i].hasAttribute( "selected" )) {}
}如果已知元素存在于一個(gè)較小的范圍內(nèi),var elements = document.getElementById( "canvas" ).getElementsByTagName ( "*" );
for (i = 0; i < elements.length; i++) {
if (elements[i].hasAttribute( "selected" )) {}
}
運(yùn)算符專題
通過(guò)原型優(yōu)化方法定義。
如果一個(gè)方法類型將被頻繁構(gòu)造,通過(guò)方法原型從外面定義附加方法,從而避免方法的重復(fù)定義。
可以通過(guò)外部原型的構(gòu)造方式初始化值類型的變量定義。(這里強(qiáng)調(diào)值類型的原因是,引用類型如果在原型中定義,一個(gè)實(shí)例對(duì)引用類型的更改會(huì)影響到其他實(shí)例。)
這條規(guī)則中涉及到JAVASCRIPT中原型的概念,構(gòu)造函數(shù)都有一個(gè)prototype屬性,指向另一個(gè)對(duì)象。這個(gè)對(duì)象的所有屬性和方法,都會(huì)被構(gòu)造函數(shù)的實(shí)例繼承??梢园涯切┎蛔兊膶傩院头椒ǎ苯佣x在prototype對(duì)象上。
可以通過(guò)對(duì)象實(shí)例訪問(wèn)保存在原型中的值。
不能通過(guò)對(duì)象實(shí)例重寫(xiě)原型中的值。
在實(shí)例中添加一個(gè)與實(shí)例原型同名屬性,那該屬性就會(huì)屏蔽原型中的屬性。
通過(guò)delete操作符可以刪除實(shí)例中的屬性。
重繪專題使用運(yùn)算符時(shí),盡量使用+=,-=、*=、=等運(yùn)算符號(hào),而不是直接進(jìn)行賦值運(yùn)算。
位運(yùn)算。
當(dāng)進(jìn)行數(shù)學(xué)運(yùn)算時(shí)位運(yùn)算較快,位運(yùn)算操作要比任何布爾運(yùn)算或算數(shù)運(yùn)算快,如取模,邏輯與和邏輯或也可以考慮用位運(yùn)算來(lái)替換。
字符串專題
減少頁(yè)面的重繪。
減少頁(yè)面重繪雖然本質(zhì)不是JAVASCRIPT優(yōu)化,但重繪往往是由JAVASCRIPT引起的,而重繪的情況直接影響頁(yè)面性能。
var str = "
這是一個(gè)測(cè)試字符串";
/效率低/
var obj = document.getElementsByTagName("body");
for(var i = 0; i < 100; i++){obj.innerHTML += str + i;}
/效率高/
var obj = document.getElementsByTagName("body");
var arr = [];
for(var i = 0; i < 100; i++){arr[i] = str + i;}
obj.innerHTML = arr.join("");一般影響頁(yè)面重繪的不僅僅是innerHTML,如果改變?cè)氐臉邮?,位置等情況都會(huì)觸發(fā)頁(yè)面重繪,所以在平時(shí)一定要注意這點(diǎn)。
使用HTML5和CSS3的一些新特性。
避免在HTML里面縮放圖片。
避免使用插件。
確保使用正確的字體大小。
決定當(dāng)前頁(yè)面是不是能被訪問(wèn)。
作用域鏈和閉包優(yōu)化
對(duì)字符串進(jìn)行循環(huán)操作。
替換、查找等操作,使用正則表達(dá)式。
因?yàn)?b>JAVASCRIPT的循環(huán)速度較慢,而正則表達(dá)式的操作是用C寫(xiě)成的API,性能比較好。
字符串的拼接。
字符串的拼接在我們開(kāi)發(fā)中會(huì)經(jīng)常遇到,所以我把其放在首位,我們往往習(xí)慣的直接用+=的方式來(lái)拼接字符串,其實(shí)這種拼接的方式效率非常的低,我們可以用一種巧妙的方法來(lái)實(shí)現(xiàn)字符串的拼接,那就是利用數(shù)組的join方法,具體請(qǐng)看我整理的:Web前端開(kāi)發(fā)規(guī)范文檔中的javaScript書(shū)寫(xiě)規(guī)范倒數(shù)第三條目。
不過(guò)也有另一種說(shuō)法,通常認(rèn)為需要用Array.join的方式,但是由于SpiderMonkey等引擎對(duì)字符串的“+”運(yùn)算做了優(yōu)化,結(jié)果使用Array.join的效率反而不如直接用“+”,但是如果考慮IE6,則其他瀏覽器上的這種效率的差別根本不值一提。具體怎么取舍,諸君自定。
作用域。
作用域(scope)是JAVASCRIPT編程中一個(gè)重要的運(yùn)行機(jī)制,在JAVASCRIPT同步和異步編程以及JAVASCRIPT內(nèi)存管理中起著至關(guān)重要的作用。
在JAVASCRIPT中,能形成作用域的有如下幾點(diǎn)。
函數(shù)的調(diào)用
with語(yǔ)句
with會(huì)創(chuàng)建自已的作用域,因此會(huì)增加其中執(zhí)行代碼的作用域的長(zhǎng)度。
全局作用域。
以下代碼為例:
var foo = function() {
var local = {};
};
foo();
console.log(local); //=undefinedvar bar = function() {
local = {};
};
bar();
console.log(local); //={}/這里我們定義了foo()函數(shù)和bar()函數(shù),他們的意圖都是為了定義一個(gè)名為local的變量。在foo()函數(shù)中,我們使用var語(yǔ)句來(lái)聲明定義了一個(gè)local變量,而因?yàn)楹瘮?shù)體內(nèi)部會(huì)形成一個(gè)作用域,所以這個(gè)變量便被定義到該作用域中。而且foo()函數(shù)體內(nèi)并沒(méi)有做任何作用域延伸的處理,所以在該函數(shù)執(zhí)行完畢后,這個(gè)local變量也隨之被銷毀。而在外層作用域中則無(wú)法訪問(wèn)到該變量。而在bar()函數(shù)內(nèi),local變量并沒(méi)有使用var語(yǔ)句進(jìn)行聲明,取而代之的是直接把local作為全局變量來(lái)定義。故外層作用域可以訪問(wèn)到這個(gè)變量。/
local = {}; // 這里的定義等效于 global.local = {};
作用域鏈
在JAVASCRIPT編程中,會(huì)遇到多層函數(shù)嵌套的場(chǎng)景,這就是典型的作用域鏈的表示。
function foo() {
var val = "hello";
function bar() {function baz() { global.val = "world;" }; baz(); console.log(val); //=hello};
bar();
};
foo();/**在`JAVASCRIPT`中,變量標(biāo)識(shí)符的查找是從當(dāng)前作用域開(kāi)始向外查找,直到全局作用域?yàn)橹埂K訿JAVASCRIPT`代碼中對(duì)變量的訪問(wèn)只能向外進(jìn)行,而不能逆而行之。baz()函數(shù)的執(zhí)行在全局作用域中定義了一個(gè)全局變量val。而在bar()函數(shù)中,對(duì)val這一標(biāo)識(shí)符進(jìn)行訪問(wèn)時(shí),按照從內(nèi)到外的查找原則:在bar函數(shù)的作用域中沒(méi)有找到,便到上一層,即foo()函數(shù)的作用域中查找。然而,使大家產(chǎn)生疑惑的關(guān)鍵就在這里:本次標(biāo)識(shí)符訪問(wèn)在foo()函數(shù)的作用域中找到了符合的變量,便不會(huì)繼續(xù)向外查找,故在baz()函數(shù)中定義的全局變量val并沒(méi)有在本次變量訪問(wèn)中產(chǎn)生影響。**/
減少作用域鏈上的查找次數(shù)
JAVASCRIPT代碼在執(zhí)行的時(shí)候,如果需要訪問(wèn)一個(gè)變量或者一個(gè)函數(shù)的時(shí)候,它需要遍歷當(dāng)前執(zhí)行環(huán)境的作用域鏈,而遍歷是從這個(gè)作用域鏈的前端一級(jí)一級(jí)的向后遍歷,直到全局執(zhí)行環(huán)境。
/效率低/
for(var i = 0; i < 10000; i++){var but1 = document.getElementById("but1");}
/效率高/
/避免全局查找/
var doc = document;
for(var i = 0; i < 10000; i++){var but1 = doc.getElementById("but1");}
/上面代碼中,第二種情況是先把全局對(duì)象的變量放到函數(shù)里面先保存下來(lái),然后直接訪問(wèn)這個(gè)變量,而第一種情況是每次都遍歷作用域鏈,直到全局環(huán)境,我們看到第二種情況實(shí)際上只遍歷了一次,而第一種情況卻是每次都遍歷了,而且這種差別在多級(jí)作用域鏈和多個(gè)全局變量的情況下還會(huì)表現(xiàn)的非常明顯。在作用域鏈查找的次數(shù)是O(n)。通過(guò)創(chuàng)建一個(gè)指向document的局部變量,就可以通過(guò)限制一次全局查找來(lái)改進(jìn)這個(gè)函數(shù)的性能。/
閉包
JAVASCRIPT中的標(biāo)識(shí)符查找遵循從內(nèi)到外的原則。
function foo() { var local = "Hello"; return function() { return local; }; } var bar = foo(); console.log(bar()); //=Hello /**這里所展示的讓外層作用域訪問(wèn)內(nèi)層作用域的技術(shù)便是閉包(Closure)。得益于高階函數(shù)的應(yīng)用,使foo()函數(shù)的作用域得到`延伸`。foo()函數(shù)返回了一個(gè)匿名函數(shù),該函數(shù)存在于foo()函數(shù)的作用域內(nèi),所以可以訪問(wèn)到foo()函數(shù)作用域內(nèi)的local變量,并保存其引用。而因這個(gè)函數(shù)直接
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/85929.html
摘要:特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 本以為自己收藏的站點(diǎn)多,可以很快搞定,沒(méi)想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補(bǔ)充。有錯(cuò)誤的地方,還請(qǐng)斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會(huì)及時(shí)更新,平時(shí)業(yè)務(wù)工作時(shí)也會(huì)不定期更...
摘要:為了可以頂下這個(gè)雷,特意買了高性能網(wǎng)站建設(shè)指南。規(guī)則七避免使用表達(dá)式原因表達(dá)式在你不知道得情況下執(zhí)行多次,嚴(yán)重影響前端性能。這也是會(huì)降低前端性能的。 最近要實(shí)現(xiàn)前端性能探測(cè),可是對(duì)于一個(gè)剛?cè)肼殘?chǎng)的我來(lái)說(shuō)前端性能是個(gè)啥,我還是個(gè)只追求頁(yè)面展示效果的娃兒~。為了可以頂下這個(gè)雷,特意買了高性能網(wǎng)站建設(shè)指南。這本書(shū)真的不錯(cuò),強(qiáng)烈推薦看到本文的朋友看一下。 規(guī)則一 減少http請(qǐng)求數(shù) 原因:為什...
摘要:先來(lái)看一張系統(tǒng)前后端架構(gòu)模型圖。一種接口的約定本文用于定義一種統(tǒng)一的接口設(shè)計(jì)方案,希望具有參考價(jià)值。,和都是常見(jiàn)的軟件架構(gòu)設(shè)計(jì)模式,它通過(guò)分離關(guān)注點(diǎn)來(lái)改進(jìn)代碼的組織方式。 如何無(wú)痛降低 if else 面條代碼復(fù)雜度 相信不少同學(xué)在維護(hù)老項(xiàng)目時(shí),都遇到過(guò)在深深的 if else 之間糾纏的業(yè)務(wù)邏輯。面對(duì)這樣的一團(tuán)亂麻,簡(jiǎn)單粗暴地繼續(xù)增量修改常常只會(huì)讓復(fù)雜度越來(lái)越高,可讀性越來(lái)越差,有沒(méi)...
摘要:前端每周清單年度總結(jié)與盤點(diǎn)在過(guò)去的八個(gè)月中,我?guī)缀踔蛔隽藘杉?,工作與整理前端每周清單。本文末尾我會(huì)附上清單線索來(lái)源與目前共期清單的地址,感謝每一位閱讀鼓勵(lì)過(guò)的朋友,希望你們能夠繼續(xù)支持未來(lái)的每周清單。 showImg(https://segmentfault.com/img/remote/1460000010890043); 前端每周清單年度總結(jié)與盤點(diǎn) 在過(guò)去的八個(gè)月中,我?guī)缀踔蛔隽?..
摘要:端優(yōu)談?wù)勱P(guān)于前端的緩存的問(wèn)題我們都知道對(duì)頁(yè)面進(jìn)行緩存能夠有利于減少請(qǐng)求發(fā)送,從而達(dá)到對(duì)頁(yè)面的優(yōu)化。而作為一名有追求的前端,勢(shì)必要力所能及地優(yōu)化我們前端頁(yè)面的性能。這種方式主要解決了淺談前端中的過(guò)早優(yōu)化問(wèn)題過(guò)早優(yōu)化是萬(wàn)惡之源。 優(yōu)化向:?jiǎn)雾?yè)應(yīng)用多路由預(yù)渲染指南 Ajax 技術(shù)的出現(xiàn),讓我們的 Web 應(yīng)用能夠在不刷新的狀態(tài)下顯示不同頁(yè)面的內(nèi)容,這就是單頁(yè)應(yīng)用。在一個(gè)單頁(yè)應(yīng)用中,往往只有一...
閱讀 1527·2021-11-24 09:38
閱讀 3376·2021-11-18 10:02
閱讀 3266·2021-09-22 15:29
閱讀 2950·2021-09-22 15:15
閱讀 1054·2021-09-13 10:25
閱讀 1869·2021-08-17 10:13
閱讀 2003·2021-08-04 11:13
閱讀 1985·2019-08-30 15:54