服務(wù)
1.Constant
2.Value
3.Service
4.Factory
5.Provider
除了Constant,所有類型服務(wù)背后都通過Provider實(shí)現(xiàn)
angular.module('lulu.app').provider('greeting', function(){
var _name = '66';
this.setName = function(name){
_name = name;
};
this.$get = function(){
return _name;
}
});
angular.module('lulu.app').controller('SomeCtrl', function($scope, greeting){
$scope.message = greeting; //值為66
});
//provider可配置
angular.module('lulu.app').config(function(greetingProvider){
greetingProvider.setName('lulu');
});
//value
angular.module('lulu.app').value('greeting', '66');
//service
angular.module('lulu.app').service('greeting', function(){
this.say = function(name){
return name;
}
});
//等價(jià)于
angular.module('lulu.app').provider('greeting', function(){
this.$get = function(){
var Greeting = function(){
this.say = function(name){
return name;
};
};
//service用new的方式創(chuàng)建
return new Greeting();
};
});
//factory
angular.module('lulu.app').factory('greeting', function(){
return 66;
});
//等價(jià)于
angular.module("lulu.app").provider('greeting', function(){
this.$get = function(){
var greeting = function(){
return 66;
}
return greeting();
}
});
***service和factory區(qū)別在于內(nèi)部創(chuàng)建時(shí)一個(gè)是new一個(gè)直接返回
//constant
angular.module('lulu.app').constant('greeting', '66');
Constant時(shí)機(jī)非常早讼昆,可以在Config中使用
MVVM
View:專注顯示忿危,視圖模板
ViewModel:負(fù)責(zé)給View提供顯示數(shù)據(jù)久信,以及供View操作Model途徑冷冗,$scope對象充當(dāng)了這個(gè)角色
Model:領(lǐng)域?qū)ο笾ㄎ眩瑯I(yè)務(wù)相關(guān)數(shù)據(jù)
Controller:負(fù)責(zé)ViewModel對象初始化
Angular啟動(dòng)過程
1.瀏覽器下載HTML/CSS/Javascript
2.瀏覽器開始構(gòu)建DOM
3.Jquery初始化
4.Angular初始化:創(chuàng)建各種模塊若厚,在模塊中注冊各種Angular對象
5.Jquery啟動(dòng)
6.AngularI啟動(dòng)拦英,查找第一個(gè)帶有ng-app的節(jié)點(diǎn)
7.加載子模塊,關(guān)聯(lián)DOM和模塊测秸,使DOM變活(展示數(shù)據(jù)疤估,響應(yīng)事件)
8.啟動(dòng)子模塊灾常,執(zhí)行run回調(diào)
9.渲染頁面
10.數(shù)據(jù)綁定與digest循環(huán)
依賴注入DI
只要指出我需要哪些對象,讓后就會(huì)有人(框架)把這個(gè)對象給我
Javascript中實(shí)現(xiàn)DI
函數(shù)對象的toString()铃拇,返回函數(shù)源碼钞瀑,解析源碼參數(shù)
Angular中的DI
所有主要編程元素都要通過某種方式注冊
注冊表Module,Angular跨Controller共享數(shù)據(jù)或通訊慷荔,可以創(chuàng)建Service/Value/Constant分別注入仔戈,共享同一對象
provider通過$get函數(shù)注入,provider('test', function(/只能注入constant, provider/))
循環(huán)依賴不能使用依賴注入拧廊,可以在代碼內(nèi)部調(diào)用
var http = $injector.get('$http');
Digest
Angular將雙向綁定轉(zhuǎn)換成一堆watch监徘,遞歸檢查watch表達(dá)式結(jié)果是否改變,等到Model值不再變化吧碾,不會(huì)觸發(fā)watcher函數(shù)凰盔,一個(gè)完整的digest循環(huán)結(jié)束
Angular拓展了瀏覽器事件模型,建立了自己上下文倦春,ngClick, ngChange會(huì)將瀏覽器事件轉(zhuǎn)化為$scope的響應(yīng)函數(shù)户敬,響應(yīng)函數(shù)中改變Model,觸發(fā)臟檢查機(jī)制睁本,并不存在定時(shí)的臟檢查
遍歷一遍所有watcher函數(shù)稱為一輪臟檢查尿庐,執(zhí)行完一輪,如果任何一個(gè)watcher監(jiān)聽值改變呢堰,再進(jìn)行一輪臟檢查抄瑟,直到所有watchers函數(shù)都報(bào)告值不變了,$digest循環(huán)結(jié)束枉疼,才能把變化更新到DOM
何時(shí)進(jìn)入臟檢查皮假?
每一個(gè)進(jìn)入Angular上下文環(huán)境的事件,都會(huì)執(zhí)行一次$digest
$watch函數(shù)返回一個(gè)反注冊函數(shù)
$scope.$apply(function(){}); //手動(dòng)觸發(fā)digest循環(huán)
$rootScopt是所有$scope基礎(chǔ)
指令生命周期
Inject, Compile, Controller加載骂维, pre-link, post-link
angular.module('lulu.app').directive('test', function(){
console.log('Inject'); //只發(fā)生一次
return {
restrict:'EA',
transclude:true,
replace:true,
template:'<div>{{count}}</div>',
scope:{
count:'='
},
//每個(gè)指令實(shí)例化時(shí)執(zhí)行一次惹资,傳入elm還未被link,無法訪問$scope
compile:function(elm, iAttrs){
console.log('compile' + iAttrs.count);
//controll初始化$scope后航闺,進(jìn)入正式解析過程褪测,對每個(gè)實(shí)例只執(zhí)行一次
return {
//從父節(jié)點(diǎn)到子節(jié)點(diǎn)觸發(fā),子節(jié)點(diǎn)DOM不穩(wěn)定潦刃,不適合在其上加DOM監(jiān)聽
pre:function(scope, elm, iAttrs){
console.log('pre-link' + iAttrs.count + ' scope' + scope.count);
},
//從子節(jié)點(diǎn)到父節(jié)點(diǎn)觸發(fā)
post:function(scope, elm, iAttrs){
console.log('post-link' + iAttrs.count + ' scope' + scope.count);
}
};
},
//初始化$scope
controller:function($scope){
console.log('controller');
}
};
});
$observe監(jiān)聽DOM中屬性值變化
$watch監(jiān)聽scope屬性變化
指令scope綁定策略
@綁定{{}}侮措,單向綁定,返回String
=綁定一個(gè)對象福铅,雙向綁定萝毛,返回Object
&綁定一個(gè)函數(shù)
指令scope作用域
false:直接使用父級scope
true:繼承父級scope,創(chuàng)建后copy父scope,與父級無關(guān)
{}:創(chuàng)建一個(gè)新的隔離scope
controller as vm
好處:
1.$scope注入不再是必須的(除非用到$watch, $emit, $on等)
2.避免this指針坑
3.避免原型鏈繼承對于值類型的坑(視圖模板所有字段都限制于vm別名應(yīng)用的屬性,會(huì)導(dǎo)致整個(gè)頁面刷新)
<div>{{vm.name}}</div>
(function(){
angular.module('lulu.app')
.controller('HomeCtrl', HomeCtrl);
function HomeCtrl(){
var vm = this;
vm.name = 66;
};
});
性能
1.移除不必要的$watch
2.::語法滑黔,實(shí)現(xiàn)one-time綁定
3.滾屏加載數(shù)據(jù)笆包,分部加載數(shù)據(jù)
4.$compileProvider.debugInfoEnabled(true);
5.慎用filter,$digest中略荡,filter至少執(zhí)行2次庵佣,應(yīng)避免filter執(zhí)行耗時(shí)操作,或在controller中預(yù)先處理完數(shù)據(jù)在視圖中直接綁定
6.ng-repeat添加track by可以避免$scope.tasks = data;移除所有DOM后重新渲染汛兜,track by可以對應(yīng)原DOM進(jìn)行更新
angular不會(huì)大范圍更新DOM巴粪,每次更新區(qū)域小,超過2000個(gè)watcher需要好好考慮優(yōu)化
攔截器(AOP機(jī)制)
實(shí)現(xiàn)Ajax請求攔截切入
防閃爍
{{}}替換為ng-bind
ngCloak通過樣式切換實(shí)現(xiàn)隱藏顯示
父子$scope嵌套
默認(rèn)使用原型鏈繼承來至父級的$scope
<div ng-controller='ParentCtrl'>
{{greeting}}
<input type="text" ng-model="greet"/>
<div ng-controller='ChildCtrl'>
{{greeting}}
<input type="text" ng-model="greet"/>
</div>
</div>
//ParentCtrl
$scope.greeting = "66";
導(dǎo)致的問題:
1.開始全部顯示66
2.改變父input值粥谬,全部同步數(shù)據(jù)
3.改變子input值肛根,子組件同步數(shù)據(jù),父組件不能同步漏策,再改變父input派哲,子組件也不會(huì)同步數(shù)據(jù)
原因:
子組件開始自己沒有g(shù)reeting,會(huì)向上查找使用父的greeting屬性
當(dāng)子組件input值變更,子組件創(chuàng)建自己的greeting屬性掺喻,后就與父級greeting屬性隔離
使用controller as vm語法綁定vm.greeting可避免
路由
(function(){
angular.module('lulu.routes')
.config(routesConfig);
function routesConfig($stateProvide, $urlRouterProvider){
$stateProvide
.state('book', {
cache:true,
url:'/book',
templateUrl:'templates/product/book.html',
controller: 'BookCtrl',
controllerAs: 'vm',
resolve:{
//返回一個(gè)promise芭届,如promise狀態(tài)reject,會(huì)擋住路由
//如不是promise, 對象會(huì)被注入到controller
loggedIn:function(mkAuth){
return mkAuth.checkLoggedIn();
}
}
})
};
});
ng-show/ng-hide/ng-if
ng-show/ng-hide通過css的display實(shí)現(xiàn)隱藏顯示感耙,ng-if通過移除添加DOM實(shí)現(xiàn)
$rootScope和$scope
$rootScope是頁面所有$scope父級
1.angular解析ng-app創(chuàng)建$rootScope
2.解析{{}}為變量
3.解析ng-controller創(chuàng)建$scope
{{}}原理
使用$interpolation服務(wù)查看文本節(jié)點(diǎn)是否有{{}}(插補(bǔ)標(biāo)記)褂乍,有則注冊watches,成為digest檢查的一部分
$timeout.cancel();
ng-repeat迭代
track by $index解決綁定數(shù)據(jù)相同(唯一表示數(shù)據(jù)和DOM關(guān)系)
頁面{{}}即硼,ng-click中可以用js原生方法么逃片?
不能,因?yàn)?scope上下文不存在那些原生方法只酥,如用as vm模式可以吧?
SPA缺點(diǎn)
1.SEO题诵,可通過Prerender解決部分
2.前進(jìn),后退等要程序管理
SPA SEO解決方案:
1.接入prerender.io等預(yù)渲染服務(wù)
2.騰出一臺(tái)服務(wù)器搭建phantomjs搞預(yù)渲染
3.將爬蟲請求引導(dǎo)到后臺(tái)定時(shí)生成或抓取的靜態(tài)HTML頁
4.SSR
項(xiàng)目
微信登錄授權(quán)
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<a >微信登錄</a>
function freshConfig() {
var url = $location.absUrl();
url = url.split('#')[0];
//傳入當(dāng)前url
weixin.jssdkConfig({url: url}).then(function (data) {
if (data.code == 0) {
//配置wx
wx.config({
debug: false, // 開啟調(diào)試模式,調(diào)用的所有api的返回值會(huì)在客戶端alert出來层皱,若要查看傳入的參數(shù)性锭,可以在pc端打開,參數(shù)信息會(huì)通過log打出叫胖,僅在pc端時(shí)才會(huì)打印草冈。
appId: mkGlobalVal.weixin_appid, // 必填,公眾號的唯一標(biāo)識
timestamp: parseInt(data.result.timestamp), // 必填瓮增,生成簽名的時(shí)間戳
nonceStr: data.result.noncestr, // 必填怎棱,生成簽名的隨機(jī)串
signature: data.result.signature,// 必填,簽名绷跑,見附錄1
jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone'] // 必填拳恋,需要使用的JS接口列表,所有JS接口列表見附錄2
});
}
});
};
function onShare(share) {
//分享到朋友圈
wx.onMenuShareTimeline(share);
//分享給朋友
wx.onMenuShareAppMessage(share);
//分享到QQ
wx.onMenuShareQQ(share);
//分享到騰訊微博
wx.onMenuShareWeibo(share);
//分享到QQ空間
wx.onMenuShareQZone(share);
}
//微信支付
if ($scope.mkTools.agentIsWeichat) {
orderInfo = {
"openid": $scope.currentUser.authData.weixin.openid,
"no": data.no,
"total_fee": data.total_fee,
"body": data.product_snapshot.title
};
mkPayTools.weixinPay(orderInfo).then(angular.noop, error);
}
//創(chuàng)建訂單成功后砸捏,呼出微信支付界面
function weixinBridge(data, defer) {
function onBridgeReady() {
WeixinJSBridge.invoke('getBrandWCPayRequest', data, function (res) {
//支付成功
if (res.err_msg == "get_brand_wcpay_request:ok") {
defer.resolve();
window.location.href = "/#/payresult";
}
else {
defer.reject();
}
});
};
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}
else {
onBridgeReady();
}
};
return defer.promise;
};