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){
}
}
}])