zoukankan      html  css  js  c++  java
  • Angular实践----理解数据绑定过程

    说起angular的数据绑定,你可能脑海马上出现的就是这些$apply,$watch,$digest和脏值检测这个名词。

    正是由于这些名词背后的含义与功能组成了angular双向数据绑定这一项强大的功能。

    首先来看一下一张图,下面的来自angular官方的图片。解释了angular在数据绑定这部分是如何与浏览器的event loop机制做交互的。

    学习这个这个知识点之前,首先要理解下浏览器的event loop机制:

    1. 浏览器的事件循环等待事件的触发。所谓事件包括用户的交互操作、定时事件、或者网络事件(服务器的响应)。
    2. 事件触发后,回调会被执行。此时会进入Javascript上下文。通常回调可以用来修改DOM结构。
    3. 一旦回调执行完毕,浏览器就会离开Javascript上下文,并且根据DOM的修改重新渲染视图。

    那如何从JS context切换到angular context呢,这个过程是怎么实现的呢?

    答案就在angular 提供的$apply()函数:

    通常情况下,你不需要自己调用$apply()函数,这是因为$apply()都已经被用来处理当前事件的相应指令执行过了。

    只有当你使用自定义的事件回调或者是使用第三方类库的回调时,才需要自己执行$apply()

    下面的数据绑定过程来自angluar中文社区

    1. 通过调用 scope.$apply(stimulusFn)来进入AngularJS的执行上下文,这里的stimulusFn是你希望在AngularJS执行上下文中执行的函数。
    2. AngularJS会执行stimulusFn(),这个函数一般会改变应用的状态。
    3. AngularJS进入$digest循环。这个循环是由两个小循环组成的,这两个小循环用来处理处理$evalAsync队列和$watch列表。这个$digest循环直到模型“稳定”前会一直迭代。这个稳定具体指的是$evalAsync对表为空,并且$watch列表中检测不到任何改变了。
    4. 这个$evalAsync队列是用来管理那些“视图渲染前需要在当前栈框架外执行的操作的”。这通常使用 setTimeout(0)来完成的。用setTimeout(0)会有速度慢的问题。并且,因为浏览器是根据事件队列按顺序渲染视图的,还会造成视图的抖动。
    5. $watch列表是一个表达式的集合,这些表达式可能是自上次迭代后发生了改变的。如果有检测到了有改变,那么$watch函数就会被调用,它通常会把新的值更新到DOM中。
    6. 一旦AngularJS的$digest循环结束,整个执行就会离开AngularJS和Javascript的上下文。这些都是在浏览器为数据改变而进行重渲染之后进行的。

    这里你需要理解一些东西:

    一、$watch list

    $watch list的操作发生在指令的link阶段。

    每次你需要把model上的数据绑定到你的view上时,$watch队列就会插入一条对应的$watch$watch就是监视的model里数据变化的东西。

    看一下下面的代码:

    <!DOCTYPE html>
    <html ng-app="app">
    <head>
        <meta charset="utf-8">
        <title></title>
    
    </head>
    <body>
    <div ng-controller="MainCtrl">
        <input type="text" ng-model="name"/>
        <input type="text" ng-model="age"/>
    </div>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    <script type="text/javascript">
        var app=angular.module('app',[]);
    
        app.controller('MainCtrl',function ($scope){
            $scope.name='chenwei';
            $scope.age='25';
            $scope.text='world';
        });
    </script>
    </body>
    </html>

    $scope上有三个对象:name,age,text。我把name和age绑定到了对应的输入框上,text没有绑定,这时候只会向$watch list上插入name和age两个$watch。

    二、$digest loop

     $digest loop 有两个子循环组成:一个处理evalAsync队列,另一个处理$watch队列。

    当浏览器接收到可以被angular context处理的事件时,$digest循环就会触发。

    $digest 会去遍历$watch list队列,去检测里面的每一项$watch是否发生变化。

    这里需要注意的一点就是,如果有至少一个$watch更新过,这个循环就会再次触发,直到所有的$watch都没有变化。这样就能够保证每个model都已经不会再变化。记住如果循环超过10次的话,它将会抛出一个异常,防止无限循环。 当$digest循环结束时,DOM相应地变化。 

    三、定义自己的watch

    $scope对象有一个可供开发者调用的$watch('watchName',fn,boolean)函数

    看一个具体的代码例子:

    <!DOCTYPE html>
    <html ng-app="app">
    <head>
        <meta charset="utf-8">
        <title></title>
    
    </head>
    <body>
    <div ng-controller="MainCtrl">
        <input type="text" ng-model="name"/>
        <input type="text" ng-model="age"/>
        <p>your name changed {{changeCount}} times</p>
    </div>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    <script type="text/javascript">
        var app=angular.module('app',[]);
    
        app.controller('MainCtrl',function ($scope){
            $scope.name='chenwei';
            $scope.age='25';
            $scope.text='world';
            $scope.changeCount=0;
            $scope.$watch('name',function (newValue,oldValue){
                if(newValue===oldValue){
                    return;
                }
                $scope.changeCount++;
            },true)
        });
    </script>
    </body>
    </html>
    View Code

    上面的列子里我用$watch函数去检测name的值,一旦name的值发生变化,我就把它记录下来,并通过changeCount亮来通知name变化的次数

  • 相关阅读:
    Java知识回顾 (2) Java 修饰符
    SpringMVC和Springboot的区别
    VS Supercharger插件的破解
    Spring3.1新属性管理API:PropertySource、Environment、Profile
    如何进行接口测试
    commons-lang3中DateUtils类方法介绍
    springCloud com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
    rabbitmq 连接报错 An unexpected connection driver error occured
    rabbitmq的安装与使用
    mysql5.7 mysql库下面的user表没有password字段无法修改密码
  • 原文地址:https://www.cnblogs.com/cwWeb/p/3508515.html
Copyright © 2011-2022 走看看