您现在的位置:首页 >> 前端 >> 内容

Angular从使用到源码学习(一)

时间:2016/9/26 10:03:29 点击:

  核心提示:搭建环境项目github地址项目中涉及了json-server模拟get请求,用了angular-route;AngularJS的一些出众构建一个CRUD应用可能用到的全部内容包括:数据绑定、基本模板...

搭建环境

项目github地址

项目中涉及了json-server模拟get请求,用了angular-route;

AngularJS的一些出众

构建一个CRUD应用可能用到的全部内容包括:数据绑定、基本模板标识符、表单验证、路由、深度链接、组件重用、依赖注入。

测试方面包括:单元测试、端对端测试、模拟和自动化测试框架。

具有目录布局和测试脚本的种子应用作为起点。

简单使用

指令:

扩展 HTML,为应用添加功能。 AngularJS 允许你自定义指令(实现组件化)。

项目中使用angular时,在模板上,使用一系列基本的指令指名不同的作用,如下:

//ng-app指明这是个Angular应用

var myApp = angular.module('myApp', ['ngRoute']); //在初始化时会初始化Angualr相关的地方;

ng-app .指令初始化一个 AngularJS 应用程序。

ng-init.指令初始化应用程序数据。

ng-model.指令把元素值(比如输入域的值)绑定到应用程序。

作用域:

如果把Angular理解成一个MVVM框架,那么Scope充当的就是ViewModel;它是View和Model的粘合体,负责View和Model的交互和协作,它负责给View提供显示的数据,以及提供了View中Command事件操作Model的途径。

Angular中的scope很重要,它是连接模板到模型的一个很重要纽带,它作用域在一个controller范围内,在这个范围内的模板和模型都是通过这个scope联系。

由于它,实现了数据的双向绑定,后面讲解的渲染机制会突出scope在渲染中的作用。

控制器:

如果把Angular理解成一个MVVM框架,它负责ViewModel对象的初始化,它将组合一个或者多个service来获取业务领域Model放在ViewModel对象上,使得应用界面在启动加载的时候达到一种可用的状态。但并不是MVVM模式的核心元素。

如下是scope和controller的使用:

1.controller中的scope

