亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

總結(jié)AngularJS開發(fā)者最常犯的十個錯誤

 更新時間:2016年08月31日 17:20:54   投稿:daisy  
AngularJS是如今最受歡迎的JS框架之一,簡化開發(fā)過程是它的目標(biāo)之一,這使得它非常適合于元型較小的apps的開發(fā),但也擴展到具有全部特征的客戶端應(yīng)用的開發(fā)。下面給大家總結(jié)了AngularJS開發(fā)者最常犯的十個錯誤,有需要的可以參考學(xué)習(xí)下。

前言

AngularJS易于開發(fā)、較多的特征及較好的效果導(dǎo)致了較多的應(yīng)用,伴隨而來的是一些陷阱。本文列舉了AngularJS的一些共同的易于出問題的地方,下面來一起看看吧。

一、MVC目錄結(jié)構(gòu)

AngularJS,直白地說,就是一個MVC框架。它的模型并沒有像backbone.js框架那樣定義的如此明確,但它的體系結(jié)構(gòu)卻恰如其分。當(dāng)你工作于一個MVC框架時,普遍的做法是根據(jù)文件類型對其進行歸類:

templates/
 _login.html
 _feed.html
app/
 app.js
 controllers/
 LoginController.js
 FeedController.js
 directives/
 FeedEntryDirective.js
 services/
 LoginService.js
 FeedService.js
 filters/
 CapatalizeFilter.js

看起來,這似乎是一個顯而易見的結(jié)構(gòu),更何況Rails也是這么干的。然而一旦app規(guī)模開始擴張,這種結(jié)構(gòu)會導(dǎo)致你一次需要打開很多目錄,無論你是使用sublime,Visual Studio或是Vim結(jié)合Nerd Tree,你都會投入很多時間在目錄樹中不斷地滑上滑下。

與按照類型劃分文件不同,取而代之的,我們可以按照特性劃分文件:

app/
 app.js
 Feed/
 _feed.html
 FeedController.js
 FeedEntryDirective.js
 FeedService.js
 Login/
 _login.html
 LoginController.js
 LoginService.js
 Shared/
 CapatalizeFilter.js

這種目錄結(jié)構(gòu)使得我們能夠更容易地找到與某個特性相關(guān)的所有文件,繼而加快我們的開發(fā)進度。盡管將.html和.js文件置于一處可能存在爭議,但節(jié)省下來的時間更有價值。

二、模塊

將所有東西都一股腦放在主模塊下是很常見的,對于小型app,剛開始并沒有什么問題,然而很快你就會發(fā)現(xiàn)坑爹的事來了。

var app = angular.module('app',[]);
app.service('MyService', function(){
 //service code
});
app.controller('MyCtrl', function($scope, MyService){
 //controller code
});

在此之后,一個常見的策略是對相同類型的對象歸類。

var services = angular.module('services',[]);
services.service('MyService', function(){
 //service code
});
 
var controllers = angular.module('controllers',['services']);
controllers.controller('MyCtrl', function($scope, MyService){
 //controller code
});
 
var app = angular.module('app',['controllers', 'services']);

這種方式和前面第一部分所談到的目錄結(jié)構(gòu)差不多:不夠好。根據(jù)相同的理念,可以按照特性歸類,這會帶來可擴展性。

var sharedServicesModule = angular.module('sharedServices',[]);
sharedServices.service('NetworkService', function($http){});
 
var loginModule = angular.module('login',['sharedServices']);
loginModule.service('loginService', function(NetworkService){});
loginModule.controller('loginCtrl', function($scope, loginService){});
 
var app = angular.module('app', ['sharedServices', 'login']);

當(dāng)我們開發(fā)一個大型應(yīng)用程序時,可能并不是所有東西都包含在一個頁面上。將同一類特性置于一個模塊內(nèi),能使跨app間重用模塊變得更容易。

三、依賴注入

依賴注入是AngularJS最好的模式之一,它使得測試更為簡單,并且依賴任何指定對象都很明確。AngularJS的注入方式非常靈活,最簡單的方式只需要將依賴的名字傳入模塊的function中即可:

var app = angular.module('app',[]);
 
app.controller('MainCtrl', function($scope, $timeout){
 $timeout(function(){
 console.log($scope);
 }, 1000);
});

