parser

##双向数据绑定 ##依赖注入

AngularJS启动过程分析

  • 概述:

1.publishExternalAPI(angular):给angular全局对象加上公共工具函数(调用外部api)
2.setupModuleLoader(window):建立模块机制
3.注册内核provider(最重要的provider(服务):$parse(解析服务)与$rootScope(根服务,属性结构))
4.angularInit:防止多次初始化ng-app
5.bootstrap(angularJS自动的启动方法):创建injector,拉起内核和启动模块,调用compile服务

6.概述图:

->用自执行函数形式让代码加载完立即执行(在window上暴露唯一的全局对象angular)
->检查是不是多次导入window.angular.bootstrap(通过检查指定元素是否存在injector进行判断(监测过程和实例化))
->尝试绑定jQuery(bindJquery())(如果有导入jQuery,则使用,否则使用Angular自己封装的JQLite)

jq判断方法:

var hasImportJQuery = jQuery && jQuery.fn.on ? true : false;

->publishExternalAPI(发布ng提供的api):

1.工具函数拷贝angular全局对象
2.调用setupModuleLoader创建模块定义和加载工具(挂在window.angular上) 3.构建内置模块ng
4.创建ng内置的director和provider;
5.两个重要的provider: $parse和$rootScope;

->查找ng-app(angularInit(document, bootstrap));->找到bootstrap(element, modules, config)开始启动;->

1.此处bootstrap是ng的启动函数;
2.调用此函数则手动启动app,页面上不可出现ng-app
3.此函数会创建并返回一个注射器(injector),此注射器就是根注射器

createInjector(modulesToLoad,strictDi)->

1.注射器有两种:providerInjector,instanceInjector

调用compile服务查找并编译指令

(function(doc, win){
	if(window.angular.bootstrap) return;
	bindJQuery();
	publicExternalAPI(angular);
	jqLite(document).ready(function(){
		angularInit(document, bootstrap);
	});
})(document, window)
  • 方式:

1.自动启动:设置ng-app
2.手动启动

var myModule = angular.module('myModule', []);
myModule.controller('myCtrl', ['$scope', function($scope){
	$scope.name = 'handleStart';
}]);
angular.element.ready(function(){
	angular.bootstrap(document, ['myModule']);
});

3.多个ng-app:第二个、三个更多需要手动启动,angular会自动启动第一个ng-app

ps:如果多次启动(自动启动后再手动启动),会报错

  • 绑定jquery:
  • 全局对象angular(injector方法):

依赖注入原理分析:Provider与Injector

  • 为什么需要依赖注入
  • 复习依赖注入(实际上是内联注入)
  • ng三种注入方式

1.推断注入:函数参数的名称必须和被注入的对象相同

var myModule = angular.module('myModule', []);
myModule.controller('myCtrl', function($scope){
	$scope.name = 'handleStart';
});

2.标注式注入:($injector.annotate(function(arg0,arg1){}))->分析函数参数

var myModule = angular.module('myModule', []);
myModule.factory('game', function(){
	return {
		name: 'handleStart'
	}
});
myModule.controller('myCtrl', ['$scope', '$injector', function($scope, $injector){
	$injector.$invoke(function(game){
		$scope.name = game.name;
	});
}]);

3.内联注入:

var myModule = angular.module('myModule', []);
myModule.controller('myCtrl', ['$scope', function($scope){
	$scope.name = 'handleStart';
}]);
  • 直接使用$injector(很少使用)
  • provider模式与ng实现

    1.provider是策略模式和工厂模式综合体
    2.核心是为了让接口和实现分离
    在ng中,所有provider都可以用来注入
    provider/factory/service/constant/value
    3.以下类型函数都可以接收注入: controller/directive/filter/service/factory等
    4.ng中的”依赖注入”是通过provider和injector这两个机制联合实现的

  • provider/factory/service/constant/value/decorator

    1.provider是基础,其余都是基于provider实现
    2.从左向右,灵活性越来越差

    1.provider实现方式:

    var myModule = angular.module('myModule', []);
    // 通过provider定义的,都是可以注入的
    myModule.provider('helloAngular', function(){
      return {
          $get: function(){
              var name = '慕课',
                  getName = function(){
                      return name;
                  };
              return {
                  getName: getName
              }
          }
      }
    });
    myModule.controller('myCtrl', ['$scope', 'helloAngular', function($scope, helloAngular){
      $scope.name = helloAngular.getName();
    }]);
    

    2.factory实现方式:

    var myModule = angular.module('myModule', []);
    // 通过provider定义的,都是可以注入的
    myModule.factory('helloAngular', function(){
      var name = '慕课',
          getName = function(){
              return name;
          };
      return {
          getName: getName
      }
    });
    myModule.controller('myCtrl', ['$scope', 'helloAngular', function($scope, helloAngular){
      $scope.name = helloAngular.getName();
    }]);
    

    3.service实现方式:

    var myModule = angular.module('myModule', []);
    // 通过provider定义的,都是可以注入的
    myModule.service('helloAngular', function(){
      this.name = '慕课';
      this.getName = function(){
          return this.name;
      };
    });
    myModule.controller('myCtrl', ['$scope', 'helloAngular', function($scope, helloAngular){
      $scope.name = helloAngular.getName();
    }]);
    
  • 内置provider($ControllerProvider L7357)
  • injector源码分析(创建、注册、调用)

