angular之中,$scope $rootScope $watch $state 是什么?


1.背景介绍

1、AngularJS Scope(作用域)

Scope(作用域)是应用在 HTML (视图)和 JavaScript (控制器)之间的纽带。Scope是一个对象,有可用的方法和属性。Scope可应用在视图和控制器上。$scope的使用贯穿整个 Angular App应用,它与数据模型相关联,同时也是表达式执行的上下文.有了 $scope就在视图和控制器之间建立了一个通道,基于作用域视图在修改数据时会立刻更新 $scope,同样的 $scope发生改变时也会立刻重新渲染视图.

2、根作用域 rootScope

所有的应用都有一个 $rootScope,它可以作用在 ng-app指令包含的所有 HTML元素中。$rootScope可作用于整个应用中。是各个 controller中 scope的桥梁。用 rootscope定义的值,可以在各个 controller中使用

2.知识剖析

      1、$scope

$scope是一个把view(一个DOM元素)连结到controller上的对象。在我们的MVC结构里,这个 $scope将成为model,它提供一个绑定到DOM元素(以及其子元素)上的excecution context。


$scope实际上就是一个JavaScript对象,controller和view都可以访问它,所以我们可以利用它在两者间传递信息。在这个 $scope对象里,我们既可以存储数据,又可以存储将要运行在view上的函数。每一个Angular应用都会有一个$rootScope。这个$rootScope 是最顶级的scope,它对应着含有ng-app 指令属性的那个DOM元素。如果页面上没有明确设定$scope ,Angular 就会把数据和函数都绑定到这里。

Angular应用启动并生成视图时,会将根 ng-app元素与 $rootScope进行绑定.$rootScope是所有 $scope的最上层对象,可以理解为一个 Angular应用中得全局作用域对象,所以不应该附加太多逻辑或者变量给$rootScope,和污染 Javascript全局作用域是一样的道理.

$scope的作用

$scope对象在 Angular中充当数据模型的作用,也就是一般 MVC框架中 Model得角色.但又不完全与通常意义上的数据模型一样,因为 $scope并不处理和操作数据,它只是建立了视图和 HTML之间的桥梁,让视图和 Controller之间可以友好的通讯。

它有如下作用和功能:

提供了观察者可以监听数据模型的变化

可以将数据模型的变化通知给整个 App

可以进行嵌套,隔离业务功能和数据

给表达式提供上下文执行环境

在 Javascript中创建一个新的执行上下文,实际就是用函数创建了一个新的本地上下文,

在 Angular中当为子 DOM元素创建新的作用域时,其实就是为子 DOM元素创建了一个新的执行上下文.

$scope的生命周期有4个阶段:

1.创建

控制器或者指令创建时, Angular会使用 $injector创建一个新的作用域,然后在控制器或指令运行时,将作用域传递进去.

2.链接

Angular启动后会将所有 $scope对象附加或者说链接到视图上,所有创建 $scope对象的函数也会被附加到视图上.

这些作用域将会注册当 Angular上下文发生变化时需要运行的函数.也就是 $watch函数, Angular通过这些函数或者何时开始事件循环.

3.更新

一旦事件循环开始运行,就会开始执行自己的脏值检测.一旦检测到变化,就会触发 $scope上指定的回调函数

4.销毁

通常来讲如果一个 $scope在视图中不再需要, Angular会自己清理它.

ng-controller指令给所在的DOM元素创建了一个新的$scope对象,并将这个$scope对象包含进外层DOM元素的$scope对象里。

在ng-app里,这个外层DOM元素的$scope对象,就是$rootScope对象。这个scope链是这样的:

所有scope都遵循原型继承(prototypal inheritance),这意味着它们都能访问父scope们。对任何属性和方法,如果AngularJS在当前scope上找不到,就会到父scope上去找,如果在父scope上也没找到,就会继续向上回溯,一直到$rootScope上。唯一的例外:有些指令属性可以选择性地创建一个独立的scope,让这个scope不继承它的父scope们。