這里,很明顯,MainCtrl依賴$scope$timeout

直到你準(zhǔn)備將其部署到生產(chǎn)環(huán)境并希望精簡代碼時,一切都很美好。如果使用UglifyJS,之前的例子會變成下面這樣:

var app=angular.module("app",[]);
app.controller("MainCtrl",function(e,t){t(function(){console.log(e)},1e3)})

現(xiàn)在AngularJS怎么知道MainCtrl依賴誰?AngularJS提供了一種非常簡單的解決方法,即將依賴作為一個數(shù)組傳入,數(shù)組的最后一個元素是一個函數(shù),所有的依賴項作為它的參數(shù)。

app.controller('MainCtrl', ['$scope', '$timeout', function($scope, $timeout){
 $timeout(function(){
 console.log($scope);
 }, 1000);
}]);

這樣做能夠精簡代碼,并且AngularJS知道如何解釋這些明確的依賴:

app.controller("MainCtrl",["$scope","$timeout",function(e,t){t(function(){console.log(e)},1e3)}])

3.1 全局依賴

在編寫AngularJS程序時,時常會出現(xiàn)這種情況:某個對象有一個依賴,而這個對象又將其自身綁定在全局scope上,這意味著在任何AngularJS代碼中這個依賴都是可用的,但這卻破壞了依賴注入模型,并會導(dǎo)致一些問題,尤其體現(xiàn)在測試過程中。

使用AngularJS可以很容易的將這些全局依賴封裝進模塊中,所以它們可以像AngularJS標(biāo)準(zhǔn)模塊那樣被注入進去。

Underscrore.js是一個很贊的庫,它可以以函數(shù)式的風(fēng)格簡化Javascript代碼,通過以下方式,你可以將其轉(zhuǎn)化為一個模塊:

var underscore = angular.module('underscore', []);
underscore.factory('_', function() {
 return window._; //Underscore must already be loaded on the page
});
var app = angular.module('app', ['underscore']);
 
app.controller('MainCtrl', ['$scope', '_', function($scope, _) {
 init = function() {
  _.keys($scope);
 }
 
 init();
}]);

這樣的做法允許應(yīng)用程序繼續(xù)以AngularJS依賴注入的風(fēng)格進行開發(fā),同時在測試階段也能將underscore交換出去。

這可能看上去十分瑣碎,沒什么必要,但如果你的代碼中正在使用use strict(而且必須使用),那這就是必要的了。

四、控制器膨脹

控制器是AngularJS的肉和土豆,一不小心就會將過多的邏輯加入其中,尤其是剛開始的時候。控制器永遠(yuǎn)都不應(yīng)該去操作DOM,或是持有DOM選擇器,那是我們需要使用指令和ng-model的地方。同樣的,業(yè)務(wù)邏輯應(yīng)該存在于服務(wù)中,而非控制器。

數(shù)據(jù)也應(yīng)該存儲在服務(wù)中,除非它們已經(jīng)被綁定在$scope上了。服務(wù)本身是單例的,在應(yīng)用程序的整個生命周期都存在,然而控制器在應(yīng)用程序的各狀態(tài)間是瞬態(tài)的。如果數(shù)據(jù)被保存在控制器中,當(dāng)它被再次實例化時就需要重新從某處獲取數(shù)據(jù)。即使將數(shù)據(jù)存儲于localStorage中,檢索的速度也要比Javascript變量慢一個數(shù)量級。

AngularJS在遵循單一職責(zé)原則(SRP)時運行良好,如果控制器是視圖和模型間的協(xié)調(diào)者,那么它所包含的邏輯就應(yīng)該盡量少,這同樣會給測試帶來便利。

五、Service vs Factory

幾乎每一個AngularJS開發(fā)人員在初學(xué)時都會被這些名詞所困擾,這真的不太應(yīng)該,因為它們就是針對幾乎相同事物的語法糖而已!

以下是它們在AngularJS源代碼中的定義:

function factory(name, factoryFn) { 
 return provider(name, { $get: factoryFn }); 
}
 
function service(name, constructor) {
 return factory(name, ['$injector', function($injector) {
 return $injector.instantiate(constructor);
 }]);
}