指令的执行过程分析

  • 自定义compile和link函数

1.compile函数:(若再定义link函数,则link函数无效)

```` var myModule = angular.module(‘myModule’, []); myModule.directive(‘autoHello’, function(){ return { restrict: ‘E’, compile: function(el, attrs, transclude){ // 从这里开始对标签元素自身进行一些变换 var tpl = el.children().clone(); for(var i = 0;i < attrs.autoHello-1;i++){ el.append(tpl.clone()); } // 实际上是link函数 return function(scope, el, attrs, controller){ console.log(‘指令链接’); } } } });


>2.link函数:
>

var myModule = angular.module(‘myModule’, []); myModule.directive(‘hello’, function(){ return { restrict: ‘E’, replace: true, template: ‘<div ng-bind="welcomeWord"></div>’, link: function(scope, el, attrs, controller){ scope.welcomeWord = ‘Hello, angularJS’; } } });


- compile和link区别
>1.compile函数的作用是对指令模板进行转换<br/>
>2.link的作用是在模型和视图间建立联系,包括在元素上注册监听事件<br/>
>3.scope在链接阶段才会被绑定到元素上,因此compile操作scope会报错<br/>
>4.对于同一个指令的多个示例,compile会执行一次,link函数会对指令的每个实例都会执行一次<br/>
>5.一般情况下,只需要编写link函数就行了<br/>
>6.若编写自定义compile函数,则link函数无效,因为自定义compile函数应该返回link函数后续处理

- 从最简单的<hello>分析指令源代码(compile)
>1.从ng-app开始,递归子层dom结构,收集指令<br/>
>2.如果有需要,为指令生成childScope,childScope绑定到元素的data属性上<br/>
>3.调用每个指令为自己的compile函数生成compositeLinkFn函数<br/>
>4.编译结果是返回一个publicLinkFn函数<br/>
>5.编译完成之后,立即调用生成的publicLinkFn函数<br/>
>>ps:外部逻辑用module的directive方法注册指令
>

var myModule = angular.module(‘myModule’, []); myModule.directive(‘hello’, function(){ return { restrict: ‘EA’, replace: true, template: ‘<div>hello</div>’, link: function(scope, el, attrs, controller){ // link } } });


### $scope与双向绑定解析
- 数据双向绑定实例

>Scope

var myModule = angular.module(‘myModule’, []); myModule.controller(‘myCtrl’, [‘$scope’, function($scop){ $scope.hello = ‘hello’; });

````

  • ng是如何发现数据发生变化的
  • 关于scope
  • 被绑定对象的结构

1.由于ng的$digest机制和”对象比较”机制,ng在处理Tree型结构方面性能非常差
2.建议不对Tree型结构使用双向数据绑定

  • 绑定过程中使用表达式
  • ng支持哪些形式的表达式

1.数学运算:+,-,/,*,%
2.比较运算:==,!=,>,<,>=,<=
3.布尔运算:&&,||,!
4.位运算:^,&,|
5.对象和数组字面值:[],{}
ps:不支持if/for/while等逻辑控制;Parse:专门解析内联表达式

  • 自己动手实现数据双向绑定

1.如何把一个Model绑定到多个View?(观察者模式)
2.如何才能知道Model发生了变化?(脏值检测$watch与$digest)
3.如果Model是深层次的嵌套结构,如何知道某个值是不是变了?(对象深比较)
4.深层次问题:A和B两个方法相互watch的时候,如何避免发生“震荡”?(TTL机制)
5.绑定过程中如何支持表达式?($parser与$eval自制js版编译器…) 参考链接