<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    瘋狂

    STANDING ON THE SHOULDERS OF GIANTS
    posts - 481, comments - 486, trackbacks - 0, articles - 1
      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    高性能JavaScript(來(lái)自高性能JavaScript一書(shū))

    Posted on 2011-10-09 11:56 瘋狂 閱讀(3033) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): java性能 、web

    高性能JavaScript

    一下內(nèi)容是轉(zhuǎn)載的,內(nèi)容應(yīng)該出自高性能JavaScript一書(shū)中,此書(shū)值得一讀。

    學(xué)習(xí)過(guò)程中寫(xiě)的筆記,有誤請(qǐng)指正。

    性能并不是唯一的考慮因素,在對(duì)性能要求并非苛刻的環(huán)境中,性能也可讓位于:團(tuán)隊(duì)編碼規(guī)范,個(gè)人編碼習(xí)慣,代碼可讀性,模塊可擴(kuò)展性等因素。

    以下提到的對(duì)性能的優(yōu)化,僅僅提供了從性能的角度去闡釋一些設(shè)計(jì)思路,但實(shí)際上,瀏覽器本身會(huì)逐步優(yōu)化自身的性能問(wèn)題,而我們那些提高性能的hack,可能會(huì)因?yàn)闉g覽器的版本更新,導(dǎo)致成為一種無(wú)用的hack,甚至讓性能更慢,所以不要無(wú)謂的使用一些hack,去優(yōu)化一些執(zhí)行次數(shù)很少的代碼,而降低代碼的可讀性,或增加代碼量,,一句話(huà):如非必要,請(qǐng)勿hack。

    一 javascript加載和執(zhí)行

    1 無(wú)論是外鏈還是內(nèi)聯(lián),script標(biāo)簽都會(huì)阻塞頁(yè)面的渲染,所以script標(biāo)簽的位置最好是</body>前
    2 減少http請(qǐng)求,合并多個(gè)script文件為一個(gè),1個(gè)90k的文件比3個(gè)30k的文件載入速度要快
    3 如何不阻塞頁(yè)面,而載入一個(gè)腳本呢:
    1)給script標(biāo)簽加defer屬性:<script src=”xxx” defer></script>,這樣不會(huì)使頁(yè)面執(zhí)行過(guò)程阻塞,但并不是所有瀏覽器都支持這個(gè)defer屬性
    2)xmlHttpRequest來(lái)請(qǐng)求腳本內(nèi)容并eval,雖然這樣可以靈活的控制腳本下載過(guò)程 及 何時(shí)執(zhí)行,但不能跨域是個(gè)硬傷
    3)createElement(‘script’)然后append是個(gè)不錯(cuò)的方案,無(wú)阻塞,有onload用onload,IE沒(méi)有就用onreadystatechange實(shí)現(xiàn)加載完成的事件,注意readyState屬性的值是complete或者loaded都要執(zhí)行回調(diào)函數(shù)并清除onreadystatechange,因?yàn)檫@兩個(gè)狀態(tài)值不穩(wěn)定。

    4 所以推薦的方式是第三種:在載入js-loader部分的少量代碼后,就用loader去加載其他js吧,注意一些市面上流行的庫(kù)如labjs實(shí)現(xiàn)了這些功能(廣告下:qwrap實(shí)現(xiàn)了依賴(lài)管理及動(dòng)態(tài)加載)

    二 數(shù)據(jù)訪(fǎng)問(wèn)
    數(shù)據(jù)存儲(chǔ)在:直接量,變量,數(shù)組元素,對(duì)象成員中。
    這又讓我想起了編譯型的語(yǔ)言,直接量存在于pe文件的.rdata段中,變量在調(diào)用棧上,數(shù)組元素和對(duì)象成員的訪(fǎng)問(wèn)需要基址+偏移量來(lái)定位,多層對(duì)象嵌套需要多次計(jì)算基址+偏移量來(lái)定位。

    這些在javascript中依然沒(méi)有太大變化:

    1 直接量的訪(fǎng)問(wèn)無(wú)疑是迅速的

    2 變量的訪(fǎng)問(wèn)需要考慮javascript允許函數(shù)進(jìn)行嵌套定義,也就形成了基于函數(shù)定義的作用域鏈,而變量的訪(fǎng)問(wèn),可能需要跨作用域來(lái)訪(fǎng)問(wèn),所以這里有一點(diǎn)性能損失,但先進(jìn)的js引擎用空間換時(shí)間(猜測(cè)在子作用域中緩存了所有父層作用域鏈上變量的name和地址,所以不會(huì)進(jìn)行上溯作用域鏈,直接執(zhí)行hash定位即可,但一個(gè)函數(shù)中如果包含eval,with,catch塊的話(huà),通過(guò)靜態(tài)代碼分析就沒(méi)辦法知道該函數(shù)中聲明了哪些變量,也就無(wú)法做到這個(gè)優(yōu)化),,不過(guò),從性能上來(lái)看,與我的實(shí)際測(cè)試,大家編碼的時(shí)候不需要注意這種性能考慮,按團(tuán)隊(duì)編碼規(guī)范和個(gè)人編碼習(xí)慣來(lái)吧。

    3 對(duì)象成員的訪(fǎng)問(wèn)要考慮上溯原型鏈,所以理論上來(lái)說(shuō)訪(fǎng)問(wèn)實(shí)例本身上的成員比訪(fǎng)問(wèn)原型的成員速度要快。

    4 多層對(duì)象的嵌套要慢,但是在對(duì)性能要求并非很苛刻的環(huán)境中不用關(guān)心這些。

    三 dom編程

    1 dom的訪(fǎng)問(wèn)和修改
    1) 標(biāo)準(zhǔn)dom方式(createElement,createTextNode,appendChild) 和 字符串拼接后設(shè)置innerHTML 之間的性能相差無(wú)幾,各個(gè)瀏覽器不同
    2) 節(jié)點(diǎn)克隆速度快一些,先用createElement創(chuàng)建好需要用到的元素類(lèi)型后,以后在循環(huán)中調(diào)用元素的cloneNode來(lái)克隆
    3) getElementsByName,getElementsByClassName,getElementsByTagName以及.images,.links,.forms屬性返回的都是html集合,是個(gè)類(lèi)數(shù)組,沒(méi)有數(shù)組的方法,但提供了length屬性和索引器,,HTML集合處于“實(shí)時(shí)狀態(tài)”,底層文檔對(duì)象更新時(shí)后自動(dòng)更新javascript中的集合,訪(fǎng)問(wèn)length時(shí)候也會(huì)去查詢(xún),所以遍歷集合時(shí)候要緩存length來(lái)提高效率
    4) 遍歷集合前記錄length,循環(huán)體中避免對(duì)集合中某相同元素進(jìn)行多次索引,一次索引到局部變量中,因?yàn)閷?duì)html集合的索引性能很差,特別在某些老瀏覽器中
    5) 遍歷dom節(jié)點(diǎn)的話(huà),綜合來(lái)說(shuō)使用nextSibling性能會(huì)比childNodes快一點(diǎn),如果只遍歷element的話(huà),children被所有瀏覽器支持,所以盡量用children而不要用childNodes再自行篩選,其他的如childElementCount,firstElementChild,lastElementChild,nextElementSibling,previousElementSibling不被全面支持,注意做特性檢測(cè)及兼容處理
    6) 注意所使用的類(lèi)庫(kù)是否支持原生的querySelectorAll優(yōu)先規(guī)則,querySelectorAll返回NodeList而不會(huì)返回HTML集合,不存在實(shí)時(shí)文檔結(jié)構(gòu)的性能問(wèn)題

    2 重繪和重排

    改變dom節(jié)點(diǎn)的幾何屬性會(huì)引起重排(reflow),而后發(fā)生重繪(repaint)。

    由于重排需要產(chǎn)生大量的計(jì)算,所以瀏覽器一般會(huì)通過(guò)隊(duì)列化修改并批量執(zhí)行來(lái)優(yōu)化重排,獲取最新布局信息的操作會(huì)導(dǎo)致強(qiáng)制觸發(fā)隊(duì)列的執(zhí)行,如獲取offsetTop,scrollTop,clientTop或調(diào)用getComputedStyle方法(currentStyle in IE)的時(shí)候,要返回諸如此類(lèi)的最新的布局信息的時(shí)候,瀏覽器就會(huì)立即渲染隊(duì)列中的變化,觸發(fā)重排,然后返回正確的值。

    由于動(dòng)作的隊(duì)列是基于頁(yè)面的,所以,即使你獲取的最新布局信息的節(jié)點(diǎn)沒(méi)有待執(zhí)行的動(dòng)作,也會(huì)觸發(fā)重排。

    所以,盡量將修改元素樣式的操作放在一起,然后再執(zhí)行獲取元素最新布局信息的操作,盡量不要交叉進(jìn)行,因?yàn)槊看潍@取元素的最新布局信息,都將觸發(fā)重排和重繪操作。

    雖然,現(xiàn)代瀏覽器進(jìn)行了優(yōu)化,并不會(huì)在每次設(shè)置元素的樣式或改變dom的結(jié)構(gòu)時(shí)都會(huì)重繪和重排,,但舊版瀏覽器仍會(huì)有性能問(wèn)題,所以盡量用以下規(guī)則來(lái)最小化重繪和重排:
    1) 設(shè)置樣式:使用cssText屬性來(lái)合并更新的樣式信息: el.style.cssText=”padding-left:10px;border:1px”
    2) 改變dom結(jié)構(gòu):將元素脫離文檔流,然后進(jìn)行一系列改變,然后再帶回文檔流中,方法如下:
    (1) 隱藏元素,對(duì)元素的dom結(jié)構(gòu)進(jìn)行一系列更改,再顯示元素
    (2) 使用createDocumentFragment創(chuàng)建文檔碎片,針對(duì)文檔碎片進(jìn)行批量操作,然后一次性添加到文檔中
    (3) 將原始元素clone到一個(gè)脫離文檔流的節(jié)點(diǎn)中,修改這個(gè)副本后,替換原始元素
    3) 緩存布局信息
    盡量減少布局信息的獲取次數(shù),如果有針對(duì)布局信息的迭代操作,先將布局信息保存到局部變量中,對(duì)該局部變量進(jìn)行迭代更新,然后將該局部變量更新到dom上
    4) 動(dòng)畫(huà)效果時(shí)脫離文檔流
      一個(gè)元素進(jìn)行動(dòng)畫(huà)效果時(shí):
    (1) 將該元素脫離文檔流,比如絕對(duì)定位該元素
    (2) 對(duì)該元素進(jìn)行動(dòng)畫(huà)操作,這樣不會(huì)觸發(fā)其他區(qū)域的重排和重繪
    (3) 動(dòng)畫(huà)結(jié)束時(shí),將元素恢復(fù)文檔流位置,這樣只會(huì)對(duì)其余區(qū)域進(jìn)行一次重排和重繪

    3 使用事件委托來(lái)減少dom樹(shù)上的事件響應(yīng)函數(shù)
    對(duì)文檔中大量的元素進(jìn)行事件綁定會(huì)導(dǎo)致運(yùn)行時(shí)效率下降,基于事件都會(huì)冒泡到父層,可以在父層上綁定一個(gè)事件,然后識(shí)別target(srcElement)來(lái)自行dispatch事件。

    四 算法和流程控制
    1 循環(huán)
    1) while,for,do-while性能上基本沒(méi)差別,不過(guò)while和do-while一般被用于基于某個(gè)條件的循環(huán),而for用于數(shù)組的迭代或線(xiàn)性的工作,而for-in用于對(duì)象的枚舉
    2)減少循環(huán)次數(shù)或減少循環(huán)中的工作量都可以?xún)?yōu)化性能,如duff循環(huán),但性能提升微乎其微,實(shí)際測(cè)試,在某些瀏覽器下duff循環(huán)不僅不會(huì)提升性能,還會(huì)降低性能,另外倒序循環(huán)可能會(huì)快一些,畢竟正序是與長(zhǎng)度進(jìn)行對(duì)比后的boolean值,而倒序循環(huán)是將表示當(dāng)前循環(huán)進(jìn)度的數(shù)值轉(zhuǎn)換為boolean
    3) 迭代器如js1.6的forEach方法等,性能比用for進(jìn)行循環(huán)要慢一些,但更語(yǔ)義化
    附(我寫(xiě)的一個(gè)duff循環(huán)的js版本)

    1. //注:duff的原理是減少循環(huán)次數(shù),從而減少對(duì)循環(huán)條件的判斷,所以duff的性能優(yōu)化只對(duì)超大數(shù)組(10萬(wàn)次條件判斷會(huì)降低為10萬(wàn)/8次條件判斷)有意義,與循環(huán)體中的語(yǔ)句數(shù)量無(wú)關(guān),所以,duff只適用于循環(huán)體執(zhí)行速度非???,而循環(huán)規(guī)模非常大的狀況,在js中只有略微的性能提升
    2. function duff(list,callback){
    3.     var i = list.length % 8;
    4.     var tails = i;
    5.     while(i){
    6.         callback(list[--i]);
    7.     }
    8.     var greatest_factor = list.length-1;
    9.     do{
    10.         process(list[greatest_factor]);
    11.         process(list[greatest_factor-1]);
    12.         process(list[greatest_factor-2]);
    13.         process(list[greatest_factor-3]);
    14.         process(list[greatest_factor-4]);
    15.         process(list[greatest_factor-5]);
    16.         process(list[greatest_factor-6]);
    17.         process(list[greatest_factor-7]);
    18.         greatest_factor-=8;
    19.     }while(greatest_factor>tails);
    20. }

    2 條件語(yǔ)句
    1)switch比if-else快一些,但性能微乎其微,建議在數(shù)量較多的分支時(shí)使用switch,而進(jìn)行范圍判斷或多重條件的時(shí)候使用if-else
    2)if-else的排列從大概率向小概率
    3)如果條件太多,建議使用查找表,而且查找表具有動(dòng)態(tài)擴(kuò)充的能力
    3 遞歸
    1) 由于調(diào)用棧的限制,遞歸是很危險(xiǎn)的
    2) 非自調(diào)用,而是交叉調(diào)用形成的“隱伏遞歸”是很危險(xiǎn)的,出錯(cuò)之后很難排錯(cuò)
    3) 盡量將遞歸轉(zhuǎn)化為迭代,比如樹(shù)的遍歷,用非遞歸的dfs,bfs來(lái)實(shí)現(xiàn)就好
    4) 很常用的函數(shù),進(jìn)行memoize,用空間換取時(shí)間

    五 字符串和正則表達(dá)式

    1 理解各個(gè)瀏覽器的js引擎字符串合并的內(nèi)部機(jī)制:
    1) 避免產(chǎn)生臨時(shí)字符串,如用str+=’abc’;str+=’def’  而不要用 str+=’abc’+'def’
    2) firefox會(huì)在編譯期合并字符串常量,如str+= ‘abc’+'def’會(huì)被轉(zhuǎn)化為str+=’abcdef’,yur-compressor也有這個(gè)功能
    3) 數(shù)組的join方法,性能不會(huì)比+連接符更快,因?yàn)榇蠖鄶?shù)瀏覽器的+連接符不會(huì)開(kāi)辟新內(nèi)存空間,而ie7,ie6卻會(huì)開(kāi)辟新空間,所以ie6,7中字符串連接應(yīng)該用數(shù)組的join,而其他瀏覽器用+連接符,,concat方法是最慢的方式

    2 正則表達(dá)式優(yōu)化
    基于各js引擎中正則表達(dá)式引擎的不同,以下某些方法會(huì)帶來(lái)某引擎性能的提升但可能同樣導(dǎo)致其他引擎性能的下降,所以原書(shū)原文也只是原理性闡述,實(shí)際開(kāi)發(fā)時(shí)視具體情況而定。
    1) 循環(huán)中使用的正則對(duì)象盡量在循環(huán)前初始化,并賦予一個(gè)變量
    2) 編寫(xiě)正則表達(dá)式時(shí),盡量考慮較少的回溯,比如編寫(xiě)分支時(shí)將大概率分支放在前面,在貪婪模式是從尾部向前回溯,懶惰模式從headPart向尾部逐字符回溯
    3) 關(guān)于回溯失控,起因是太寬泛的匹配模式,如最后的part沒(méi)能成功匹配,則會(huì)記住回溯位置,嘗試修改前面的part的匹配方式,如嘗試讓前面的懶惰模式包含一次endPart,然后從新位置再?lài)L試匹配,正則引擎會(huì)一直嘗試,最終導(dǎo)致回溯失控
    4) 在只是搜索確定的字面量,及字面量位置也確定如行首,行尾等,正則非最佳工具
    (詳細(xì)的正則優(yōu)化先略過(guò),等以后再回來(lái)寫(xiě)吧)

    六 快速響應(yīng)用戶(hù)界面

    1 瀏覽器UI線(xiàn)程和UI隊(duì)列
    UIThread:javascript和ui是共用同一個(gè)線(xiàn)程的,這樣做的好處是無(wú)需考慮運(yùn)行時(shí)的用戶(hù)態(tài)或核心態(tài)的線(xiàn)程同步,也無(wú)需在語(yǔ)言層面實(shí)現(xiàn)臨界區(qū)(critical section),在我最初自己摸索javascript的時(shí)候,認(rèn)為javascript也是多線(xiàn)程的,后來(lái)寫(xiě)代碼測(cè)試后發(fā)現(xiàn),javascript并非多線(xiàn)程,且javascript代碼的執(zhí)行會(huì)阻塞和ui共用的這唯一的線(xiàn)程,使用戶(hù)的操作得不到ui的反饋。

    UIQueue:上面的UIThread負(fù)責(zé)執(zhí)行UIQueue中的task,無(wú)論用戶(hù)對(duì)UI采取的動(dòng)作觸發(fā)的dom事件響應(yīng)函數(shù),還是javascript執(zhí)行過(guò)程中用setTimeout或setInterval創(chuàng)建的定時(shí)器事件響應(yīng)函數(shù),都會(huì)被插入到UIQueue中,當(dāng)UIThread處于busy狀態(tài)時(shí),可能會(huì)忽略掉一些task,不置入U(xiǎn)IQueue,比如用戶(hù)動(dòng)作的產(chǎn)生的ui更新task和觸發(fā)的dom-event兩者,ui更新的task會(huì)被忽略不放入U(xiǎn)IQueue,而dom-event會(huì)放入U(xiǎn)IQueue等待UIThread處于idle狀態(tài)時(shí)執(zhí)行,而setInterval函數(shù)的周期性定時(shí)器事件,會(huì)視UIQueue中是否有相同的事件響應(yīng)函數(shù),如沒(méi)有才會(huì)將該task置入U(xiǎn)IQueue。
    UIQueue的task來(lái)源:
    1) 用戶(hù)操作產(chǎn)生的ui更新重繪
    2) 用戶(hù)操作觸發(fā)的綁定在dom上的javascript事件
    3) dom節(jié)點(diǎn)自身狀態(tài)改變觸發(fā)的綁定在自身上的javascript事件,如的onload
    4) setTimeout與setInterval設(shè)置的定時(shí)器事件
    5) ajax過(guò)程中的onreadystatechange事件,這個(gè)javascript函數(shù)并非綁定在dom上,而是綁定在xmlHttpRequest對(duì)象上

    瀏覽器限制:為了避免某個(gè)javascript事件函數(shù)執(zhí)行時(shí)間過(guò)長(zhǎng),一直占據(jù)UIThread,從而導(dǎo)致用戶(hù)操作觸發(fā)的UIUpdate任務(wù)得不到執(zhí)行,各個(gè)瀏覽器使用不同的方案限制了單個(gè)javascript事件函數(shù)的執(zhí)行時(shí)間
    1) ie:500萬(wàn)條,在注冊(cè)表有設(shè)置
    2) firefox: 10秒,在瀏覽器配置設(shè)置中(about:config->dom.max_script_run_time)
    3) safari: 5秒,無(wú)法修改
    4) chrome: 依賴(lài)通用崩潰檢測(cè)系統(tǒng)
    5) opera: 無(wú)

    界面多久無(wú)反應(yīng)會(huì)讓用戶(hù)無(wú)法忍受: 最長(zhǎng)100毫秒,用戶(hù)的動(dòng)作之后超過(guò)100毫秒界面沒(méi)有做出響應(yīng),用戶(hù)就會(huì)認(rèn)為自己和界面失去了聯(lián)系。

    2 用定時(shí)器讓出時(shí)間片斷(分解任務(wù))
    任務(wù)分解過(guò)程:用戶(hù)無(wú)法忍受一個(gè)10秒的任務(wù)占據(jù)UI線(xiàn)程,而自己的任何操作得不到反饋,于是我們可以將這個(gè)10秒的任務(wù)分為200次50毫秒的任務(wù),每執(zhí)行50毫秒,讓出UI線(xiàn)程去執(zhí)行UI隊(duì)列中的界面更新的任務(wù),讓用戶(hù)及時(shí)得到反饋,然后再執(zhí)行50毫秒,直到執(zhí)行完畢。
    基于大多數(shù)長(zhǎng)時(shí)間UI任務(wù)都是對(duì)數(shù)組的循環(huán)操作,于是我們可以將這個(gè)循環(huán)過(guò)程進(jìn)行拆解,示例代碼:

    1. function timedProcessArray(list, callback, complete, progress){
    2.     var total = list.length;
    3.     var curProgress = 0;
    4.     var preProgress = 0;
    5.    
    6.     (function(list, iteration){
    7.         var fn = arguments.callee;
    8.         var st = +new Date();
    9.         while(list.length && (+new Date() - st < 50) ){
    10.             iteration = callback(list.shift(), iteration);
    11.         }
    12.         if(list.length){
    13.             if(progress){ //如果需要對(duì)進(jìn)度進(jìn)行通知)
    14.                 curProgress = 100 - (100 / total * list.length ^ 0);
    15.                 if(curProgress != preProgress){
    16.                     preProgress = curProgress;
    17.                     progress(curProgress);
    18.                 }
    19.             }
    20.             setTimeout(function(){
    21.                 fn.call(null, list, iteration);
    22.             }, 25);
    23.         }else{
    24.             progress && progress(100);
    25.             complete && complete(iteration);
    26.         }
    27.     })(list.concat(),0);
    28.    
    29. }

    阻塞和非阻塞(任務(wù)分割)方式的示例,:http://lichaosoft.net/case/progress.html

    3 web-workers
    在web-workers之前,javascript是沒(méi)有多線(xiàn)程的,web-workers標(biāo)準(zhǔn)帶來(lái)了真正的多線(xiàn)程,web-workers本來(lái)是html5的一部分,現(xiàn)在已經(jīng)分離出去成為獨(dú)立的規(guī)范:http://www.w3.org/TR/workers/

    和web-workers之間僅能通過(guò)onmessage和postMessage交互。

    使用web-workers以輔助線(xiàn)程進(jìn)行計(jì)算并擁有進(jìn)度通知的示例:
    http://lichaosoft.net/case/worker.html(請(qǐng)使用支持web-workers的chrome或safari瀏覽)

    web-workers適用于那些無(wú)法拆解的任務(wù),對(duì)數(shù)組的遍歷是一個(gè)可以被拆解的任務(wù),對(duì)樹(shù)的遍歷通過(guò)使用dfs或bfs將樹(shù)平坦化為數(shù)組后也可以進(jìn)行拆解,不能拆解的任務(wù):
    1) 編碼/解碼大字符串
    2) 復(fù)雜數(shù)學(xué)運(yùn)算
    3) 大數(shù)組排序

    超過(guò)100毫秒的任務(wù),如瀏覽器支持web-workers,優(yōu)先使用web-workers,如不支持則使用timedProcessArray進(jìn)行分割運(yùn)行。

    沒(méi)有任何javascript代碼的重要度高于用戶(hù)體驗(yàn),用戶(hù)體驗(yàn)是至高重要的,無(wú)論如何不能讓用戶(hù)覺(jué)得界面反應(yīng)速度慢。

    七 AJAX
    從廣義上來(lái)看,AJAX是指不重載整個(gè)頁(yè)面的情況下,與服務(wù)端進(jìn)行數(shù)據(jù)傳輸,解析數(shù)據(jù),并局部刷新頁(yè)面區(qū)域的改善用戶(hù)體驗(yàn)的行為,那么我們下面介紹:數(shù)據(jù)傳輸,數(shù)據(jù)格式。

    1 數(shù)據(jù)傳輸
    1) XHR: 創(chuàng)建一個(gè)XMLHttpRequest對(duì)象與服務(wù)端通信
    2) 動(dòng)態(tài)腳本注入: 這是一個(gè)hack,創(chuàng)建一個(gè)script元素并設(shè)置src為任意uri-A(可跨域),可在頁(yè)面中先定義一個(gè)數(shù)據(jù)處理函數(shù)如function newsListProc(list){},然后在該uri-A指向的script文件中調(diào)用newsListProc并將數(shù)據(jù)傳入,這項(xiàng)技術(shù)也稱(chēng)為:JSON-P。
    3) mXHR: 將多個(gè)資源文件使用XHR傳輸?shù)綖g覽器端,js負(fù)責(zé)對(duì)數(shù)據(jù)流分割,然后dispath給不同類(lèi)型資源文件的處理函數(shù)
    4) 流式XHR: 在支持readyState為3時(shí),可訪(fǎng)問(wèn)已解析好的部分xhr數(shù)據(jù),既是支持流式XHR,可進(jìn)行流式處理來(lái)優(yōu)化執(zhí)行效率,目前實(shí)際測(cè)試ff3.6支持,而ie6,7,8及chrome都不支持流式XHR。
    5) iframes: 待續(xù)
    6) comet: 待續(xù)

    注1:關(guān)于mXHR和流式XHR,我寫(xiě)了一個(gè)demo用來(lái)演示多資源文件合并,由XHR向客戶(hù)端傳輸,并在支持流式XHR的瀏覽器中使用流式XHR,DEMO地址:

    注2:純粹的發(fā)送數(shù)據(jù)而無(wú)需接受數(shù)據(jù),可用beacons方式,類(lèi)似動(dòng)態(tài)腳本注入,不過(guò)創(chuàng)建的不是script元素,而是Image對(duì)象,并設(shè)置src為要請(qǐng)求的URI,所以這種方式只能使用GET方式,代碼示例:

    1. function keepalive(uri, delay){
    2.     var beacon, delay = delay || 1000,
    3.     timer = setTimeout(function(){
    4.         var fn = arguments.callee;
    5.         beacon = new Image();
    6.         beacon.onload = beacon.onerror = function(){
    7.             timer = setTimeout(fn, delay);
    8.         }
    9.         beacon.src = uri;
    10.     }, delay);
    11.     return function(){
    12.         console.log('stop');
    13.         clearTimeout(timer);
    14.     };
    15. }

    2 數(shù)據(jù)格式
    1) XML: 使用responseXML對(duì)象的getElementsByTagName,getElementById,node.getAttribute等api對(duì)xml文檔進(jìn)行解析,也可以用XPath進(jìn)行解析,性能更好些,硬傷是:
    (1) “結(jié)構(gòu)/數(shù)據(jù)”比 太高
    (2) XPath在支持并不廣泛
    (3) 最重要的就是需要先知道內(nèi)容的詳細(xì)結(jié)構(gòu),針對(duì)每個(gè)數(shù)據(jù)結(jié)構(gòu)編寫(xiě)特定的解析方法
    2) JSON: 最廣泛的數(shù)據(jù)格式,解析性能較之xml高,”結(jié)構(gòu)/數(shù)據(jù)”比低,如使用縮略屬性名或完全使用多層數(shù)組格式,結(jié)構(gòu)數(shù)據(jù)比更低,傳輸性能更高
    3) JSON-P: 無(wú)需解析,屬于javascript的正常函數(shù)調(diào)用,性能最高
    4) HTML: 無(wú)需解析,服務(wù)端已構(gòu)造好用于局部更新的html數(shù)據(jù),直接用innerHTML更新,數(shù)據(jù)結(jié)構(gòu)比太高,壓力被集中在服務(wù)端,網(wǎng)絡(luò)傳輸數(shù)據(jù)量高
    5) 自定義分隔符: 性能最高,結(jié)構(gòu)數(shù)據(jù)比最低,對(duì)于性能要求比較苛刻的環(huán)境中使用

    附:創(chuàng)建xhr對(duì)象的代碼:

    1. function createXhrObject(){
    2.     if(window.XMLHttpRequest){
    3.         return new XMLHttpRequest();
    4.     }else{
    5.         var msxml_progid = [
    6.             'MSXML2.XMLHTTP.6.0', //支持readyState的3狀態(tài),但此狀態(tài)時(shí)讀取responseText為空,覺(jué)得似乎沒(méi)意義。。
    7.             'MSXML3.XMLHTTP',
    8.             'Microsoft.XMLHTTP',
    9.             'MSXML2.XMLHTTP.3.0'
    10.         ];
    11.         var req;
    12.         for(var i=0;i<msxml_progid.length;i++){
    13.             try{
    14.                 req = new ActiveXObject(msxml_progid[i]);
    15.                 break;
    16.             }catch(ex){}
    17.         }
    18.         return req;
    19.     }
    20. }

    從字符流中異步解析數(shù)據(jù)的工具類(lèi)

    1. /*
    2. * @method StringStreamParser 流式字符串異步解析類(lèi)
    3. * @param onRow 解析出數(shù)據(jù)行的回調(diào)函數(shù)
    4. * @param RowSeperator 行分隔符,默認(rèn)'u0001'
    5. * @param ColSeperator 列分隔符,默認(rèn)'u0002'
    6. */
    7. function StringStreamParser(onRow, rowSeperator, colSeperator){
    8.     if (!(this instanceof arguments.callee)){
    9.         return new arguments.callee(onRow, rowSeperator, colSeperator);
    10.     }
    11.     var stream = '';                               
    12.     rowSeperator = rowSeperator || 'u0001';
    13.     colSeperator = colSeperator || 'u0002';
    14.    
    15.     /* @method write 向字符流寫(xiě)入包
    16.      * @param packet 寫(xiě)入的包
    17.      * @param lastPacket 是否為最后一個(gè)包,默認(rèn)false
    18.      */
    19.     this.write = function(packet, lastPacket){
    20.         stream += packet;
    21.         var rowIdx = stream.indexOf(rowSeperator);
    22.         var colIdx,strRow, dataRow;
    23.        
    24.         while(rowIdx!==-1 || lastPacket){
    25.        
    26.             if(rowIdx===-1){   
    27.                 strRow = stream.substr(0);
    28.                 lastPacket = false;
    29.             }else{
    30.                 strRow = stream.substr(0,rowIdx);
    31.                 stream = stream.substr(rowIdx+1);
    32.                 rowIdx = stream.indexOf(rowSeperator);                   
    33.             }
    34.            
    35.             dataRow = [];
    36.             while(colIdx = strRow.indexOf(colSeperator)){
    37.                 if(colIdx !== -1){
    38.                     dataRow.push(strRow.substr(0, colIdx));
    39.                     strRow = strRow.substr(colIdx+1);
    40.                 }else{
    41.                     dataRow.push(strRow.substr(0));
    42.                     break;
    43.                 }
    44.             }
    45.             onRow.call(null, dataRow);
    46.            
    47.         }
    48.     }
    49. }

    3 其他
    在確定了合適的數(shù)據(jù)傳輸技術(shù),和數(shù)據(jù)傳輸格式之后,還可以采取以下方式酌情優(yōu)化ajax:
    1) 緩存數(shù)據(jù): 最快的請(qǐng)求,是不請(qǐng)求,有以下兩種方式緩存:
      (1) 對(duì)于GET請(qǐng)求,在response中,設(shè)置expires頭信息,瀏覽器即會(huì)將此請(qǐng)求緩存,這種緩存是跨會(huì)話(huà)也是跨頁(yè)面的
    (2) 在javascript中,以u(píng)rl作為唯一標(biāo)識(shí)符緩存請(qǐng)求到的數(shù)據(jù),無(wú)法跨頁(yè)面也無(wú)法跨會(huì)話(huà),但可編程控制緩存過(guò)程(不能跨會(huì)話(huà)也不能跨頁(yè)面,這種緩存基本上是無(wú)意義的)
    2) 在必要的時(shí)候,直接使用XHR對(duì)象而非ajax庫(kù),如需要流式的數(shù)據(jù)處理

    八 常見(jiàn)編碼中的性能提高點(diǎn)
    1 避免雙重求值: eval,Function,setTimeout和setInterval都允許傳入字符串,而此時(shí)會(huì)創(chuàng)建一個(gè)新的編譯器的實(shí)例,將會(huì)導(dǎo)致很大的性能損失。(另外這些方式執(zhí)行代碼時(shí),代碼的作用域在各個(gè)瀏覽器也不盡相同,容易掉坑)
    2 使用Object/Array的直接量進(jìn)行定義(且直接量比new Object()然后設(shè)置屬性更節(jié)省代碼)
    3 不要讓代碼重復(fù)運(yùn)行
    1) 延遲定義

    2) 條件預(yù)定義
    4 盡量使用原生javascript
    轉(zhuǎn)載自:http://www.cnblogs.com/pansly/archive/2011/06/29/2093769.html

    主站蜘蛛池模板: 亚洲一级毛片中文字幕| 亚洲精品无码成人| 欧美日韩亚洲精品| a级日本高清免费看| 全免费A级毛片免费看网站| 国产亚洲一区二区三区在线不卡| 亚洲熟妇色自偷自拍另类| 色多多A级毛片免费看| 国产成人精品免费视频大全麻豆 | 亚洲精品乱码久久久久久蜜桃不卡 | 亚洲国产成人乱码精品女人久久久不卡 | 亚洲精品第一国产综合精品| 亚洲av日韩综合一区久热| 免费人成在线观看网站| 日本免费一区二区三区最新vr| 亚洲国语精品自产拍在线观看| 美女18一级毛片免费看| 2021在线永久免费视频| 一本色道久久综合亚洲精品高清| 亚洲色欲色欲www| 国产永久免费高清在线| 国产成人免费a在线视频app| 亚洲激情校园春色| 好男人资源在线WWW免费 | 亚洲伊人久久综合影院| 久久亚洲精品国产亚洲老地址| 中国内地毛片免费高清| 成全影视免费观看大全二| 亚洲欧洲无码AV电影在线观看| 亚洲色欲色欱wwW在线| 国产一级淫片a免费播放口| 日本一道本高清免费| 亚洲精品亚洲人成在线麻豆| aaa毛片视频免费观看| 国产婷婷高清在线观看免费 | 亚洲国产最大av| 日本在线看片免费人成视频1000| 亚洲精品线路一在线观看| 亚洲精品天堂成人片AV在线播放| 99在线免费观看视频| 亚洲精品蜜桃久久久久久|