從源代碼中你可以看到,service僅僅是調(diào)用了factory函數(shù),而后者又調(diào)用了provider函數(shù)。事實上,AngularJS也為一些值、常量和裝飾提供額外的provider封裝,而這些并沒有導(dǎo)致類似的困惑,它們的文檔都非常清晰。

由于service僅僅是調(diào)用了factory函數(shù),這有什么區(qū)別呢?線索在$injector.instantiate:在這個函數(shù)中,$injectorservice的構(gòu)造函數(shù)中創(chuàng)建了一個新的實例。

以下是一個例子,展示了一個service和一個factory如何完成相同的事情:

var app = angular.module('app',[]);
 
app.service('helloWorldService', function(){
 this.hello = function() {
 return "Hello World";
 };
});
 
app.factory('helloWorldFactory', function(){
 return {
 hello: function() {
  return "Hello World";
 }
 }
});

當(dāng)helloWorldServicehelloWorldFactory被注入到控制器中,它們都有一個hello方法,返回”hello world”。service的構(gòu)造函數(shù)在聲明時被實例化了一次,同時factory對象在每一次被注入時傳遞,但是仍然只有一個factory實例。所有的providers都是單例。

既然能做相同的事,為什么需要兩種不同的風(fēng)格呢?相對于service,factory提供了更多的靈活性,因為它可以返回函數(shù),這些函數(shù)之后可以被新建出來。這迎合了面向?qū)ο缶幊讨泄S模式的概念,工廠可以是一個能夠創(chuàng)建其他對象的對象。

app.factory('helloFactory', function() {
 return function(name) {
 this.name = name;
 
 this.hello = function() {
  return "Hello " + this.name;
 };
 };
});

這里是一個控制器示例,使用了service和兩個factoryhelloFactory返回了一個函數(shù),當(dāng)新建對象時會設(shè)置name的值。

app.controller('helloCtrl', function($scope, helloWorldService, helloWorldFactory, helloFactory) {
 init = function() {
 helloWorldService.hello(); //'Hello World'
 helloWorldFactory.hello(); //'Hello World'
 new helloFactory('Readers').hello() //'Hello Readers'
 }
 
 init();
});

在初學(xué)時,最好只使用service。

Factory在設(shè)計一個包含很多私有方法的類時也很有用:

app.factory('privateFactory', function(){
 var privateFunc = function(name) {
 return name.split("").reverse().join(""); //reverses the name
 };
 
 return {
 hello: function(name){
  return "Hello " + privateFunc(name);
 }
 };
});

通過這個例子,我們可以讓privateFactory的公有API無法訪問到privateFunc方法,這種模式在service中是可以做到的,但在factory中更容易。

六、沒有使用Batarang

Batarang是一個出色的Chrome插件,用來開發(fā)和測試AngularJS app。

Batarang提供了瀏覽模型的能力,這使得我們有能力觀察AngularJS內(nèi)部是如何確定綁定到作用域上的模型的,這在處理指令以及隔離一定范圍觀察綁定值時非常有用。

Batarang也提供了一個依賴圖, 如果我們正在接觸一個未經(jīng)測試的代碼庫,這個依賴圖就很有用,它能決定哪些服務(wù)應(yīng)該被重點關(guān)照。

最后,Batarang提供了性能分析。Angular能做到開包即用,性能良好,然而對于一個充滿了自定義指令和復(fù)雜邏輯的應(yīng)用而言,有時候就不那么流暢了。使用Batarang性能工具,能夠直接觀察到在一個digest周期中哪個函數(shù)運行了最長時間。性能工具也能展示一棵完整的watch樹,在我們擁有很多watcher時,這很有用。

七、過多的watcher

在上一點中我們提到,AngularJS能做到開包即用,性能良好。由于需要在一個digest周期中完成臟數(shù)據(jù)檢查,一旦watcher的數(shù)量增長到大約2000時,這個周期就會產(chǎn)生顯著的性能問題。(2000這個數(shù)字不能說一定會造成性能大幅下降,但這是一個不錯的經(jīng)驗數(shù)值。在AngularJS 1.3 release版本中,已經(jīng)有一些允許嚴(yán)格控制digest周期的改動了。)

