99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

AngularJS開(kāi)發(fā)的一些注意事項(xiàng)

發(fā)布時(shí)間:2017-05-15 12:42  回復(fù):0  查看:2335   最后回復(fù):2017-05-15 12:42  
AngularJS 是目前最為活躍的 Javascript 框架之一, AngularJS 的目標(biāo)之一是簡(jiǎn)化開(kāi)發(fā)過(guò)程,這使得 AngularJS 非常善于構(gòu)建小型 app 原型,但 AngularJS 對(duì)于全功能的客戶端應(yīng)用程序同樣強(qiáng)大,它結(jié)合了開(kāi)發(fā)簡(jiǎn)便,特性廣泛和出眾的性能,使其被廣泛使用。然而,大量使用也會(huì)產(chǎn)生諸多誤區(qū)。本文和大家分享的就是AngularJS 開(kāi)發(fā)最常犯的錯(cuò)誤,一起來(lái)看看吧,希望對(duì)大家 學(xué)習(xí)AngularJS有所幫助。
  1. MVC 目錄結(jié)構(gòu)
  AngularJS ,直白地說(shuō),就是一個(gè) MVC 框架。它的模型并沒(méi)有像 backbone.js 框架那樣定義的如此明確,但它的體系結(jié)構(gòu)卻恰如其分。當(dāng)你工作于一個(gè) MVC 框架時(shí),普遍的做法是根據(jù)文件類型對(duì)其進(jìn)行歸類:
  templates/
  _login.html
  _feed.html
  app/
  app.js
  controllers/
  LoginController.js
  FeedController.js
  directives/
  FeedEntryDirective.js
  services/
  LoginService.js
  FeedService.js
  filters/
  CapatalizeFilter.js
  看起來(lái),這似乎是一個(gè)顯而易見(jiàn)的結(jié)構(gòu),更何況Rails 也是這么干的。然而一旦 app 規(guī)模開(kāi)始擴(kuò)張,這種結(jié)構(gòu)會(huì)導(dǎo)致你一次需要打開(kāi)很多目錄,無(wú)論你是使用 sublime Visual Studio 或是 Vim 結(jié)合 Nerd Tree ,你都會(huì)投入很多時(shí)間在目錄樹中不斷地滑上滑下。
  與按照類型劃分文件不同,取而代之的,我們可以按照特性劃分文件:
  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)使得我們能夠更容易地找到與某個(gè)特性相關(guān)的所有文件,繼而加快我們的開(kāi)發(fā)進(jìn)度。盡管將.html .js 文件置于一處可能存在爭(zhēng)議,但節(jié)省下來(lái)的時(shí)間更有價(jià)值。
  2.  模塊
  將所有東西都一股腦放在主模塊下是很常見(jiàn)的,對(duì)于小型app ,剛開(kāi)始并沒(méi)有什么問(wèn)題,然而很快你就會(huì)發(fā)現(xiàn)坑爹的事來(lái)了。
  var app = angular.module('app',[]);
  app.service('MyService', function(){
  //service code
  });
  app.controller('MyCtrl', function($scope, MyService){
  //controller code
  });
  在此之后,一個(gè)常見(jiàn)的策略是對(duì)相同類型的對(duì)象歸類。
  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ù)相同的理念,可以按照特性歸類,這會(huì)帶來(lái)可擴(kuò)展性。
  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)我們開(kāi)發(fā)一個(gè)大型應(yīng)用程序時(shí),可能并不是所有東西都包含在一個(gè)頁(yè)面上。將同一類特性置于一個(gè)模塊內(nèi),能使跨app 間重用模塊變得更容易。
  3.  依賴注入
  依賴注入是AngularJS 最好的模式之一,它使得測(cè)試更為簡(jiǎn)單,并且依賴任何指定對(duì)象都很明確。 AngularJS 的注入方式非常靈活,最簡(jiǎn)單的方式只需要將依賴的名字傳入模塊的 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)境并希望精簡(jiǎn)代碼時(shí),一切都很美好。如果使用UglifyJS ,之前的例子會(huì)變成下面這樣:
  var app=angular.module("app",[]);
  app.controller("MainCtrl",function(e,t){t(function(){console.log(e)},1e3)})
  現(xiàn)在AngularJS 怎么知道 MainCtrl 依賴誰(shuí)? AngularJS 提供了一種非常簡(jiǎn)單的解決方法,即將依賴作為一個(gè)數(shù)組傳入,數(shù)組的最后一個(gè)元素是一個(gè)函數(shù),所有的依賴項(xiàng)作為它的參數(shù)。
  app.controller('MainCtrl', ['$scope', '$timeout', function($scope, $timeout){
  $timeout(function(){
  console.log($scope);
  }, 1000);
  }]);
  這樣做能夠精簡(jiǎn)代碼,并且AngularJS 知道如何解釋這些明確的依賴:
  app.controller("MainCtrl",["$scope","$timeout",function(e,t){t(function(){console.log(e)},1e3)}])
  3.1  全局依賴
  在編寫AngularJS 程序時(shí),時(shí)常會(huì)出現(xiàn)這種情況:某個(gè)對(duì)象有一個(gè)依賴,而這個(gè)對(duì)象又將其自身綁定在全局 scope 上,這意味著在任何 AngularJS 代碼中這個(gè)依賴都是可用的,但這卻破壞了依賴注入模型,并會(huì)導(dǎo)致一些問(wèn)題,尤其體現(xiàn)在測(cè)試過(guò)程中。
  使用AngularJS 可以很容易的將這些全局依賴封裝進(jìn)模塊中,所以它們可以像 AngularJS 標(biāo)準(zhǔn)模塊那樣被注入進(jìn)去。
  Underscrore.js 是一個(gè)很贊的庫(kù),它可以以函數(shù)式的風(fēng)格簡(jiǎn)化 Javascript 代碼,通過(guò)以下方式,你可以將其轉(zhuǎn)化為一個(gè)模塊:
  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)格進(jìn)行開(kāi)發(fā),同時(shí)在測(cè)試階段也能將 underscore 交換出去。
  這可能看上去十分瑣碎,沒(méi)什么必要,但如果你的代碼中正在使用use strict (而且必須使用),那這就是必要的了。
  4.  控制器膨脹
  控制器是AngularJS 的肉和土豆,一不小心就會(huì)將過(guò)多的邏輯加入其中,尤其是剛開(kāi)始的時(shí)候。控制器永遠(yuǎn)都不應(yīng)該去操作 DOM ,或是持有 DOM 選擇器,那是我們需要使用指令和 ng-model 的地方。同樣的,業(yè)務(wù)邏輯應(yīng)該存在于服務(wù)中,而非控制器。
  數(shù)據(jù)也應(yīng)該存儲(chǔ)在服務(wù)中,除非它們已經(jīng)被綁定在$scope 上了。服務(wù)本身是單例的,在應(yīng)用程序的整個(gè)生命周期都存在,然而控制器在應(yīng)用程序的各狀態(tài)間是瞬態(tài)的。如果數(shù)據(jù)被保存在控制器中,當(dāng)它被再次實(shí)例化時(shí)就需要重新從某處獲取數(shù)據(jù)。即使將數(shù)據(jù)存儲(chǔ)于 localStorage 中,檢索的速度也要比 Javascript 變量慢一個(gè)數(shù)量級(jí)。
  AngularJS 在遵循單一職責(zé)原則( SRP )時(shí)運(yùn)行良好,如果控制器是視圖和模型間的協(xié)調(diào)者,那么它所包含的邏輯就應(yīng)該盡量少,這同樣會(huì)給測(cè)試帶來(lái)便利。
  5. Service vs Factory
  幾乎每一個(gè)AngularJS 開(kāi)發(fā)人員在初學(xué)時(shí)都會(huì)被這些名詞所困擾,這真的不太應(yīng)該,因?yàn)樗鼈兙褪轻槍?duì)幾乎相同事物的語(yǔ)法糖而已!
  以下是它們?cè)?/span>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ù)。事實(shí)上, AngularJS 也為一些值、常量和裝飾提供額外的 provider 封裝,而這些并沒(méi)有導(dǎo)致類似的困惑,它們的文檔都非常清晰。
  由于service 僅僅是調(diào)用了 factory 函數(shù),這有什么區(qū)別呢?線索在 $injector.instantiate :在這個(gè)函數(shù)中, $injector service 的構(gòu)造函數(shù)中創(chuàng)建了一個(gè)新的實(shí)例。
  以下是一個(gè)例子,展示了一個(gè)service 和一個(gè) 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)helloWorldService helloWorldFactory 被注入到控制器中,它們都有一個(gè) hello 方法,返回 ”hello world” 。 service 的構(gòu)造函數(shù)在聲明時(shí)被實(shí)例化了一次,同時(shí) factory 對(duì)象在每一次被注入時(shí)傳遞,但是仍然只有一個(gè) factory 實(shí)例。所有的 providers 都是單例。
  既然能做相同的事,為什么需要兩種不同的風(fēng)格呢?相對(duì)于service , factory 提供了更多的靈活性,因?yàn)樗梢苑祷睾瘮?shù),這些函數(shù)之后可以被新建出來(lái)。這迎合了面向?qū)ο缶幊讨泄S模式的概念,工廠可以是一個(gè)能夠創(chuàng)建其他對(duì)象的對(duì)象。
  app.factory('helloFactory', function() {
  return function(name) {
  this.name = name;
  this.hello = function() {
  return "Hello " + this.name;
  };
  };
  });
  這里是一個(gè)控制器示例,使用了service 和兩個(gè) factory , helloFactory 返回了一個(gè)函數(shù),當(dāng)新建對(duì)象時(shí)會(huì)設(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é)時(shí),最好只使用service 。
  Factory 在設(shè)計(jì)一個(gè)包含很多私有方法的類時(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);
  }
  };
  });
  通過(guò)這個(gè)例子,我們可以讓privateFactory 的公有 API 無(wú)法訪問(wèn)到 privateFunc 方法,這種模式在 service 中是可以做到的,但在 factory 中更容易。
  6.  沒(méi)有使用 Batarang
  Batarang 是一個(gè)出色的 Chrome 插件,用來(lái)開(kāi)發(fā)和測(cè)試 AngularJS app
  Batarang 提供了瀏覽模型的能力,這使得我們有能力觀察 AngularJS 內(nèi)部是如何確定綁定到作用域上的模型的,這在處理指令以及隔離一定范圍觀察綁定值時(shí)非常有用。
  Batarang 也提供了一個(gè)依賴圖, 如果我們正在接觸一個(gè)未經(jīng)測(cè)試的代碼庫(kù),這個(gè)依賴圖就很有用,它能決定哪些服務(wù)應(yīng)該被重點(diǎn)關(guān)照。
  最后,Batarang 提供了性能分析。 Angular 能做到開(kāi)包即用,性能良好,然而對(duì)于一個(gè)充滿了自定義指令和復(fù)雜邏輯的應(yīng)用而言,有時(shí)候就不那么流暢了。使用 Batarang 性能工具,能夠直接觀察到在一個(gè) digest 周期中哪個(gè)函數(shù)運(yùn)行了最長(zhǎng)時(shí)間。性能工具也能展示一棵完整的 watch 樹,在我們擁有很多 watcher 時(shí),這很有用。
  7.  過(guò)多的 watcher
  在上一點(diǎn)中我們提到,AngularJS 能做到開(kāi)包即用,性能良好。由于需要在一個(gè) digest 周期中完成臟數(shù)據(jù)檢查,一旦 watcher 的數(shù)量增長(zhǎng)到大約 2000 時(shí),這個(gè)周期就會(huì)產(chǎn)生顯著的性能問(wèn)題。( 2000 這個(gè)數(shù)字不能說(shuō)一定會(huì)造成性能大幅下降,但這是一個(gè)不錯(cuò)的經(jīng)驗(yàn)數(shù)值。在 AngularJS 1.3 release 版本中,已經(jīng)有一些允許嚴(yán)格控制 digest 周期的改動(dòng)了, Aaron Gray 有一篇很好的文章對(duì)此進(jìn)行解釋。)
  以下這個(gè)立即執(zhí)行的函數(shù)表達(dá)式 (IIFE)” 會(huì)打印出當(dāng)前頁(yè)面上所有的 watcher 的個(gè)數(shù),你可以簡(jiǎn)單的將其粘貼到控制臺(tái)中,觀察結(jié)果。這段 IIFE 來(lái)源于 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);
  })();
  通過(guò)這個(gè)方式得到watcher 的數(shù)量,結(jié)合 Batarang 性能板塊中的 watch 樹,應(yīng)該可以看到哪里存在重復(fù)代碼,或著哪里存在不變數(shù)據(jù)同時(shí)擁有 watch 。
  當(dāng)存在不變數(shù)據(jù),而你又想用AngularJS 將其模版化,可以考慮使用 bindonce 。 Bindonce 是一個(gè)簡(jiǎn)單的指令,允許你使用 AngularJS 中的模版,但它并不會(huì)加入 watch ,這就保證了 watch 數(shù)量不會(huì)增長(zhǎng)。
  8.  限定 $scope 的范圍
  Javascript 基于原型的繼承與面向?qū)ο笾谢陬惖睦^承有著微妙的區(qū)別,這通常不是什么問(wèn)題,但這個(gè)微妙之處在使用 $scope 時(shí)就會(huì)表現(xiàn)出來(lái)。在 AngularJS 中,每個(gè) $scope 都會(huì)繼承父 $scope ,最高層稱之為 $rootScope 。( $scope 與傳統(tǒng)指令有些不同,它們有一定的作用范圍 i ,且只繼承顯式聲明的屬性。)
  由于原型繼承的特點(diǎn),在父類和子類間共享數(shù)據(jù)不太重要,不過(guò)如果不小心的話,也很容易誤用了一個(gè)父$scope 的屬性。
  比如說(shuō),我們需要在一個(gè)導(dǎo)航欄上顯示一個(gè)用戶名,這個(gè)用戶名是在登錄表單中輸入的,下面這種嘗試應(yīng)該是能工作的:
  <div ng-controller="navCtrl">
  <span>{{user}}span>
  <div ng-controller="loginCtrl">
  <span>{{user}}span>
  <input ng-model="user">input>
  div>div>
  那么問(wèn)題來(lái)了…… :在 text input 中設(shè)置了 user ng-model ,當(dāng)用戶在其中輸入內(nèi)容時(shí),哪個(gè)模版會(huì)被更新? navCtrl 還是 loginCtrl ,還是都會(huì)?
  如果你選擇了loginCtrl ,那么你可能已經(jīng)理解了原型繼承是如何工作的了。
  當(dāng)你檢索字面值時(shí),原型鏈并不起作用。如果navCtrl 也同時(shí)被更新的話,檢索原型鏈?zhǔn)潜仨毜?;但如果值是一個(gè)對(duì)象,這就會(huì)發(fā)生。(記住,在 Javascript 中,函數(shù)、數(shù)組和對(duì)象都是對(duì)象)
  所以為了獲得預(yù)期的行為,需要在navCtrl 中創(chuàng)建一個(gè)對(duì)象,它可以被 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 是一個(gè)對(duì)象,原型鏈就會(huì)起作用, navCtrl 模版和 $scope loginCtrl 都會(huì)被更新。
  這看上去是一個(gè)很做作的例子,但是當(dāng)你使用某些指令去創(chuàng)建子$scope ,如 ngRepeat 時(shí),這個(gè)問(wèn)題很容易就會(huì)產(chǎn)生。
  9.  手工測(cè)試
  由于TDD 可能不是每個(gè)開(kāi)發(fā)人員都喜歡的開(kāi)發(fā)方式,因此當(dāng)開(kāi)發(fā)人員檢查代碼是否工作或是否影響了其它東西時(shí),他們會(huì)做手工測(cè)試。
  不去測(cè)試AngularJS app ,這是沒(méi)有道理的。 AngularJS 的設(shè)計(jì)使得它從頭到底都是可測(cè)試的,依賴注入和 ngMock 模塊就是明證。 AngularJS 核心團(tuán)隊(duì)已經(jīng)開(kāi)發(fā)了眾多能夠使測(cè)試更上一層樓的工具。
  9.1 Protractor
  單元測(cè)試是一個(gè)測(cè)試工作的基礎(chǔ),但考慮到app 的日益復(fù)雜,集成測(cè)試更貼近實(shí)際情況。幸運(yùn)的是, AngularJS 的核心團(tuán)隊(duì)已經(jīng)提供了必要的工具。
  我們已經(jīng)建立了Protractor ,一個(gè)端到端的測(cè)試器用以模擬用戶交互,這能夠幫助你驗(yàn)證你的 AngularJS 程序的健康狀況。
  Protractor 使用 Jasmine 測(cè)試框架定義測(cè)試, Protractor 針對(duì)不同的頁(yè)面交互行為有一個(gè)非常健壯的 API 。
  我們還有一些其他的端到端測(cè)試工具,但是Protractor 的優(yōu)勢(shì)是它能夠理解如何與 AngularJS 代碼協(xié)同工作,尤其是在 $digest 周期中。
  9.2 Karma
  一旦我們用Protractor 完成了集成測(cè)試的編寫工作,接下去就是執(zhí)行測(cè)試了。等待測(cè)試執(zhí)行,尤其是集成測(cè)試,對(duì)每個(gè)開(kāi)發(fā)人員都是一種淡淡的憂傷。 AngularJS 的核心團(tuán)隊(duì)也感到極為蛋疼,于是他們開(kāi)發(fā)了 Karma 。
  Karma 是一個(gè)測(cè)試器,它有助于關(guān)閉反饋回路。 Karma 之所以能夠做到這點(diǎn),是因?yàn)樗谥付ㄎ募桓淖儠r(shí)就運(yùn)行測(cè)試。 Karma 同時(shí)也會(huì)在多個(gè)瀏覽器上運(yùn)行測(cè)試,不同的設(shè)備也可以指向 Karma 服務(wù)器,這樣就能夠更好地覆蓋真實(shí)世界的應(yīng)用場(chǎng)景。
  10.  使用 jQuery
  jQuery 是一個(gè)酷炫的庫(kù),它有標(biāo)準(zhǔn)化的跨平臺(tái)開(kāi)發(fā),幾乎已經(jīng)成為了現(xiàn)代化 Web 開(kāi)發(fā)的必需品。不過(guò)盡管 JQuery 如此多的優(yōu)秀特性,它的理念和 AngularJS 并不一致。
  AngularJS 是一個(gè)用來(lái)建立 app 的框架,而 JQuery 則是一個(gè)簡(jiǎn)化 “HTML 文檔操作、事件處理、動(dòng)畫和 Ajax” 的庫(kù)。這是兩者最基本的區(qū)別, AngularJS 致力于程序的體系結(jié)構(gòu),與 HTML 頁(yè)面無(wú)關(guān)。
  為了更好的理解如何建立一個(gè)AngularJS 程序,請(qǐng)停止使用 jQuery 。 JQuery 使開(kāi)發(fā)人員以現(xiàn)存的 HTML 標(biāo)準(zhǔn)思考問(wèn)題,但正如文檔里所說(shuō)的, “AngularJS 能夠讓你在應(yīng)用程序中擴(kuò)張 HTML 這個(gè)詞匯 。
  DOM 操作應(yīng)該只在指令中完成,但這并不意味著他們只能用 JQuery 封裝。在你使用 JQuery 之前,你應(yīng)該總是去想一下這個(gè)功能是不是 AngularJS 已經(jīng)提供了。當(dāng)指令互相依賴時(shí)能夠創(chuàng)建強(qiáng)大的工具,這確實(shí)很強(qiáng)大。
  但一個(gè)非常棒的JQuery 是必需品時(shí),這一天可能會(huì)到來(lái),但在一開(kāi)始就引入它,是一個(gè)常見(jiàn)的錯(cuò)誤。
  結(jié)論
  AngularJS 是一卓越的框架,在社區(qū)的幫助下始終在進(jìn)步。雖說(shuō) AngularJS 仍然是一個(gè)不斷發(fā)展的概念,但我希望人們能夠遵循以上談到的這些約定,避免開(kāi)發(fā) AngularJS 應(yīng)用所遇到的那些問(wèn)題。


來(lái)源:網(wǎng)絡(luò)
您還未登錄,請(qǐng)先登錄

熱門帖子

最新帖子

?