emu in blogjava

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            171 隨筆 :: 103 文章 :: 1052 評(píng)論 :: 2 Trackbacks

          原文:《Speeding up JavaScript: Working with the DOM》

          作者: KeeKim Heng, Google Web Developer

          在我們開(kāi)發(fā)互聯(lián)網(wǎng)富應(yīng)用(RIA)時(shí),我們經(jīng)常寫一些javascript腳本來(lái)修改或者增加頁(yè)面元素,這些工作最終是DOM——或者說(shuō)文檔對(duì)象模型——來(lái)完成的,而我們的實(shí)現(xiàn)方式會(huì)影響到應(yīng)用的響應(yīng)速度。

          DOM操作會(huì)導(dǎo)致瀏覽器重解析(reflow),這是瀏覽器的一個(gè)決定頁(yè)面元素如何展現(xiàn)的計(jì)算過(guò)程。直接修改DOM,修改元素的CSS樣式,修改瀏覽器的窗口大小,都會(huì)觸發(fā)重解析。讀取元素的布局屬性比如offsetHeithe或者offsetWidth也會(huì)觸發(fā)重解析。重解析需要花費(fèi)計(jì)算時(shí)間,因此重解析觸發(fā)的越少,應(yīng)用就會(huì)越快。

          DOM操作通常要不就是修改已經(jīng)存在的頁(yè)面上的元素,要不就是創(chuàng)建新的頁(yè)面元素。下面的4種優(yōu)化方案覆蓋了修改和創(chuàng)建DOM節(jié)點(diǎn)兩種方式,幫助你減少觸發(fā)瀏覽器重解析的次數(shù)。

          方案一:通過(guò)CSS類名切換來(lái)修改DOM 

          這個(gè)方案讓我們可以一次性修改一個(gè)元素和它的子元素的多個(gè)樣式屬性而只觸發(fā)一次重解析。

          需求:

          (emu注:原文作者寫到這里的時(shí)候腦子顯然短路了一下,把后面的Out-of-the-flow DOM Manipulation模式要解決的問(wèn)題給擺到這里來(lái)了,不過(guò)從示范代碼中很容易明白作者真正想描述的問(wèn)題,因此emu就不照翻原文了)

          我們現(xiàn)在需要寫一個(gè)函數(shù)來(lái)修改一個(gè)超鏈接的幾個(gè)樣式規(guī)則。要實(shí)現(xiàn)很簡(jiǎn)單,把這幾個(gè)規(guī)則對(duì)應(yīng)的屬性逐一改了就好了。但是帶來(lái)的問(wèn)題是,每修改一個(gè)樣式屬性,都會(huì)導(dǎo)致一次頁(yè)面的重解析。

          function selectAnchor(element) {
            element.style.fontWeight 
          = 'bold';
            element.style.textDecoration 
          = 'none';
            element.style.color 
          = '#000';
          }

           

          解決方案

          要解決這個(gè)問(wèn)題,我們可以先創(chuàng)建一個(gè)樣式名,并且把要修改的樣式規(guī)則都放到這個(gè)類名上,然后我們給超鏈接添加上這個(gè)新類名,就可以實(shí)現(xiàn)添加幾個(gè)樣式規(guī)則而只觸發(fā)一次重解析了。這個(gè)模式還有個(gè)好處是也實(shí)現(xiàn)了表現(xiàn)和邏輯相分離。


           

          .selectedAnchor {
            font
          -weight: bold;
            text
          -decoration: none;
            color: #
          000;
          }

          function selectAnchor(element) {
            element.className 
          = 'selectedAnchor';
          }

          方案二:在非渲染區(qū)修改DOM

          (emu注:作者在這里再次腦子短路,把DocumentFragment DOM Generation模式的介紹提前到這里來(lái)了,emu只好再次發(fā)揮一下)
          上一個(gè)方案解決的是修改一個(gè)超鏈接的問(wèn)題,當(dāng)一次需要對(duì)很多個(gè)超鏈接進(jìn)行相同修改的時(shí)候,這個(gè)方案就可以大顯身手了。

          需求

          需求是這樣的,我們要寫一個(gè)函數(shù)來(lái)修改一個(gè)指定元素的子元素中所有的超鏈接的樣式名(className)屬性。要實(shí)現(xiàn)很簡(jiǎn)單,我們可以通過(guò)遍歷每個(gè)超鏈接并且修改它們的樣式名來(lái)完成任務(wù)。但是帶來(lái)的問(wèn)題就是,每修改一個(gè)超鏈接都會(huì)導(dǎo)致一次重解析。

          function updateAllAnchors(element, anchorClass) {
            
          var anchors = element.getElementsByTagName('a');
            
          for (var i = 0, length = anchors.length; i < length; i ++) {
              anchors[i].className 
          = anchorClass;
            }
          }

          解決方案

          要解決這個(gè)問(wèn)題,我們可以把被修改的指定元素從DOM里面移除,再修改所有的超鏈接,然后在把這個(gè)元素插入回到它原來(lái)的位置上。為了完成這個(gè)復(fù)雜的操作,我們可以先寫一個(gè)可重用的函數(shù),它不但移除了這個(gè)DOM節(jié)點(diǎn),還返回了一個(gè)把元素插回到原來(lái)的位置的函數(shù)。

          /**
           * Remove an element and provide a function that inserts it into its original position
           * @param element {Element} The element to be temporarily removed
           * @return {Function} A function that inserts the element into its original position
           *
          */
          function removeToInsertLater(element) {
            
          var parentNode = element.parentNode;
            
          var nextSibling = element.nextSibling;
            parentNode.removeChild(element);
            
          return function() {
              
          if (nextSibling) {
                parentNode.insertBefore(element, nextSibling);
              } 
          else {
                parentNode.appendChild(element);
              }
            };
          }

          有了上面這個(gè)函數(shù),現(xiàn)在我們就可以在一個(gè)不需要解析渲染的元素上面修改那些超鏈接了。這樣只在移除和插入元素的時(shí)候各觸發(fā)一次重解析。
          function updateAllAnchors(element, anchorClass) {
            
          var insertFunction = removeToInsertLater(element);
            
          var anchors = element.getElementsByTagName('a');
            
          for (var i = 0, length = anchors.length; i < length; i ++) {
              anchors[i].className 
          = anchorClass;
            }
            insertFunction();
          }

          方案三:一次性的DOM元素生成

          這個(gè)方案讓我們創(chuàng)建一個(gè)元素的過(guò)程只觸發(fā)一次重解析。在創(chuàng)建完元素以后,先進(jìn)行所有需要的修改,最后才把它插入到DOM里面去就可以了

          需求

          需求是這樣的,實(shí)現(xiàn)一個(gè)函數(shù),往一個(gè)指定的父元素上插入一個(gè)超鏈接元素。這個(gè)函數(shù)要同時(shí)可以設(shè)置這個(gè)超鏈接的顯示文字和樣式類。我們可以這樣做:創(chuàng)建元素,插入到DOM里面,然后設(shè)置相應(yīng)的屬性。這就要觸發(fā)3次重解析。

          function addAnchor(parentElement, anchorText, anchorClass) {
            
          var element = document.createElement('a');
            parentElement.appendChild(element);
            element.innerHTML 
          = anchorText;
            element.className 
          = anchorClass;
          }

          解決方案

          很簡(jiǎn)單,我們只要把插入元素這個(gè)操作放到最后做,就可以只進(jìn)行一次重解析了。


          function addAnchor(parentElement, anchorText, anchorClass) {
            
          var element = document.createElement('a');
            element.innerHTML 
          = anchorText;
            element.className 
          = anchorClass;
            parentElement.appendChild(element);
          }

          不過(guò),要是我們想要插入很多個(gè)超鏈接到一個(gè)元素里面的話,那么這個(gè)做法還是有問(wèn)題:每插入一個(gè)超鏈接還是要觸發(fā)一次重解析。下一個(gè)方案可以解決這個(gè)問(wèn)題。

          方案四:通過(guò)文檔片段對(duì)象(DocumentFragment)創(chuàng)建一組元素

          這個(gè)方案允許我們創(chuàng)建并插入很多個(gè)元素而只觸發(fā)一次重解析。要實(shí)現(xiàn)這點(diǎn)需要用到所謂的文檔片段對(duì)象(DocumentFragment)。我們先在DOM之外創(chuàng)建一個(gè)文檔片段對(duì)象(這樣它也就不需要解析和渲染),然后我們?cè)谖臋n片段對(duì)象中創(chuàng)建很多個(gè)元素,最后我們把這個(gè)文檔片段對(duì)象中所有的元素一次性放到DOM里面去,只觸發(fā)一次重解析。

          需求


          我們要寫一個(gè)函數(shù),往一個(gè)指定的元素上面增加10個(gè)超鏈接。如果我們簡(jiǎn)單的直接插入10個(gè)超鏈接到元素上面,就會(huì)觸發(fā)10次重解析。

          function addAnchors(element) {
            
          var anchor;
            
          for (var i = 0; i < 10; i ++) {
              anchor 
          = document.createElement('a');
              anchor.innerHTML 
          = 'test';
              element.appendChild(anchor);
            }
          }

          解決方案

          要解決這個(gè)問(wèn)題,我們要先創(chuàng)建一個(gè)文檔片段對(duì)象,然后把每個(gè)新創(chuàng)建的超鏈接都插入到它里面去。當(dāng)我們把文檔片段對(duì)象用appendChild命令插入到指定的節(jié)點(diǎn)時(shí),這個(gè)文檔片段對(duì)象的所有子節(jié)點(diǎn)就一起被插入到指定的元素里面,而且只需要觸發(fā)一次重解析。

          function addAnchors(element) {
            
          var anchor, fragment = document.createDocumentFragment();
            
          for (var i = 0; i < 10; i ++) {
              anchor 
          = document.createElement('a');
              anchor.innerHTML 
          = 'test';
              fragment.appendChild(anchor);
            }
            element.appendChild(fragment);
          }


          注意:如無(wú)特別聲明,本文中引用的所有程序均不是Google開(kāi)發(fā),也與Google沒(méi)有其他什么關(guān)系。這些程序引發(fā)的責(zé)任均由其開(kāi)發(fā)者或者所有者自己承擔(dān),與google無(wú)關(guān)。
          posted on 2010-03-01 17:20 emu 閱讀(4126) 評(píng)論(4)  編輯  收藏 所屬分類: web優(yōu)化

          評(píng)論

          # re: [翻譯]加速Javascript:DOM操作優(yōu)化 2010-03-02 08:45 Allen.M
          怎么沒(méi)翻譯完?后面的沒(méi)了?  回復(fù)  更多評(píng)論
            

          # re: [翻譯]加速Javascript:DOM操作優(yōu)化 2010-03-02 09:29 emu
          @Allen.M
          抱歉,昨天可能誤操作了,已經(jīng)補(bǔ)齊。  回復(fù)  更多評(píng)論
            

          # re: [翻譯]加速Javascript:DOM操作優(yōu)化 2010-03-16 09:43 殘冰
          不錯(cuò),謝謝,繼續(xù)努力  回復(fù)  更多評(píng)論
            

          # re: [翻譯]加速Javascript:DOM操作優(yōu)化 2011-05-07 15:45 gujian
          這個(gè)地方寫的也不錯(cuò)可以參考:
          http://www.01yun.com/jssx  回復(fù)  更多評(píng)論
            

          主站蜘蛛池模板: 武山县| 白银市| 梧州市| 南漳县| 云霄县| 开封市| 门头沟区| 惠来县| 会泽县| 隆德县| 汕尾市| 德保县| 城步| 中卫市| 扶风县| 琼结县| 新源县| 湘潭市| 兴海县| 宝丰县| 肇州县| 峨眉山市| 焦作市| 大埔区| 怀柔区| 广水市| 磴口县| 九龙坡区| 满城县| 鹤庆县| 延吉市| 汶川县| 达日县| 额尔古纳市| 德格县| 页游| 博乐市| 板桥市| 台中县| 彭山县| 德昌县|