AngularJS資料雙向繫結實現原理
- 每一個雙向繫結的元素都有一個watcher
- 在某些事件發生時,呼叫digest髒資料檢測。事件有:表單內容變化,Ajax請求響應,點選按鈕執行函式等
- 髒資料檢測會檢測rootscope下所有被watcher的元素
- $digest函式觸發髒資料監測
Angular生命週期
應用啟動後,進行編譯和連結,作用域同HTML繫結。瀏覽器載入html,渲染dom樹----廣播一個事件----ng監聽dom完成事件,查詢ng-app元素----ng以當前dom為起點開始遞迴查詢所有子元素,符合應用程式定義好的指令規則。
編譯階段: angularjs遍歷整個html文件,並根據指令定義來處理頁面上宣告的指令。指令也可能在套用其它指令。有機會在指令的模板函式返回前,對編譯後的DOM樹進行修改。
連結階段:連結函式將模板和作用域連結起來;設定事件監聽,監視資料變化等等。如果定義了編譯函式,會返回連結函式,如果都定義了,編譯函式會過載連結函式。
angular.module('moduleName',[])
.directive('name',['$http',function($http){
return {
restrict: 'A',
scope:{},
template: '<div ng-transclude>當前內容會被替換</div>'
transclude: true,
compile: function(element,attrs,transcludeFn){
//進行編譯後的操作
return {
pre: function(scope,element,attrs,controller){
//子元素被連結之前
},
post: function(scope,element,attrs,controller){
//子元素被連結之後
}
}
},
link: function(scope,element){
//定義了compile函式,不會執行link函式
},
controller: function($scope,$transclude){
//controller和link都定義了,controller會先執行,在執行link函式
//指令的控制器和link函式可以進行互換。控制器主要是用來提供可在指令間複用的行為,但連結函式只能在當前內部指令中定義行為,且無法在指令間複用.link函式可以將指令互相隔離開來,而controller則定義可複用的行為。
}
}
}])
複製程式碼
程式啟動
//手動啟動
var module = angular.module("myApp",[]);
module.controller("MyCtrl",['$scope',function($scope){
//code
}])
angular.element(document).ready(function(){
angualr.bootstrap(document,['module'])
})
//自動啟動,找到ng-app標籤啟動
var module = angular.module('myApp',[]);
<body ng-app="myApp">
複製程式碼
依賴注入
- 原理:假設函式的引數名就是依賴服務的名字,在依賴對映中,去查詢具體的依賴項服務
//1.陣列注入(注意依賴項的順序)
myApp.controller('myCtrl',['$scope','$http',function($scope,$http){
}])
//2.顯示$inject
myApp.controller('myCtrl',myCtrl);
function myCtrl($scope,$http){
}
myCtrl.$inject = ['$scope','$http']
複製程式碼
controller之間通訊
- event事件傳播方式
//父傳子
$rootScope.$broadcast(name,params)
//子傳父
$scope.$emit(eventname,params)
$scope.$on(name,function(event,data){})
複製程式碼
- 建立service
- 建立專用的事件Service,按照業務邏輯切分,資料儲存在Service中
- $rootScope方式
- 本地儲存localstore等等
-
ng-repeat迭代時遇見相同值,track by $index
-
通過使用$ sce.trustAsHtml(),該方法將值轉換為特權所接受並能安全地使用“ng-bind-html”
ng-if/ng-show/hide區分
- ng-if生成新的作用域
- ng-if控制dom的增刪來控制節點,ng-show初始化時就建立了,用display:block/none來控制
產生的一個小問題:在 ng-if 中用基本變數繫結 ng-model,並在外層 div 中把此 model 繫結給另一個顯示區域,內層改變時,外層不會同步改變,因為此時已經是兩個變數了。避免這類問題出現的辦法是,始終將頁面中的元素繫結到物件的屬性(data.x)而不是直接繫結到基本變數(x)上。
<p>{{name}}</p>
<div ng-if="true">
<input type="text" ng-model="name">
</div>
複製程式碼
效能優化問題
- 減少監控項(不會變化的資料採用單向繫結)
- 單次繫結{{::item}}
- 主動設定索引
- 資料變更檢測與繫結的方式
- 索引的效能
- 資料的大小
- 資料的結構
建立services
services在需要的時候被建立,只在應用生命週期結束的時候才會被清除;而controllers在不需要的時候就會被銷燬
- factory()
- factory()定義的服務不能注入到config()中
angular.module('myapp.Services').factory('User',function($http){
var def = {
//code
}
return def;
})
angular.module('myApp').controller('MainCtrl',function($scope,User){
//code
})
複製程式碼
- service()
angualr.module('myApp.services').service('User',function($http){
var self = this; // Save reference
var backendUrl = "http://localhost:3000";
this.user = {};
this.setName = function(newName) {
self.user['name'] = newName;
}
this.setEmail = function(newEmail) {
self.user['email'] = newEmail;
},
this.save = function() {
return $http.post(backendUrl + '/users', {
user: this.user
});
}
})
複製程式碼
- provider()
- 唯一一個可以使用.config()方法配置建立service的方法
-使用provider()angular.module('myApp.services').provider('User',function(){ this.backendUrl = "http://localhost:3000"; this.setBackendUrl = function(newUrl) { if (url) this.backendUrl = newUrl; } this.$get = function($http) { // injectables go here var self = this; var service = { user: {}, setName: function(newName) { service.user['name'] = newName; }, setEmail: function(newEmail) { service.user['email'] = newEmail; }, save: function() { return $http.post(self.backendUrl + '/users', { user: service.user }) } }; return service; } }) 複製程式碼
angular.module('myApp').config(function(UserProvider) {
// config()裡只能注入provider,注意名稱是服務名稱+Provider
UserProvider.setBackendUrl("http://myApiBackend.com/api");
})
angular.module('myApp').controller('MainCtrl', function($scope, User) {
// controller裡面注入服務,只能呼叫provider的get方法裡定義的內容
$scope.saveUser = User.save;
});
複製程式碼
過濾器filter
- 在模板中使用
{{expression|filter1|filter2|filter3|...}}//串起來使用
{{expression|filter:argument1:argement2:...}}//接收引數
<span ng-repeat="a in array | filter">//在指令中使用
複製程式碼
- 在controller和service中使用
//方式1
app.controller('testC', function($scope, currencyFilter){
$scope.num = currencyFilter(123534);
}
//方式2
app.controller('testC', function($scope, $filter){
$scope.num = $filter('currency')(123534);
$scope.date = $filter('date')(new Date());
}
複製程式碼
指令
指令詳解
- restrict[string]
- A代表屬性、E代表元素、C代表類、M代表註釋
- template/templateUrl [string/function]
- priority[number]
- 自定義指令的優先順序,當一個DOM元素上有一個以上的指令的時候,就需要比較指令的優先順序。
- terminal[boplean]
- 是否停止當前元素上比本指令優先順序低的指令
- replace[boolean]
- link[function]
- scope-沒定義當前指令scope屬性時候,代表父controller的scope
- element-jQLite包裝的DOM元素
- attrs-包含該指令所在元素的屬性的標椎化引數物件
- scope[boolean/object]
- 預設是false,繼承父controller的scope
- true時,建立一個繼承父scope的scope
- {},建立一個隔離的scope,不會繼承父scope
- @單項繫結
- =雙向繫結
- &呼叫父scope裡的方法
- transclude[boolean]
- 規定指令是否可以包含任意內容
- compile[function]
- 寫了compile函式,link函式不會執行
- compile可以rueturn prelink和postlink
angular.module('moduleName',[])
.directive('name',['$http',function($http){
return {
restrict: 'A',
scope:{},
link: function(scope,element){
}
}
}])
複製程式碼