以下這個“立即執(zhí)行的函數(shù)表達式(IIFE)”會打印出當(dāng)前頁面上所有的watcher的個數(shù),你可以簡單的將其粘貼到控制臺中,觀察結(jié)果。這段IIFE來源于Jared在StackOverflow上的回答:

(function () { 
 var root = $(document.getElementsByTagName('body'));
 var watchers = [];
 
 var f = function (element) {
 if (element.data().hasOwnProperty('$scope')) {
  angular.forEach(element.data().$scope.$$watchers, function (watcher) {
  watchers.push(watcher);
  });
 }
 
 angular.forEach(element.children(), function (childElement) {
  f($(childElement));
 });
 };
 
 f(root);
 
 console.log(watchers.length);
})();

通過這個方式得到watcher的數(shù)量,結(jié)合Batarang性能板塊中的watch樹,應(yīng)該可以看到哪里存在重復(fù)代碼,或著哪里存在不變數(shù)據(jù)同時擁有watch。

當(dāng)存在不變數(shù)據(jù),而你又想用AngularJS將其模版化,可以考慮使用bindonce。Bindonce是一個簡單的指令,允許你使用AngularJS中的模版,但它并不會加入watch,這就保證了watch數(shù)量不會增長。

八、限定$scope的范圍

Javascript基于原型的繼承與面向?qū)ο笾谢陬惖睦^承有著微妙的區(qū)別,這通常不是什么問題,但這個微妙之處在使用$scope時就會表現(xiàn)出來。在AngularJS中,每個$scope都會繼承父$scope,最高層稱之為$rootScope。($scope與傳統(tǒng)指令有些不同,它們有一定的作用范圍i,且只繼承顯式聲明的屬性。)

由于原型繼承的特點,在父類和子類間共享數(shù)據(jù)不太重要,不過如果不小心的話,也很容易誤用了一個父$scope的屬性。

比如說,我們需要在一個導(dǎo)航欄上顯示一個用戶名,這個用戶名是在登錄表單中輸入的,下面這種嘗試應(yīng)該是能工作的:

<div ng-controller="navCtrl">
 <span>{{user}}</span>
 <div ng-controller="loginCtrl">
 <span>{{user}}</span>
 <input ng-model="user"></input>
 </div>
</div>

那么問題來了……:在text input中設(shè)置了user的ng-model,當(dāng)用戶在其中輸入內(nèi)容時,哪個模版會被更新?navCtrl還是loginCtrl,還是都會?

如果你選擇了loginCtrl,那么你可能已經(jīng)理解了原型繼承是如何工作的了。

當(dāng)你檢索字面值時,原型鏈并不起作用。如果navCtrl也同時被更新的話,檢索原型鏈?zhǔn)潜仨毜模?strong>但如果值是一個對象,這就會發(fā)生。(記住,在Javascript中,函數(shù)、數(shù)組和對象都是對象)

所以為了獲得預(yù)期的行為,需要在navCtrl中創(chuàng)建一個對象,它可以被loginCtrl引用。

<div ng-controller="navCtrl">
 <span>{{user.name}}</span>
 <div ng-controller="loginCtrl">
 <span>{{user.name}}</span>
 <input ng-model="user.name"></input>
 </div>
</div>

現(xiàn)在,由于user是一個對象,原型鏈就會起作用,navCtrl模版和$scopeloginCtrl都會被更新。

這看上去是一個很做作的例子,但是當(dāng)你使用某些指令去創(chuàng)建子$scope,如ngRepeat時,這個問題很容易就會產(chǎn)生。

九、手工測試

由于TDD可能不是每個開發(fā)人員都喜歡的開發(fā)方式,因此當(dāng)開發(fā)人員檢查代碼是否工作或是否影響了其它東西時,他們會做手工測試。

不去測試AngularJS app,這是沒有道理的。AngularJS的設(shè)計使得它從頭到底都是可測試的,依賴注入和ngMock模塊就是明證。AngularJS核心團隊已經(jīng)開發(fā)了眾多能夠使測試更上一層樓的工具。

9.1 Protractor

單元測試是一個測試工作的基礎(chǔ),但考慮到app的日益復(fù)雜,集成測試更貼近實際情況。幸運的是,AngularJS的核心團隊已經(jīng)提供了必要的工具。

