angular性能優(yōu)化心得

不知不覺镶柱,在項(xiàng)目中用angular已經(jīng)半年多了型豁,踩了很多坑酸些。 趁著放假壮吩,把a(bǔ)ngular的3本書都看了遍,結(jié)合這半年的經(jīng)驗(yàn)贯城,是該做個(gè)總結(jié)了熊楼。 希望可以給大家?guī)?lái)啟示,少踩點(diǎn)坑能犯。
本文針對(duì)的讀者:

  • 具備JavaScript性能優(yōu)化的相關(guān)知識(shí)( 雅虎14條性能優(yōu)化原則鲫骗、《高性能網(wǎng)站建設(shè)指南》等)
  • 擁有angular實(shí)戰(zhàn)經(jīng)驗(yàn)。

臟數(shù)據(jù)檢查 != 輪詢檢查更新

談起angular的 臟檢查機(jī)制(dirty-checking) , 常見的誤解就是認(rèn)為: ng是定時(shí)輪詢?nèi)z查model是否變更踩晶。 其實(shí)执泰,ng只有在指定事件觸發(fā)后,才進(jìn)入 $digest cycle

  • DOM事件渡蜻,譬如用戶輸入文本术吝,點(diǎn)擊按鈕等。( ng-click)
  • XHR響應(yīng)事件 ( $http)
  • 瀏覽器Location變更事件 ( $location)
  • Timer事件( $timeout , $interval)
  • 執(zhí)行 $digest()或 $apply()

參考《mastering web application development with angularjs》 P294

$digest后批量更新UI

傳統(tǒng)的JS MVC框架, 數(shù)據(jù)變更是通過(guò)setter去觸發(fā)事件茸苇,然后立即更新UI排苍。 而angular則是進(jìn)入 $digest cycle,等待所有model都穩(wěn)定后学密,才批量一次性更新UI淘衙。 這種機(jī)制能減少瀏覽器repaint次數(shù),從而提高性能腻暮。

參考《mastering web application development with angularjs》 P296
另推薦閱讀: 構(gòu)建自己的AngularJS彤守,第一部分:Scope和Digest

提速 $digest cycle

盡少的觸發(fā)$digest (P310)
盡快的執(zhí)行$digest

優(yōu)化$watch

$scope.$watch(watchExpression, modelChangeCallback) , watchExpression可以是String或Function毯侦。
避免watchExpression中執(zhí)行耗時(shí)操作 ,因?yàn)樗诿看?digest都會(huì)執(zhí)行1~2次具垫。
避免watchExpression中操作dom侈离,因?yàn)樗芎臅r(shí)。

清除console.log

console.log也很耗時(shí)做修,記得發(fā)布時(shí)干掉它霍狰。(用grunt groundskeeper)

ng-if vs ng-show

前者會(huì)移除DOM和對(duì)應(yīng)的watch

及時(shí)移除不必要的$watch抡草。

var unwatch = $scope.$watch("someKey", function(newValue, oldValue){ 
   if(someCondition){  
      unwatch(); //當(dāng)不需要的時(shí)候,及時(shí)移除watch
  } 
});

參考《mastering web application development with angularjs》 P303~309

避免深度watch饰及, 即第三個(gè)參數(shù)為true

參考《mastering web application development with angularjs》 P313

減少watch的變量長(zhǎng)度 如下,angular不會(huì)僅對(duì) {{variable}}

建立watcher康震,而是對(duì)整個(gè)p標(biāo)簽燎含。雙括號(hào)應(yīng)該被span包裹,因?yàn)閣atch的是外部element腿短。

<p>plain text other {{variable}} plain text other</p>
//改為:<p>plain text other <span ng-bind='variable'></span> plain text other</p>
//或<p>plain text other <span>{{variable}}</span> plain text other</p>

參考《mastering web application development with angularjs》 P314

$apply vs $digest

$apply會(huì)使ng進(jìn)入 $digest cycle, 并從$rootScope開始遍歷(深度優(yōu)先)檢查數(shù)據(jù)變更屏箍。
$digest僅會(huì)檢查該scope和它的子scope,當(dāng)你確定當(dāng)前操作僅影響它們時(shí)橘忱,用$digest可以稍微提升性能赴魁。

參考《mastering web application development with angularjs》 P308

延遲執(zhí)行

一些不必要的操作,放到 $timeout里面延遲執(zhí)行钝诚。
如果不涉及數(shù)據(jù)變更颖御,還可以加上第三個(gè)參數(shù)false,避免調(diào)用 $apply凝颇。
對(duì)時(shí)間有要求的潘拱,第二個(gè)參數(shù)可以設(shè)置為0。

