美文网首页IT修真院-前端我爱编程
angular之中,$scope $rootScope $wat

angular之中,$scope $rootScope $wat

作者: 你隔壁的陌生人 | 来源:发表于2017-12-29 20:16 被阅读62次


    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 的祖宗作用域


    相关文章

      网友评论

        本文标题:angular之中,$scope $rootScope $wat

        本文链接:https://www.haomeiwen.com/subject/zjwpgxtx.html