http://www.aygfsteel.com/ebecket 返還網(wǎng)
          隨筆-140  評(píng)論-11  文章-131  trackbacks-0

          Douglas Crockford談Ajax性能

          2008年12月25日,星期四
          這是Douglas Crockford在圣誕節(jié)前的一個(gè)講座。我覺(jué)得對(duì)于前端工程師來(lái)說(shuō)算是一個(gè)“新年大片”。英文好的建議直接去看原文http://ericmiraglia.com/blog/?p=140感謝Eric Miraglia提供了完整的文字記錄。
          Crock先用<記憶碎片 >這部電影作為引子。我還沒(méi)看過(guò),一定要找來(lái)看看。
          如 果要向不太了解JSON的解釋JSON為什么好Crock這句話說(shuō)的很清楚“A JSON message is less work for the server to generate, it moves much faster on the wire because it’s smaller, it’s less work for the browser to parse and render than an HTML document.”
          “The page is an application with a data connection to a server.”Crock對(duì)Ajax Revolution標(biāo)題的注解。他提到“the client dose not need a copy of the database. it just needs, at any moment, just enough information to serve the user”,后端把所有數(shù)據(jù)都交給前端處理,后端是省事了,而瀏覽器端的性能則變的極差。“the client and the server are in a dialog, and we make the messages between them be as small as possible”,這是一個(gè)關(guān)鍵原則。just-in-time data delivery(按需傳輸數(shù)據(jù))。
          這 些道理其實(shí)盡人皆知,但在設(shè)計(jì)復(fù)雜的應(yīng)用時(shí),在進(jìn)行多人協(xié)作的項(xiàng)目時(shí),仍然會(huì)犯這樣的錯(cuò)誤。為什么?因?yàn)槿绻娴囊獙?shí)現(xiàn)一個(gè)客戶端和服務(wù)器端的高效對(duì)話, 服務(wù)器端的設(shè)計(jì)就會(huì)變的復(fù)雜,我充分理解后端工程師也要考慮他們的問(wèn)題。但為了更高效率,后端多花一些心思是值得的,因?yàn)槟壳盀g覽器還是一個(gè)非常低效的應(yīng) 用平臺(tái)(這也是Google推出Chrome的原因),存在顯著的安全問(wèn)題和性能問(wèn)題。
          Crock舉的這個(gè)memoizer(緩存器)的例子非常經(jīng)典:
          var fibonacci = function(n){
          return n < 2 ? n :
          fibonacci(n - 1) + fibonacci(n - 2);
          };
          fibonacci(40);
          執(zhí)行自己 331,160,280 次
          var memoizer = function(memo, fundamental){
          var shell = function(n){
          var result = memo[n];
          if(typeof result !== ‘number’){
          result = fundamental(shell, n);
          memo[n] = result;
          }
          return result;
          }
          return shell;
          }
          var fibonacci = memoizer([0, 1], function(recur, n){
          return recur(n - 1) + recur(n - 2);
          });
          優(yōu)化后的程序僅執(zhí)行了38次。”the key to optimization is work avoidance.” (優(yōu)化的關(guān)鍵是“逃避工作”的技巧)

          Crock提到兩種優(yōu)化思路:streamline和special casing

          streamline(簡(jiǎn) 化為使效率更高):更換算法(選擇更好的算法)、逃避工作、清除冗余代碼(項(xiàng)目反復(fù)修改每次只是增加,往往忽略清除沒(méi)用的代碼)而且”These things are always good to do”這些事情應(yīng)該一直去做而不是等到出現(xiàn)性能問(wèn)題之后再解決。

          special casing理解成特別包裝更好一些。為了解決某些特別需求在原有代碼基礎(chǔ)上所做的包裝,優(yōu)其是對(duì)公共組件。會(huì)導(dǎo)致冗余代碼越來(lái)越多,增加代碼的 size,同時(shí)還會(huì)增加更多的測(cè)試路徑(如果代碼要進(jìn)行白盒測(cè)試,無(wú)疑測(cè)試成本將增加很多)。要知道產(chǎn)品的需求永遠(yuǎn)是善變的。處理這種特殊需求可以寫(xiě)一些 “適配器”或“橋接器”,不用的話就直接拿掉。

           

          “Avoid unnecessary displays or animation”很多產(chǎn)品經(jīng)理認(rèn)為花哨的交互就是好的用戶體驗(yàn),或者就是認(rèn)為它夠shiny,殊不知它帶來(lái)的負(fù)面影響遠(yuǎn)比它的作用要大的多。 Crock是這么說(shuō)的“A ‘wow’ effect is definitely worthwhile if it improves the user’s productivity, or improves the value of the experience to user.If it’s there just to show that we can do that, I think it’sa waste of our time, it’sa waste of the user’s time.”(譯:如果這樣做能提高用戶的生產(chǎn)力或提升用戶體驗(yàn)的價(jià)值,一個(gè)‘喔噻’的效果是價(jià)得的。但如果它的存在只是為了表明,我們能夠做到這一點(diǎn), 我認(rèn)為這是浪費(fèi)我們的時(shí)間,這是浪費(fèi)用戶的時(shí)間)
          重 點(diǎn)解決影響最大的性能問(wèn)題。看似費(fèi)話,其實(shí)在開(kāi)發(fā)中避重就輕的事沒(méi)少干。關(guān)鍵首先要準(zhǔn)確的判斷出哪些環(huán)節(jié)是最大的癥結(jié)所在。The bottleneck tends to be the DOM interface — DOM is a really inefficient API.(譯: 瓶頸傾向于DOM接囗 - DOM是一個(gè)相當(dāng)?shù)托У腁PI),還會(huì)有reflow計(jì)算的問(wèn)題。“So touch the DOM lightly, if you can.”怎么才能“touch lightly”,Crock告訴我們兩個(gè)技巧:一個(gè)是在創(chuàng)建的結(jié)點(diǎn)添加到DOM樹(shù)上之前對(duì)其進(jìn)行操作,這樣不會(huì)有回流計(jì)算的問(wèn)題。二是用 innerHTML,它只會(huì)跟DOM發(fā)生一次關(guān)系。
          var d = document.createElement(”div”);
          d.style.height = ‘100px’;
          d.style.border = ‘1px solid red’;
          d.style.color = ‘green’;
          …(該辦的事先辦完)…
          //最后再添加到dom樹(shù)中
          document.body.appendChild(d);

          “Don’t Tune For Quirks”不要調(diào)那些瀏覽器的怪異問(wèn)題。建議首先是盡量繞開(kāi)這些quirks,雖然通過(guò)一些技巧可以解決這些問(wèn)題,但也許在其它瀏覽器上性能就會(huì)變 差。而且當(dāng)瀏覽器升級(jí)后修復(fù)了這個(gè)問(wèn)題,之前所做的優(yōu)化可能會(huì)變得更糟。Crock建議不做短期優(yōu)化。
          短短三十多分鐘的講座細(xì)細(xì)品味的話可以引出很多思考。
          簡(jiǎn)單總結(jié)一下:
          一是客戶端要輕。程序不要臃腫,不要把所有數(shù)據(jù)都交給前端處理
          二是代碼是基礎(chǔ)。尋求更好的算法,更好的設(shè)計(jì)模式
          三是借住工具,不憑直覺(jué)
          四是要抓重點(diǎn)。揪住主要問(wèn)題解決
          五是DOM操作是瓶頸
          六是不要做短期優(yōu)化

          高性能的Ajax應(yīng)用-Julien Lecomte

          2007年12月21日,星期五

          高性能的Ajax應(yīng)用-Julien Lecomte

          第1部分 高性能的開(kāi)發(fā)

          1.為高性能計(jì)劃和設(shè)計(jì)
          -從每一天開(kāi)始就要計(jì)劃高性能
          -跟產(chǎn)品經(jīng)理和設(shè)計(jì)師緊密的合作
          -理解設(shè)計(jì)的基本原理
          -設(shè)計(jì)和性能之前做解釋和權(quán)衡
          -提供選擇和展示各種可能(原型)
          -挑戰(zhàn)自己,實(shí)現(xiàn)有挑戰(zhàn)性的設(shè)計(jì)(不要只說(shuō)“不”)
          -為了簡(jiǎn)化設(shè)計(jì)和交互,有時(shí)候需要妥協(xié)

          2.高性能工程開(kāi)發(fā):一些基本規(guī)則
          -少就是多。不要做任何不必要的事。直到變得絕對(duì)重要,否則不要做。
          -打破規(guī)則。只能在迫不得以的情況下,才能妥協(xié)并打破最佳做法。
          -在提升可以感覺(jué)到的性能上下功夫。

          3.衡量性能
          -使用用戶真實(shí)環(huán)境測(cè)試
          -在開(kāi)發(fā)中,profile你的代碼
          -使用自動(dòng)的profiling/性能測(cè)試
          -保持功能執(zhí)行的歷史數(shù)據(jù)
          -在產(chǎn)品中考慮保留一些(少量的)profiling代碼

          第2部分 高性能的頁(yè)面下載

          1.Yahoo!的14條性能原則
          一個(gè)頁(yè)面工作于3(有時(shí)是重疊的)個(gè)階段-下載,渲染,運(yùn)行。14條原則大部分作用于第1個(gè)階段。

          2.資源優(yōu)化
          -最小化CSS和Javascript文件。推薦使用YUI Compressor(http://developer.yahoo.com/yui/compressor)壓縮。遠(yuǎn)離那些所謂的先進(jìn)的壓縮方案,如Packer。
          -合并CSS和Javascript文件。在發(fā)布的時(shí)候合并(http://www.julienlecomte.net/blog/2007/09/16)或在運(yùn)行的時(shí)候合并。
          -優(yōu)化圖片資源。如:PngCrush(http://pmt.sourceforge.net/pngcrush)、PngOptimizer(http://psydk.org/PngOptimizer.php)等。

          3.減小非壓縮代碼的大小
          -下載和解析HTML、CSS、Javascript代碼的成本是很高的。
          -用簡(jiǎn)練寫(xiě)法和寫(xiě)更少的代碼
          -用好Javascript庫(kù)
          -在考慮將大的Javascript文件拆成小的文件(bundle)時(shí),解析和編譯腳本時(shí)要花費(fèi)大量額外的時(shí)間
          -按需下載代碼(HTML、CSS、Javascript),如,Lazy Loading
          * 參見(jiàn),http://ajaxpatterns.org/On-Demand_Javascript
          * 使用 YUI Loader
          * Dojo的package system
          * JSAN的Import System

          4.優(yōu)化初始渲染(1):綜合技巧
          -Consider rendering the first view on the server
          -關(guān)閉你的HTML標(biāo)簽提高解析速度。參見(jiàn)http://msdn2.microsoft.com/en-us/library/ms533020.aspx#Close_Your_Tags
          -考慮盡早緩存。
          -下載基本的資源,延遲或按需下載其他資源。使用YUI ImageLoader。

          5.優(yōu)化初始渲染(2):不要一直等onload
          -大部分DOM操作可以在onload事件觸發(fā)前完成
          -如果你需要控制你的初始化代碼,可以直接寫(xiě)在里,并把它放在靠近的位置
          -否則,使用YUI事件組件中的onDOMReady方法
          YAHOO.util.Event.onDOMReady(function () {
          // Do something here…
          // e.g., attach event handlers.
          });

          6.優(yōu)化初始渲染(3):在頁(yè)面下載后,再下載腳本。
          -一個(gè)好的網(wǎng)站應(yīng)該在Javascript失效下功能也應(yīng)該是完整的
          -因此,你可以延遲下載腳本
          -這樣做對(duì)下載其他資源(樣式表、圖片等)是有好處的
          -這樣做使網(wǎng)站下載更快

          7.優(yōu)化初始渲染(4):有條件的預(yù)下載
          -預(yù)下載潛在的資源(Javascript、CSS、圖片等)真的可以增強(qiáng)用戶體驗(yàn)。
          -可是,在什么時(shí)候進(jìn)行功妙的預(yù)下載是關(guān)鍵,否則,預(yù)下載會(huì)影響用戶體驗(yàn)。
          -參見(jiàn)http://www.sitepoint.com/article/web-site-optimization-steps/3
          -參見(jiàn)http://search.yahoo.com

          第3部分 高性能的Javascript

          1.減少符號(hào)查尋(1):范圍鏈
          -每次訪問(wèn)變量時(shí)都會(huì)執(zhí)行查尋
          -變量從當(dāng)前范圍向上執(zhí)行查尋
          -因此,無(wú)論何時(shí)都在相同范圍中聲明和使用變量
          -完全不要使用with,它會(huì)阻止編譯器生成代碼時(shí)訪問(wèn)本地變量的速度(因?yàn)槭紫纫闅v原型鏈,然后是范圍鏈等)
          -緩存外部變量到本地變量。
          不好的寫(xiě)法:
          var arr = …;
          var globalVar = 0;
          (function () {
          var i;
          for (i = 0; i < arr.length; i++) {
          globalVar++;
          }
          })();

          好的寫(xiě)法:
          var arr = …;
          var globalVar = 0;
          (function () {
          var i, l, localVar;
          l = arr.length;
          localVar = globalVar;
          for (i = 0; i < l; i++) {
          localVar++;
          }
          globalVar = localVar;
          })();

          2.減少符號(hào)查尋(2):原型鏈
          -訪問(wèn)主對(duì)象上的成員的速度比訪問(wèn)原型鏈上的成員的速度快25%
          -原型鏈越長(zhǎng)查尋越慢
          function A () {}
          A.prototype.prop1 = …;

          function B () {
          this.prop2 = …;
          }

          B.prototype = new A();
          var b = new B();//(譯者:prop2為b的主對(duì)象成員,而prop1是原型鏈上的成員)

          3.優(yōu)化對(duì)象實(shí)例化
          -如果你需要?jiǎng)?chuàng)建很多對(duì)象,可以考慮添加成員到原型中,而不添加到單個(gè)對(duì)象的構(gòu)造器中。
          -這樣會(huì)減少內(nèi)存的消耗
          -然而會(huì)拖慢查尋查尋對(duì)象成員的速度

          4.不要使用eval
          -傳字符串到eval中,需要編譯和解釋,相當(dāng)?shù)穆?br /> -完全不要傳一個(gè)字符串到setTimeout和setInterval中。可以使用匿名函數(shù)代替。
          setTimeout(function () {
          // Code to execute on a timeout
          }, 50);
          -完全不要eval做為方法的構(gòu)造器。

          5.優(yōu)化字符串連接
          -在IE(JScript)中,連接兩個(gè)字符串會(huì)導(dǎo)致一個(gè)新的字符串被重新分配資源,同時(shí)兩個(gè)原字符串被復(fù)制:
          var s = “xxx” + “yyy”;
          s += “zzz”;
          -因此在IE中,添加字符串到數(shù)組中然后再用Array.join連接比直接用+連接快很多(不要用在簡(jiǎn)單的連接中)
          -其他Javascript引擎(WebKit、SpiderMonkey)已經(jīng)對(duì)字符串連接做了優(yōu)化
          -使用YUI Compressor!

          6.優(yōu)化正則表達(dá)式
          -盡量不要用RegExp構(gòu)造,除非你的正則表達(dá)式需要實(shí)時(shí)創(chuàng)建。
          -使用test方法測(cè)試一個(gè)pattern(exec方法會(huì)有小的性能問(wèn)題)
          -使用非捕獲組(?:)
          -保持pattern的簡(jiǎn)單

          7.緩存
          -在下面情況下應(yīng)用緩存是合理的:
          * 更低成本的獲取一個(gè)值
          * 值會(huì)被經(jīng)常讀取
          * 值不經(jīng)常改變
          -會(huì)增加內(nèi)存消耗(權(quán)衡)
          -Memoization:
          Module Pattern:
          var fn = (function () {
          var b = false, v;
          return function () {
          if (!b) {
          v = …;
          b = true;
          }
          return v;
          };
          })();

          Store value in function object:
          function fn () {
          if (!fn.b) {
          fn.v = …;
          fn.b = true;
          }
          return fn.v;
          }

          Lazy function definition:
          var fn = function () {
          var v = …;
          return (fn = function () {
          return v;
          })();
          };

          8.如何控制長(zhǎng)時(shí)間運(yùn)行處理的Javascript
          -在Javascrit的長(zhǎng)時(shí)間運(yùn)行處理過(guò)程中,整個(gè)瀏覽器會(huì)被凍結(jié)
          -因此為了維持好的用戶體驗(yàn),確保Javascript一個(gè)線程在約300兆秒內(nèi)完成
          -你可以通過(guò)用setTimeout將長(zhǎng)運(yùn)行處理拆成的更小處理單元串起來(lái)執(zhí)行
          -更多見(jiàn)http://www.julienlecomte.net/blog/2007/10/28/
          -例子http://www.julienlecomte.net/blogfiles/javascript/long-running-js-process.html
          function doSomething (callbackFn) {
          // Initialize a few things here…
          (function () {
          // Do a little bit of work here…
          if (termination condition) {
          // We are done
          callbackFn();
          } else {
          > // P
          rocess next chunk
          setTimeout(arguments.callee, 0);
          }
          })();
          }

          9.綜合技巧
          -簡(jiǎn)單的操作符往往比相應(yīng)的方法要快
          c = Math.min(a, b);
          c = a < b ? a : b;//更快

          myArray.push(value);
          myArray[myArray.length] = value;//比上面快
          myArray[idx++] = value;//比上面快

          -避免使用try…catch在影響性能的部分:
          不好的寫(xiě)法:
          var i;
          for (i = 0; i < 100000; i++) {
          try {

          } catch (e) {

          }
          }
          好的寫(xiě)法:
          var i;
          try {
          for (i = 0; i < 100000; i++) {

          }
          } catch (e) {

          }
          -If possible, avoid for…in in performance-critical sections
          -無(wú)論何時(shí)分支條件都不改變的情況下,分支應(yīng)該在外面,不要在里面:
          不好的寫(xiě)法:
          function fn () {
          if (…) {

          } else {

          }
          }

          好的寫(xiě)法:
          var fn;
          if (…) {
          fn = function () {…};
          } else {
          fn = function () {…};
          }

          第4部分 高性能的動(dòng)態(tài)HTML

          1.文檔樹(shù)的修改:使用innerHTML
          注意事項(xiàng)http://www.julienlecomte.net/blog/2007/12/38

          2.文檔樹(shù)的修改:使用cloneNode
          注意:expando屬性或附加的事件會(huì)丟失

          3.文檔樹(shù)的修改:使用DocumentFragment
          -DocumentFragment(DOM Level 1 Core)是一個(gè)輕量級(jí)的文檔對(duì)象
          var i, j, el, table, tbody, row, cell, docFragment;
          docFragment = document.createDocumentFragment();
          el = document.createElement(”div”);
          docFragment.appendChild(el);
          table = document.createElement(”table”);
          el.appendChild(table);
          tbody = document.createElement(”tbody”);
          table.appendChild(tbody);
          for (i = 0; i < 1000; i++) {

          }
          document.body.appendChild(docFragment);
          -它僅僅支持常規(guī)DOM方法和屬性的子集
          -IE實(shí)現(xiàn)DocumentFragment不服從W3C規(guī)范

          4.限制事件柄的個(gè)數(shù)
          -附加事件到上百個(gè)元素上的成本很高
          -多個(gè)事件柄會(huì)增加潛在的內(nèi)存漏洞
          -解決方案:使用事件委托機(jī)制,一種依靠事件冒泡的機(jī)制

          5.限制回流(Reflow)

          第5部分 高性能的布局和CSS

          綜合技巧
          -使用CSS Sprites
          -避免使用Javascript布局
          -避免使用IE表達(dá)式
          -避免使用IE濾鏡(或盡可能少用)
          -優(yōu)化Table布局
          -優(yōu)化CSS選擇器http://developer.mozilla.org/en/docs/Writing_Efficient_CSS

          第6部分 高性能的Ajax

          1.綜合技巧
          -完全不要使用同步的XMLHttpRequest。參見(jiàn)http://yuiblog.com/blog/2006/04/04/synchronous-v-asynchronous
          -編程處理網(wǎng)絡(luò)超時(shí)
          -解決方案:使用YUI Connection Manager
          var callback = {
          success: function () { /* Do something */ },
          failure: function () { /* Do something */ },
          timeout: 5000
          };
          YAHOO.util.Connect.asyncRequest(”GET”, url, callback);

          2.提升可以感覺(jué)到的網(wǎng)絡(luò)延遲體驗(yàn)
          -如果數(shù)據(jù)在提交到服務(wù)器端之前經(jīng)過(guò)本地校驗(yàn),通常請(qǐng)求的成功率達(dá)99.9%
          -因此,為了優(yōu)化用戶體驗(yàn),我們可以采用下面的Pattern:
          * 當(dāng)請(qǐng)求發(fā)出時(shí)要更新UI
          * Lock the UI/data structures with the finest possible granularity.
          * 讓用戶知道發(fā)生了什么事
          * 讓用戶知道為什么UI被鎖定
          * 當(dāng)成功返回結(jié)果后要及時(shí)解除鎖定
          * 要用優(yōu)雅的方式處理錯(cuò)誤

          3.綜合技巧
          -知道并發(fā)HTTP/1.1連接的最大數(shù)量
          -如果后端支持,支持多元的Ajax請(qǐng)求
          -Piggyback unsollicited notifications in a response to an Ajax request.
          -用JSON代替XML做為數(shù)據(jù)交換格式
          -推送,不要輪詢。使用COMET向?yàn)g覽器發(fā)送實(shí)時(shí)的通知
          -考慮使用本地存儲(chǔ)器緩存數(shù)據(jù)。
          * IE的userData
          * Flash本地存儲(chǔ)
          * DOM:Storage(WHATWG持久存儲(chǔ)API, 已在Firefox2中實(shí)現(xiàn))
          * Google Gears
          * 其它

          第7部分 性能工具

          -YSlow? http://developer.yahoo.com/yslow
          -Task Manager
          -IE Leak Detector a.k.a Drip [ http://www.outofhanwell.com/ieleak/ ]
          -Stopwatch profiling
          * AjaxView [ http://research.microsoft.com/projects/ajaxview/ ]
          * JsLex [ http://rockstarapps.com/pmwiki/pmwiki.php?n=JsLex.JsLex ]
          * YUI profiler [ http://developer.yahoo.com/yui/profiler/ ]
          -Venkman or Firebug Profiler [ http://www.getfirebug.com/ ]

          (原文:http://yuiblog.com/blog/2007/12/20/video-lecomte/

          posted on 2010-02-28 23:53 becket_zheng 閱讀(257) 評(píng)論(0)  編輯  收藏 所屬分類: 網(wǎng)頁(yè)web前端技術(shù)
          主站蜘蛛池模板: 宁河县| 凤山市| 麻城市| 孟连| 廊坊市| 黑龙江省| 格尔木市| 宁夏| 潜山县| 乐山市| 平凉市| 赤城县| 光山县| 苍梧县| 康定县| 湖南省| 汉寿县| 五指山市| 邛崃市| 洞口县| 长岭县| 武乡县| 高雄市| 武隆县| 万年县| 安泽县| 比如县| 婺源县| 永登县| 建瓯市| 易门县| 葫芦岛市| 旅游| 当涂县| 东安县| 华阴市| 大连市| 尉氏县| 西畴县| 伽师县| 辛集市|