```

myApp.controller("messageTypeController",function($scope,$http){

$scope.title = "D";

$scope.msgType=[{id:1,name:"push",option:'xxx'}];

$scope.text="'Hello World!'";

});

对于model的数据可以从网络中获取,然后赋值个scope这个中间人,最后会渲染到界面上。

依赖注入:

这虽不是angular提出的一个新的概念,但angular是用这用方式管理各个对象确实吸引很大,方便设计低耦合高复用的对象。

provider(创建可提供的依赖)

AngularJS 通过$provider对象的provider创建一个可注入的东西,还有其他接口如,value,factory,service,Constant但这四个内部实现都是通过provider创建的,他们是provider的简写。

value

用来将值传递过程中的配置相位控制器 factory

工厂是用于返回函数的值。它根据需求创造值,每当一个服务或控制器需要。它通常使用一个工厂函数来计算并返回对应值。 service

服务是一个单一的JavaScript包含了一组函数对象来执行某些任务。服务使用service()函数,然后注入到控制器的定义。 constant

常量用于通过配置相位值考虑事实,值不能使用期间的配置阶段被传递

注入器( injector)注入器负责从我们通过provide创建的服务中创建注入的实例,每一个AngularJS应用都有唯一一个injector,当应用启动的时候它被创造出来,你可以通过将injector注入到任何可注入函数中来得到它($injector知道如何注入它自己!)。

一旦你拥有了$injector,你可以动过调用get函数来获得任何一个已经被定义过的服务的实例

//../utils/utils.js

module.exports = function() {

var factory = {};

factory.reComputeResult = function(value,a, b) {

switch(value){

case "option1":

return a+b;

break;

case "option2":

return a-b;

break;

case "option3":

return a*b;

break;

case "option4":

return a/b;

break;

}

}

return factory;

}

var utils=require('../utils/utils.js');

myApp.value("defaultInput1", 1);

myApp.value("defaultInput2", 2);

myApp.constant("_constant","can't modify");

myApp.config(function($provide,_constant) {

$provide.provider('MathService', function() {

this.$get = function() {

var factory = {};

factory.reComputeResult = function(value,a, b) {

switch(value){

case "option1":

return a+b;

break;

case "option2":

return a-b;

break;

case "option3":

return a*b;

break;

case "option4":

return a/b;

break;

}

}

return factory;

};

});

});

myApp.factory('MathService', utils); //这里的MathService和上面创建的是一样的效果,实际中只保留一个。

myApp.service('CalcService', function($injector){

var MathService=$injector.get('MathService'); //使用$injector的方式注入

this.reComputeResult = function(value,a,b) {

return MathService.reComputeResult(value,a,b);

}

});

myApp.controller('strategyController', function($scope, CalcService, defaultInput1,defaultInput2,_constant) {//直接在controler中注入

$scope.title = "B";

$scope.redioValue="option1";

$scope.firstNum = defaultInput1;

$scope.secondNum = defaultInput2;

console.log(_constant);

$scope.computeResult = CalcService.reComputeResult($scope.redioValue,$scope.firstNum,$scope.secondNum);

$scope.reComputeResult = function() {

$scope.computeResult = CalcService.reComputeResult($scope.redioValue,$scope.firstNum,$scope.secondNum);

}

});

scope的嵌套关系

子作用域一般都会通过javascript原型继承机制继承其父作用域的属性和方法:

子作用域与父作用域中相同的值会覆盖父作用域中的值,但修改子作用域中的值不会修改父作用域中的值; 对于通过drirective创建的组件的作用域可以根据scope的true配置成原型继承,false配置为独立的作用域。

简单的作用域间的通信

子作用域利用原型继承可以直接获取父作用域上的对象和方法,但是不能修改;若要修改可以调用父作用域中的方法修改; 父作用域修改子作用域中的值通过$broadcast广播的形式传递。

渲染方式

AngularJS修改了一般的javascript工作流,并且提供了它自己的事件处理机制:把javascript的context分隔成两部分,一部分是原生的javascript的context,另一部分是AngularJS的context.

一般的工作流

浏览器UI线程基于一个简单的队列系统,任务hi被添加到队列中直到UI线程空闲,一旦空闲,队列中的下一个任务会被提取出来并运行。这些任务要么是运行javascript,要么是执行UI更新,包括重绘和重排。

AngularJS的工作流

首先将任务加入UI队列。 如果队列中的任务被$apply()包起来的话,就会进入到AngularJS的执行上下文(AngularJS执行的线程)中。 AngularJS的执行上下文执行这个任务。 然后AngularJS会进入到由两个小循环组成的digest循环中,一个循环是用来处理evalAsync队列,一个循环是处理watch列表。digest循环会一直迭代直到evalAsync队列为空并且watch列表也为空。 一旦AngularJS的$digest循环结束,整个执行就会离开AngularJS执行上下文,回到一般的工作流,将相应的UI的更新任务加入队列等待UI线程执行,最后更新DOM。

数据脏检测,如何实现双向效果

编译的时候,遍历DOM,找出与这部分DOM相关的指令,这些指令有一个compile方法,它表明如何去修改DOM的结构,每个指令最后都返回一个链接函数。这部分DOM的有一个Scope,这个Scope与这些指令的连接函数集合链接。这样就形成了Scope的DOM的动态绑定。任何一个Scope的改变都会在DOM上体现出来。

每次由视图触发的事件都会在angular执行上下文中循环检测watch列表的变化,watch队列里的每个$watch监听着一个视图上的model的变化,若model发生变化也就是这个Scope发生了改变,从而导致相应的视图重新渲染。

大概就是:

model使scope发生改变,直接根据指令解析时与scope的链接关系更新相应的dom; view使scope发生改变,angularjs都会进入一个循环,里面有两个小循环,其中的一个$watch他会检测由于任务的执行改变的那些Scope,将改变的表示为脏,对于这些脏的scope会在返回一般工作流中一次性更新相应的dom,如何判断更新那部分,则是根据scope与指令返回的链接函数可以得到。

angular是如何确定值为脏的呢?

一般的脏值检测:使用“===”; 基于值得脏值检测:深度拷贝; 集合监控:浅度拷贝;

angular的$apply作用及应用

apply可以触发digest循环; 内部的angular指令比如ng-click事件中的函数都被apply包裹,触发事件既可以触发digest循环; 对于直接使用javascript的事件,若要触发一次渲染则需要手动对事件用$apply包裹;

Delayed Message: {{message}}

angular.module('myApp',[]).controller('MessageController', function($scope) {

$scope.getMessage = function() {

setTimeout(function() {

$scope.$apply(function() {

//wrapped this within $apply

$scope.message = 'Fetched after 3 seconds';

console.log('message:' + $scope.message);

});

}, 2000);

}

$scope.getMessage();

});

Tags:AN NG GU UL 
作者:网络 来源:mzzzzq的博客