使用Angular.js開發(fā)的注意事項
前言
近期一直在玩Angularjs,不得不說,相對于Knockout,Angularjs這一MVVM框架更強大,也更復雜,各種教程網(wǎng)上到處都是,不過真正用到項目的時候會遇到各種坑。
一、ng-repeat
ng-repeat 用于標識某個 elem 需要重復輸出,同時重復輸出的內(nèi)容需為唯一
<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 = ["李", "濱", "泓", "泓"];
})
二、provider, service, factory 之間的關系
factory
factory 很像 service,不同之處在于,service 在 Angular 中是一個單例對象,即當需要使用 service 時,使用 new 關鍵字來創(chuàng)建一個(也僅此一個)service。而 factory 則是一個普通的函數(shù),當需要用時,他也僅僅是一個普通函數(shù)的調(diào)用方式,它可以返回各種形式的數(shù)據(jù),例如通過返回一個功能函數(shù)的集合對象來將供與使用。
定義:
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 在使用時是一個單例對象,同時也是一個 constructor,它的特點讓它可以不返回任何東西,因為它使用 new 關鍵字新建,同時它可以用在 controller 之間的通訊與數(shù)據(jù)交互,因為 controller 在無用時其作用域鏈會被銷毀(例如使用路由跳轉(zhuǎn)到另一個頁面,同時使用了另一個 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 是 service 的底層創(chuàng)建方式,可以理解 provider 是一個可配置版的 service,我們可以在正式注入 provider 前對 provider 進行一些參數(shù)的配置。
定義:
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);
});
三、handlebars 與 angular 符號解析沖突
場景:
當我使用 node.js 作為服務端,而其中使用了 handlebars 作為模板引擎,當 node.js 對某 URL 進行相應并 render,由于其模板使用 { {} } 作為變量解析符號。同樣地,angular 也使用 { {} } 作為變量解析符號,所以當 node.js 進行 render 頁面后,如果 { {} } 內(nèi)的變量不存在,則該個區(qū)域會被清空,而我的原意是這個作為 angular 的解析所用,而不是 handlebars 使用,同時我也想繼續(xù)使用 handlebars,那么此時就需要將 angular 默認的 { {} } 解析符號重新定義。即使用依賴注入 $interpolateProvider 進行定義,如下示例:
app.config($interpolateProvider => {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
四、ng-annotate-loader
ng-annotate-loader 應用于 webpack + angular 的開發(fā)場景,是用于解決 angular 在進行 JS 壓縮后導致依賴注入失效并出現(xiàn)錯誤的解決方法
安裝
$ npm install ng-annotate-loader --save-dev
配置
// webpack.config.js
{
test: /\.js?$/,
exclude: /(node_modules|bower_components)/,
loader: 'ng-annotate!babel?presets=es2015'
},
五、雙向數(shù)據(jù)綁定
當我們使用非 Angular 自帶的事件時,$scope 里的數(shù)據(jù)改變并不會引起 $digest 的 dirty-checking 循環(huán),這將導致當 model 改變時,view 不會同步更新,這時我們需要自己主動觸發(fā)更新
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,所以我們需要自己觸發(fā) $apply 或者創(chuàng)建一個 $watch 來觸發(fā)或檢測數(shù)據(jù)變化
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 循環(huán)檢測 flag 時,如果新舊值不一致將調(diào)用該函數(shù)
$scope.foo = $scope.flag;
});
document.getElementById("addBtn").addEventListener("click", () => {
$scope.flag++;
// 主動觸發(fā) $digest 循環(huán)
$scope.$digest();
}, false);
})
六、$watch(watchExpression, listener, [objectEquality])
注冊一個 listener 回調(diào)函數(shù),在每次 watchExpression 的值發(fā)生改變時調(diào)用
watchExpression 在每次 $digest 執(zhí)行時被調(diào)用,并返回要被檢測的值(當多次輸入同樣的值時,watchExpression 不應該改變其自身的值,否則可能會引起多次的 $digest 循環(huán),watchExpression 應該冪等)
listener 將在當前 watchExpression 返回值和上次的 watchExpression 返回值不一致時被調(diào)用(使用 !== 來嚴格地判斷不一致性,而不是使用 == 來判斷,不過 objectEquality == true 除外)
objectEquality 為 boolean 值,當為 true 時,將使用 angular.equals 來判斷一致性,并使用 angular.copy 來保存此次的 Object 拷貝副本供給下一次的比較,這意味著復雜的對象檢測將會有性能和內(nèi)存上的問題
七、$apply([exp])
$apply 是 $scope 的一個函數(shù),用于觸發(fā) $digest 循環(huán)
$apply 偽代碼
function $apply(expr) {
try {
return $eval(expr);
} catch (e) {
$exceptionHandler(e);
} finally {
$root.$digest();
}
}
使用 $eval(expr) 執(zhí)行 expr 表達式
如果在執(zhí)行過程中跑出 exception,那么執(zhí)行 $exceptionHandler(e)
最后無論結果,都會執(zhí)行一次 $digest 循環(huán)
總結
以上就是這篇文章的全部內(nèi)容,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。
- 學習Angular中作用域需要注意的坑
- Angular.js與Bootstrap相結合實現(xiàn)表格分頁代碼
- 淺談angular.js中實現(xiàn)雙向綁定的方法$watch $digest $apply
- Angular.js如何從PHP讀取后臺數(shù)據(jù)
- 總結十個Angular.js由淺入深的面試問題
- Angular.js回顧ng-app和ng-model使用技巧
- Angular.js與Bootstrap相結合實現(xiàn)手風琴菜單代碼
- Angular.js 實現(xiàn)數(shù)字轉(zhuǎn)換漢字實例代碼
- angular.js之路由的選擇方法
- angular.js分頁代碼的實例
相關文章
通過angular CDK實現(xiàn)頁面元素拖放的步驟詳解
這篇文章主要給大家介紹了關于如何通過angular CDK實現(xiàn)頁面元素拖放的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者使用angular具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2020-07-07