我們已經(jīng)建立了Protractor,一個端到端的測試器用以模擬用戶交互,這能夠幫助你驗證你的AngularJS程序的健康狀況。

Protractor使用Jasmine測試框架定義測試,Protractor針對不同的頁面交互行為有一個非常健壯的API。

我們還有一些其他的端到端測試工具,但是Protractor的優(yōu)勢是它能夠理解如何與AngularJS代碼協(xié)同工作,尤其是在$digest周期中。

9.2 Karma

一旦我們用Protractor完成了集成測試的編寫工作,接下去就是執(zhí)行測試了。等待測試執(zhí)行,尤其是集成測試,對每個開發(fā)人員都是一種淡淡的憂傷。AngularJS的核心團隊也感到極為蛋疼,于是他們開發(fā)了Karma。

Karma是一個測試器,它有助于關(guān)閉反饋回路。Karma之所以能夠做到這點,是因為它在指定文件被改變時就運行測試。Karma同時也會在多個瀏覽器上運行測試,不同的設(shè)備也可以指向Karma服務(wù)器,這樣就能夠更好地覆蓋真實世界的應(yīng)用場景。

十、使用jQuery

jQuery是一個酷炫的庫,它有標(biāo)準(zhǔn)化的跨平臺開發(fā),幾乎已經(jīng)成為了現(xiàn)代化Web開發(fā)的必需品。不過盡管JQuery如此多的優(yōu)秀特性,它的理念和AngularJS并不一致。

AngularJS是一個用來建立app的框架,而JQuery則是一個簡化“HTML文檔操作、事件處理、動畫和Ajax”的庫。這是兩者最基本的區(qū)別,AngularJS致力于程序的體系結(jié)構(gòu),與HTML頁面無關(guān)。

為了更好的理解如何建立一個AngularJS程序,請停止使用jQuery。JQuery使開發(fā)人員以現(xiàn)存的HTML標(biāo)準(zhǔn)思考問題,但正如文檔里所說的,“AngularJS能夠讓你在應(yīng)用程序中擴張HTML這個詞匯”。

DOM操作應(yīng)該只在指令中完成,但這并不意味著他們只能用JQuery封裝。在你使用JQuery之前,你應(yīng)該總是去想一下這個功能是不是AngularJS已經(jīng)提供了。當(dāng)指令互相依賴時能夠創(chuàng)建強大的工具,這確實很強大。

但一個非常棒的JQuery是必需品時,這一天可能會到來,但在一開始就引入它,是一個常見的錯誤。

總結(jié)

AngularJS是一卓越的框架,在社區(qū)的幫助下始終在進步。雖說AngularJS仍然是一個不斷發(fā)展的概念,但我希望人們能夠遵循以上談到的這些約定,避免開發(fā)AngularJS應(yīng)用所遇到的那些問題。希望這篇文章的內(nèi)容對大家能有有所幫助,如果有問題可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • angular組件間傳值測試的方法詳解

    angular組件間傳值測試的方法詳解

    這篇文章主要給大家介紹了關(guān)于如何測試angular組件間傳值的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用angular組件具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • 使用AngularJS編寫多選按鈕選中時觸發(fā)指定方法的指令代碼詳解

    使用AngularJS編寫多選按鈕選中時觸發(fā)指定方法的指令代碼詳解

    最近做項目時遇到了需要用到多選按鈕選中觸發(fā)事件的功能,小編試著手寫一個指令,具體實現(xiàn)代碼大家參考下本文吧
    2017-07-07
  • Angularjs修改密碼的實例代碼

    Angularjs修改密碼的實例代碼

    這篇文章主要介紹了Angularjs修改密碼的實例代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-05-05
  • AngularJS實現(xiàn)動態(tài)切換樣式的方法分析

    AngularJS實現(xiàn)動態(tài)切換樣式的方法分析

    這篇文章主要介紹了AngularJS實現(xiàn)動態(tài)切換樣式的方法,結(jié)合實例形式分析了AngularJS事件響應(yīng)與樣式動態(tài)控制相關(guān)操作技巧,需要的朋友可以參考下
    2018-06-06
  • 最新評論