3、$watch:

        angularjs核心之一是双向绑定,那么这个双向绑定是如何实现的呢?  当我们在创建出scope下的一个新属性的时候,ng就会主动为我们新属性注册$watch这个方法,$watch用来监听的数据变化,当数据变化之后,就立即把view和scope上数据同步。AngularJS就能够自动注册并监听变量的改变。AngularJS会首先将在{{ }}中声明的表达式编译成函数并调用$watch方法。

 $watch是一个scope函数,用于监听模型变化

 $watch(watchExpression, listener, objectEquality){ ... };

 watchExpression:$watch方法的第一个参数是一个函数,它通常被称为watch函数,它的返回值声明需要监听的变量;

 listener:第二个参数是listener,在变量发生改变的时候会被调用。和传统的事件注册和监听没有什么本质上的差别,差别仅在于AngularJS能够自动注册绝大多数的change事件并进行监听,只要按照AngularJS要求的语法来写HTML中的表达式代码,即{{ }}。 $watch方法为当前scope注册了一个watcher,这个watcher会被保存到一个scope内部维护的数组中,即是$$watchers。 watcher的主要目的是对scope上的某个属性进行监控

    objectEquality:是否深度监听,如果设置为true,它告诉Angular检查所监控的对象中每一个属性的变化.   当浏览器接收到可以被angular context处理的事件时,$digest循环就会触发。这个循环是由两个更小的循环组合起来的。    一个处理evalAsync队列(这个没有探究),另一个处理$watch队列。$digest将会遍历我们的$watch队列。如果有至少一个更新过, 这个循环就会再次触发,直到所有的$watch都没有变化。这样就能够保证每个model都已经不会再变化。 如果循环超过10次的话,它将会抛出一个异常,防止无限循环。每次当$digest循环结束时,DOM相应地变化。

    例如我们按下按钮触发ng-click事件:

  1、浏览器接收到一个事件,进入angular context。

 2、 $digest循环开始执行,查询每个$watch是否变化。

  3、 由于监视$scope.name的$watch报告了变化,它会强制再执行一次$digest循环。

  4、 新的$digest循环没有检测到变化。

   5、浏览器拿回控制权,更新与$scope.name新值相应部分的DOM。

    6、这里重要的是每一个进入angular context的事件都会执行一个$digest循环,也就是说每次我们输入一个字母循环都会检查整个页面的所有$watch。  Angular会为我们自动调用$apply!因此当点击带有ng-click的元素时,事件就会被封装到一个$apply调用。  比如有一个ng-model="foo"的输入框,然后敲一个f,事件就会这样调用$apply("foo = 'f';"),触发$digest循环。

4、$state

    $state是ui-rooter的一项服务负责表示状态以及它们之间的转换。它还提供了接口来询问当前状态

常用的方法有:

$state.go(to, params, options) :转换到新状态的方便方法

$state.includes(stateOrName, params, options) :确定当前活动状态是否等于或是状态状态子的方法。返回布尔值

$state.params: 返回状态参数的对象$stateParams

$stateParams是一个对象,包含 url中每个参数的键/值。$stateParams可以为控制器或者服务提供 url的各个部分。

    注意:$stateParams必须与一个控制器相关,并且$stateParams中的“键/值”也必须事先在那个控制器的url属性中有定义。

3.常见问题

1、 如何自定义$watch?

2、 什么时候需要我们去调用$watch?

4.解决方案

1、自定义自己的watches:

    angular.module("myApp",[]).controller('MainCtrl', function($scope) {$scope.name = "hello";$scope.updated = -1;$scope.$watch('name', function() {$scope.updated++;});});

//创造一个新的$watch的方法。第一个参数是一个字符串或者函数,在这里是只是一个字符串,就是我们要监视的变量的名字,

//第二个参数是当$watch说我监视的表达式发生变化后要执行的。当controller执行到这个$watch时,它会立即执行一次

2、 取消 $watch :

$watch会影响性能问题,特别是在移动设备上,在不需要时应该清除

$watch函数本身返回一个函数,所以,当$watch不再需要的时候,我们只需调用返回的函数即可:

.controller('MainCtrl', function($scope) {

$scope.updated = 0;

$scope.stop = function() {

textWatch();

};

var textWatch = $scope.$watch('text', function(newVal, oldVal) {

if (newVal === oldVal) { return; }

$scope.updated++;

});

});

2、 什么时候需要我们去调用$watch?

被调用的事件没有进入angular context,$digest循环永远没有执行。这种情况一般出现在指令的隔离作用域中

,也会出现在异步执行的函数体中。调用$watch需要通过$apply。

6.扩展思考


指令中的scope三个值有什么用?

   false 共享作用域

      true 创建自己的作用域,并继承父作用域

      {}创建隔离作用域

7.参考文献

参考一:  Angular.js中使用$watch监听模型变化 http://yuankeqiang.lofter.com/post/8de51_1454f93

参考二:关于$watch应用的一些小技巧 http://blog.csdn.net/u010451286/article/details/50635839

参考三: how the apply runs a digest  :http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest

参考四:深入解析AngularJS框架中$scope的作用与生命周期 http://www.jb51.net/article/80492.htm

参考五:-@ui-router——$state服务原版详解  https://www.cnblogs.com/koleyang/p/4576419.html

问题:


1、如何移除不必要的$watch?

.controller('MainCtrl', function($scope) {

$scope.updated = 0;

$scope.stop = function() {

textWatch();

};

var textWatch = $scope.$watch('text', function(newVal, oldVal) {

if (newVal === oldVal) { return; }

$scope.updated++;

});

});

2、1 ui-sref、$state.go 的区别ui-sref 一般使用在...消息中心$state.go('someState')一般使用在 controller里面;.controller('firstCtrl', function($scope, $state) { $state.go('login'); });这两个本质上是一样的东西,我们看ui-sref的源码:...element.bind("click", function(e) { var button = e.which || e.button; if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) { var transition = $timeout(function() { // HERE we call $state.go inside of ui-sref $state.go(ref.state, params, options); });ui-sref最后调用的还是$state.go()方法

3、什么时候使用$watch

angular会为我们自动执行$watch,当指令中有独立作用域,或者在异步函数中,改变的数据不在angular的执行上下文,就需要手动调用$apply 来触发$digest去执行$watch

4、$scope 和$rootscope的区别是,$rootscope是$scope 的祖宗作用域


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容