使用Angular.js開(kāi)發(fā)的注意事項(xiàng)
前言
近期一直在玩Angularjs,不得不說(shuō),相對(duì)于Knockout,Angularjs這一MVVM框架更強(qiáng)大,也更復(fù)雜,各種教程網(wǎng)上到處都是,不過(guò)真正用到項(xiàng)目的時(shí)候會(huì)遇到各種坑。
一、ng-repeat
ng-repeat 用于標(biāo)識(shí)某個(gè) elem 需要重復(fù)輸出,同時(shí)重復(fù)輸出的內(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 = ["李", "濱", "泓"];
// 下面存在兩個(gè)“泓”,會(huì)報(bào)錯(cuò)
// $scope.repeatContent = ["李", "濱", "泓", "泓"];
})
二、provider, service, factory 之間的關(guān)系
factory
factory 很像 service,不同之處在于,service 在 Angular 中是一個(gè)單例對(duì)象,即當(dāng)需要使用 service 時(shí),使用 new 關(guān)鍵字來(lái)創(chuàng)建一個(gè)(也僅此一個(gè))service。而 factory 則是一個(gè)普通的函數(shù),當(dāng)需要用時(shí),他也僅僅是一個(gè)普通函數(shù)的調(diào)用方式,它可以返回各種形式的數(shù)據(jù),例如通過(guò)返回一個(gè)功能函數(shù)的集合對(duì)象來(lái)將供與使用。
定義:
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 在使用時(shí)是一個(gè)單例對(duì)象,同時(shí)也是一個(gè) constructor,它的特點(diǎn)讓它可以不返回任何東西,因?yàn)樗褂?new 關(guān)鍵字新建,同時(shí)它可以用在 controller 之間的通訊與數(shù)據(jù)交互,因?yàn)?controller 在無(wú)用時(shí)其作用域鏈會(huì)被銷毀(例如使用路由跳轉(zhuǎn)到另一個(gè)頁(yè)面,同時(shí)使用了另一個(gè) 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 是一個(gè)可配置版的 service,我們可以在正式注入 provider 前對(duì) provider 進(jìn)行一些參數(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,使用駝峰命名來(lái)注入正確的需要配置的 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 符號(hào)解析沖突
場(chǎng)景:
當(dāng)我使用 node.js 作為服務(wù)端,而其中使用了 handlebars 作為模板引擎,當(dāng) node.js 對(duì)某 URL 進(jìn)行相應(yīng)并 render,由于其模板使用 { {} } 作為變量解析符號(hào)。同樣地,angular 也使用 { {} } 作為變量解析符號(hào),所以當(dāng) node.js 進(jìn)行 render 頁(yè)面后,如果 { {} } 內(nèi)的變量不存在,則該個(gè)區(qū)域會(huì)被清空,而我的原意是這個(gè)作為 angular 的解析所用,而不是 handlebars 使用,同時(shí)我也想繼續(xù)使用 handlebars,那么此時(shí)就需要將 angular 默認(rèn)的 { {} } 解析符號(hào)重新定義。即使用依賴注入 $interpolateProvider 進(jìn)行定義,如下示例:
app.config($interpolateProvider => {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
四、ng-annotate-loader
ng-annotate-loader 應(yīng)用于 webpack + angular 的開(kāi)發(fā)場(chǎng)景,是用于解決 angular 在進(jìn)行 JS 壓縮后導(dǎo)致依賴注入失效并出現(xiàn)錯(cuò)誤的解決方法
安裝
$ 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ù)綁定
當(dāng)我們使用非 Angular 自帶的事件時(shí),$scope 里的數(shù)據(jù)改變并不會(huì)引起 $digest 的 dirty-checking 循環(huán),這將導(dǎo)致當(dāng) model 改變時(shí),view 不會(huì)同步更新,這時(shí)我們需要自己主動(dòng)觸發(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);
})
很明顯,示例的意圖是當(dāng)點(diǎn)擊 button 時(shí),foo 自增長(zhǎng)并更新 View,但是實(shí)際上,$scope.foo 是改變了,但是 View 并不會(huì)刷新,這是因?yàn)?foo 并沒(méi)有一個(gè) $watch 檢測(cè)變化后 $apply,最終引起 $digest,所以我們需要自己觸發(fā) $apply 或者創(chuàng)建一個(gè) $watch 來(lái)觸發(fā)或檢測(cè)數(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) => {
// 當(dāng) $digest 循環(huán)檢測(cè) flag 時(shí),如果新舊值不一致將調(diào)用該函數(shù)
$scope.foo = $scope.flag;
});
document.getElementById("addBtn").addEventListener("click", () => {
$scope.flag++;
// 主動(dòng)觸發(fā) $digest 循環(huán)
$scope.$digest();
}, false);
})
六、$watch(watchExpression, listener, [objectEquality])
注冊(cè)一個(gè) listener 回調(diào)函數(shù),在每次 watchExpression 的值發(fā)生改變時(shí)調(diào)用
watchExpression 在每次 $digest 執(zhí)行時(shí)被調(diào)用,并返回要被檢測(cè)的值(當(dāng)多次輸入同樣的值時(shí),watchExpression 不應(yīng)該改變其自身的值,否則可能會(huì)引起多次的 $digest 循環(huán),watchExpression 應(yīng)該冪等)
listener 將在當(dāng)前 watchExpression 返回值和上次的 watchExpression 返回值不一致時(shí)被調(diào)用(使用 !== 來(lái)嚴(yán)格地判斷不一致性,而不是使用 == 來(lái)判斷,不過(guò) objectEquality == true 除外)
objectEquality 為 boolean 值,當(dāng)為 true 時(shí),將使用 angular.equals 來(lái)判斷一致性,并使用 angular.copy 來(lái)保存此次的 Object 拷貝副本供給下一次的比較,這意味著復(fù)雜的對(duì)象檢測(cè)將會(huì)有性能和內(nèi)存上的問(wèn)題
七、$apply([exp])
$apply 是 $scope 的一個(gè)函數(shù),用于觸發(fā) $digest 循環(huán)
$apply 偽代碼
function $apply(expr) {
try {
return $eval(expr);
} catch (e) {
$exceptionHandler(e);
} finally {
$root.$digest();
}
}
使用 $eval(expr) 執(zhí)行 expr 表達(dá)式
如果在執(zhí)行過(guò)程中跑出 exception,那么執(zhí)行 $exceptionHandler(e)
最后無(wú)論結(jié)果,都會(huì)執(zhí)行一次 $digest 循環(huán)
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流。
- 學(xué)習(xí)Angular中作用域需要注意的坑
- Angular.js與Bootstrap相結(jié)合實(shí)現(xiàn)表格分頁(yè)代碼
- 淺談angular.js中實(shí)現(xiàn)雙向綁定的方法$watch $digest $apply
- Angular.js如何從PHP讀取后臺(tái)數(shù)據(jù)
- 總結(jié)十個(gè)Angular.js由淺入深的面試問(wèn)題
- Angular.js回顧ng-app和ng-model使用技巧
- Angular.js與Bootstrap相結(jié)合實(shí)現(xiàn)手風(fēng)琴菜單代碼
- Angular.js 實(shí)現(xiàn)數(shù)字轉(zhuǎn)換漢字實(shí)例代碼
- angular.js之路由的選擇方法
- angular.js分頁(yè)代碼的實(shí)例
相關(guān)文章
AngularJS動(dòng)態(tài)加載模塊和依賴的方法分析
這篇文章主要介紹了AngularJS動(dòng)態(tài)加載模塊和依賴的方法,結(jié)合實(shí)例形式分析了AngularJS使用ocLazyLoad實(shí)現(xiàn)動(dòng)態(tài)加載的相關(guān)操作技巧,需要的朋友可以參考下2016-11-11
基于angular2 的 http服務(wù)封裝的實(shí)例代碼
這篇文章主要介紹了基于angular2 的 http服務(wù)封裝實(shí)例代碼,2017-06-06
AngularJS實(shí)現(xiàn)與后臺(tái)服務(wù)器進(jìn)行交互的示例講解
今天小編就為大家分享一篇AngularJS實(shí)現(xiàn)與后臺(tái)服務(wù)器進(jìn)行交互的示例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
angular6根據(jù)environments配置文件更改開(kāi)發(fā)所需要的環(huán)境的方法
這篇文章主要介紹了angular6根據(jù)environments配置文件更改開(kāi)發(fā)所需要的環(huán)境的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03
教你用AngularJS框架一行JS代碼實(shí)現(xiàn)控件驗(yàn)證效果
簡(jiǎn)單來(lái)說(shuō)Angular.js是google開(kāi)發(fā)者設(shè)計(jì)和開(kāi)發(fā)的一套前端開(kāi)發(fā)框架,幫助你簡(jiǎn)化前端開(kāi)發(fā)的負(fù)擔(dān)。到底能簡(jiǎn)化到什么程度呢,今天我們來(lái)看下,一行代碼實(shí)現(xiàn)控件驗(yàn)證效果,有木有嚇尿?2014-06-06