$http.get('http://path/to/url').success(function(data){ 
  $scope.name = data.name;
  $timeout(function(){ //do sth later, such as log }, 0, false);
});

$evalAsync vs $timeout

http://stackoverflow.com/questions/17301572/angularjs-evalasync-vs-timeout
directive中執(zhí)行的 $evalAsync拧略,會(huì)在angular操作DOM之后芦岂,瀏覽器渲染之前執(zhí)行。
controller中執(zhí)行的 $evalAsync垫蛆, 會(huì)在angular操作DOM之前執(zhí)行禽最,一般不這么用。
而使用 $timeout袱饭,會(huì)在瀏覽器渲染之后執(zhí)行弛随。

優(yōu)化ng-repeat

  • 限制列表個(gè)數(shù)
    列表對(duì)象的數(shù)據(jù)轉(zhuǎn)換,在放入scope之前處理宁赤。如 $scope.dataList = convert(dataFromServer)舀透。
    可以使用 ngInfiniteScroll 來(lái)做無(wú)限滾動(dòng)。

  • 使用 track by
    刷新數(shù)據(jù)時(shí)决左,我們常這么做: $scope.tasks = data || []; 愕够。這會(huì)導(dǎo)致angular移除掉所有的DOM走贪,重新創(chuàng)建和渲染。 若優(yōu)化為 ng-repeat="task in tasks track by task.id后惑芭,angular就能復(fù)用task對(duì)應(yīng)的原DOM進(jìn)行更新坠狡,減少不必要渲染。

參見: http://www.codelord.net/2014/04/15/improving-ng-repeat-performance-with-track-by

  • 使用bindonce減少綁定
    我們都知道angular建議一個(gè)頁(yè)面最多2000個(gè)雙向綁定遂跟,但在列表頁(yè)面通常很容易超標(biāo)逃沿。 譬如一個(gè)滑動(dòng)到底部加載下頁(yè)的表格,一行20+個(gè)綁定, 展示個(gè)100行就超標(biāo)了幻锁。 但其實(shí)很多屬性顯示后是幾乎不會(huì)變更的凯亮, 這時(shí)候就沒必要雙向綁定了。

慎用filter

在$digest過(guò)程中哄尔,filter會(huì)執(zhí)行很多次假消,至少兩次。 所以要避免在filter中執(zhí)行耗時(shí)操作 岭接。

angular.module('filtersPerf', []).filter('double', function(){ 
  return function(input) { 
     console.log('Calling double on: '+input); return input + input;   //至少輸出兩次  
  };
});

可以在controller中預(yù)先處理

mainCtrl.jsangular.module('filtersPerf', []).controller('mainCtrl',function($scope, $filter){
  $scope.dataList = $filter('double')(dataFromServer);
});

參考《mastering web application development with angularjs》 P136

慎用事件

減少事件廣播富拗,使用雙向數(shù)據(jù)綁定或共享service等方法來(lái)代替。
$broadcast會(huì)遍歷scope和它的子scope鸣戴,而不是只通知注冊(cè)了該事件的子scope啃沪。一個(gè)優(yōu)化方式是使用 $emit.

參見 https://github.com/angular/angular.js/issues/4574

1.2.7版本對(duì)事件做過(guò)一個(gè)優(yōu)化,

參見 https://github.com/angular/angular.js/blob/master/CHANGELOG.md#127-emoji-clairvoyance-2014-01-03

對(duì)高頻的事件做緩沖限速窄锅,避免觸發(fā)太頻繁创千。

directive

跟scope數(shù)據(jù)無(wú)關(guān)的操作放在compile階段,它只執(zhí)行一次酬滤。
除了directive外其他地方签餐,特別是controller里面不要操作dom, 尤其是綁定到scope后盯串,便是災(zāi)難氯檐。改變以前使用JQuery那樣 以DOM為中心的思維,擁抱以數(shù)據(jù)為中心的思維体捏。

參見: http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background
翻譯: http://blog.jobbole.com/46589/

使用Batarang來(lái)分析性能

AngularJS Batarang 是官方提供的chrome插件

搬運(yùn)自: http://atian25.github.io/2014/05/09/angular-performace/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末冠摄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子几缭,更是在濱河造成了極大的恐慌河泳,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件年栓,死亡現(xiàn)場(chǎng)離奇詭異拆挥,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)某抓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門纸兔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)惰瓜,“玉大人,你說(shuō)我怎么就攤上這事汉矿∑榉唬” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵洲拇,是天一觀的道長(zhǎng)奈揍。 經(jīng)常有香客問(wèn)我,道長(zhǎng)赋续,這世上最難降的妖魔是什么男翰? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮蚕捉,結(jié)果婚禮上奏篙,老公的妹妹穿的比我還像新娘柴淘。我一直安慰自己迫淹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布为严。 她就那樣靜靜地躺著敛熬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪第股。 梳的紋絲不亂的頭發(fā)上应民,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音夕吻,去河邊找鬼诲锹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涉馅,可吹牛的內(nèi)容都是我干的归园。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼稚矿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼庸诱!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起晤揣,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤桥爽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后昧识,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钠四,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年跪楞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缀去。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片环疼。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖朵耕,靈堂內(nèi)的尸體忽然破棺而出炫隶,到底是詐尸還是另有隱情,我是刑警寧澤阎曹,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布伪阶,位于F島的核電站,受9級(jí)特大地震影響处嫌,放射性物質(zhì)發(fā)生泄漏栅贴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一熏迹、第九天 我趴在偏房一處隱蔽的房頂上張望檐薯。 院中可真熱鬧,春花似錦注暗、人聲如沸坛缕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)赚楚。三九已至,卻和暖如春骗卜,著一層夾襖步出監(jiān)牢的瞬間宠页,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工寇仓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留举户,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓遍烦,卻偏偏與公主長(zhǎng)得像俭嘁,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乳愉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容