首頁 >web前端 >js教程 >使用Angular.js開發的注意事項

使用Angular.js開發的注意事項

高洛峰
高洛峰原創
2016-12-09 16:07:161031瀏覽

前言

近期一直在玩Angularjs,不得不說,相對於Knockout,Angularjs這一MVVM框架更強大,也更複雜,各種教程網上到處都是,不過真正用到項目的時候會遇到各種坑。

一、ng-repeat

ng-repeat 用來識別某個elem 需要重複輸出,同時重複輸出的內容需為唯一

<div ng-app="app" ng-controller="control">
  <h3 ng-repeat="content in repeatContent">ng-repeat: {{ content }}</h3>
</div>

    

let app = angular.module("app", []);
app.controller("control", ($scope) => {
  // 输出李滨泓
  $scope.repeatContent = ["李", "滨", "泓"];
  // 下面存在两个“泓”,会报错
  // $scope.repeatContent = ["李", "滨", "泓", "泓"];
})

的關係

factory


factory 很像service,不同之處在於,service 在Angular 中是一個單例對象,即當需要使用service 時,使用new 關鍵字來建立一個(也僅此一個)service。而 factory 則是一個普通的函數,當需要用時,他也只是一個普通函數的調用方式,它可以返回各種形式的數據,例如通過返回一個功能函數的集合對象來將供與使用。

定義:

let app = angular.module("app", []);
 
// 这里可以注入 $http 等 Provider
app.factory("Today", () => {
  let date = new Date();
  return {
    year: date.getFullYear(),
    month: date.getMonth() + 1,
    day: date.getDate()
  };
});

   

使用注入:

app.controller("control", (Today) => {
  console.log(Today.year);
  console.log(Today.month);
  console.log(Today.day);
});

   

service

   

service

因為它使用new 關鍵字新建,同時它可以用在controller 之間的通訊與資料交互,因為controller 在無用時其作用域鏈會被銷毀(例如使用路由跳到另一個頁面,同時使用了另一個controller)

定義:

let app = angular.module("app", []);
 
// 这里可以注入 $http 等 Provider
// 注意这里不可以使用 arrow function
// arrow function 不能作为 constructor
app.service("Today", function() {
  let date = new Date();
  this.year = date.getFullYear();
  this.month = date.getMonth() + 1;
  this.day = date.getDate();
});

   

使用注入:

app.controller("control", (Today) => {
  console.log(Today.year);
  console.log(Today.month);
  console.log(Today.day);
});

   

provider 前對provider 進行一些參數的配置。

定義:

let app = angular.module("app", []);
 
// 这里可以注入 $http 等 Provider
// 注意这里不可以使用 arrow function
// arrow function 不能作为 constructor
app.provider("Today", function() {
  this.date = new Date();
  let self = this;
 
  this.setDate = (year, month, day) => {
    this.date = new Date(year, month - 1, day);
  }
 
  this.$get = () => {
    return {
      year: this.date.getFullYear(),
      month: this.date.getMonth() + 1,
      day: this.date.getDate()
    };
  };
});

   

使用注入:

// 这里重新配置了今天的日期是 2015年2月15日
// 注意这里注入的是 TodayProvider,使用驼峰命名来注入正确的需要配置的 provider
app.config((TodayProvider) => {
  TodayProvider.setDate(2015, 2, 15);
});
 
app.controller("control", (Today) => {
  console.log(Today.year);
  console.log(Today.month);
  console.log(Today.day);
});

   

、handlebarbar ,而其中使用了handlebars 作為模板引擎,當node.js 對某URL 進行對應並render,由於其模板使用{ {} } 作為變數解析符號。同樣地,angular 也使用{ {} } 作為變數解析符號,所以當node.js 進行render 頁面後,如果{ {} } 內的變數不存在,則該個區域會被清空,而我的原意是這個作為angular 的解析所用,而不是handlebars 使用,同時我也想繼續使用handlebars,那麼此時就需要將angular 預設的{ {} } 解析符號重新定義。即使以依賴注入$interpolateProvider 進行定義,如下範例:

app.config($interpolateProvider => {
  $interpolateProvider.startSymbol(&#39;{[{&#39;);
  $interpolateProvider.endSymbol(&#39;}]}&#39;);
});

   

四、ng-annotate-loader

ng-annotate-loader 應用於webpack +ular 在開發場景JS 壓縮後導致依賴注入失效並出現錯誤的解決方法

安裝

$ npm install ng-annotate-loader --save-dev

   

配置

// webpack.config.js
{
  test: /\.js?$/,
  exclude: /(node_modules|bower_components)/,
  loader: &#39;ng-annotate!babel?presets=es2015&#39;
},

   

,$scope 裡的資料改變並不會引起$digest 的dirty-checking 循環,這將導致當model 改變時,view 不會同步更新,這時我們需要自己主動觸發更新

HTML

<div>{{ foo }}</div>
<button id="addBtn">go</button>

   

JavaScript

app.controller("control", ($scope) => {
  $scope.foo = 0;
  document.getElementById("addBtn").addEventListener("click", () => {
    $scope.foo++;
  }, false);
})

   


很明顯,示例的意圖是當點擊button 時,foo 自增長並更新View,但是實際上,$scope.foo 是改變了,但是View 並不會刷新,這是因為foo 並沒有一個$watch 檢測變化後$apply,最終引起$digest,所以我們需要自己觸發$apply 或創建一個$watch 來觸發或檢測數據變化

JavaScript(使用$apply)

app.controller("control", ($scope) => {
  $scope.foo = 0;
  document.getElementById("addBtn").addEventListener("click", () => {
     
    $scope.$apply(function() {
      $scope.foo++;
    });
 
  }, false);
})

   

JavaScript(使用$watch & $digest)

app.controller("control", ($scope) => {
  $scope.foo = 0;
  $scope.flag = 0;
 
  $scope.$watch("flag", (newValue, oldValue) => {
 
    // 当 $digest 循环检测 flag 时,如果新旧值不一致将调用该函数
    $scope.foo = $scope.flag;
  });
 
  document.getElementById("addBtn").addEventListener("click", () => {
     
    $scope.flag++;
    // 主动触发 $digest 循环
    $scope.$digest();
  }, false);
})

   

六、$watch(watchExpression, listener, [objectEquality])

註冊一個listener 回呼函數,每次發生回呼函數,在每次發生時都會改變的值在每次$digest 執行時被調用,並返回要被檢測的值(當多次輸入相同的值時,watchExpression 不應該改變其自身的值,否則可能會引起多次的$digest 循環,watchExpression 應該冪等等)

listener 將在當前watchExpression 回傳值和上次的watchExpression 回傳值不一致時被呼叫(使用!== 來嚴格地判斷不一致性,而不是使用== 來判斷,不過objectEquality == true 除外)

objectEquality 為boolean 值,當為true 時,將使用angular.equals 來判斷一致性,並使用angular.copy 來保存此次的Object 拷貝副本供給下一次的比較,這意味著複雜的對象檢測將會有效能和記憶體上的問題

七、$apply([exp])

$apply 是$scope 的函數,用於觸發$digest 循環

$apply 偽代碼

function $apply(expr) {
  try {
    return $eval(expr);
  } catch (e) {
    $exceptionHandler(e);
  } finally {
    $root.$digest();
  }
}

使用$eval(expr) 執行expr 表達式

如果在執行過程中跑出exception,那麼執​​行$exceptionHandler(e)

最後無論結果,都會執行一次 $digest 循環


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn