• Angularjs的$apply及其优化使用


    今天,我们要聊得是Angularjs中的小明星$apply。当我们数据更新了,但是view层却没反应时,总能听到有人说,用apply吧,然后,懵懂无知的我们,在赋值代码后面加了$scope.$apply(),然后就惊喜的发现。噢,真的更新了。
    然而,有些时候,编译器会无情的给你返回

    Error: $digest already in progress
    

    那么,导致这些现象的原因时什么的呢?$apply究竟干了啥?听我慢慢道来。

    一.$apply的作用

    $apply()函数可以从Angular框架的外部让表达式在Angular上下文内部执行。

    上面是AngularJs权威教程中的一句话。什么意思呢?
    首先,你要清楚,在原生js或者第三方框架下,修改model,是有可能不会触发视图更新的,比如setTimeout、jquery插件。为什么?因为他们脱离了Angularjs的上下文,Angularjs并不能监听到数据的改变。看例子。

    1.setTimeout

    html:

    <p>{{name}}</p>
    

    js:

    $scope.name="张三";
    setTimeout(function(){
    $scope.name = '李四';
    //$scope.$apply()
    },500)
    

    首先,name等于张三,500ms后,我把他赋值为李四,但是,页面上并没有改变,依然是张三。
    而,我们把$scope.$apply()放开,就正常了,张三成功变为李四。

    2.第三方插件

    html:

    <p>Date: <input type="text" id="datepicker"></p>
    <p>
    <header>所选日期</header>
    {{selectedDate}}
    </p>
    

    js:

    $scope.selectedDate = '';
    $( function() {
        $( "#datepicker" ).datepicker({
        onClose: function( selectedDate ) {
            $scope.selectedDate = selectedDate;
            // $scope.$apply();
        }
        });
    } );
    

    这是jquery的datepicker插件,当我们选定日期后,下面的日期应该随之显现,而现在却没有。这种情况就必须依靠$apply(),才能更新视图。

    以上两种情况,都因为不处于Angularjs上下文中,导致监听不到数据的变化。而$apply究竟干了什么,才导致数据更新正常了呢?

    其实$apply相当于一个触发器,它的作用就是触发digest循环,从而更新视图。

    在digest是Angularjs的核心,是它实现了神奇的数据绑定。凡是触发事件,必会触发digest循环,比如,我们数值的ng事件,click啊,change,实际上都是触发了digest循环。

    所以,我们所做的事,其实就是手动触发了digest循环。关于digest循环,属于题外话,这里不做过多介绍,想深入了解的同学,可以看看书籍,或者百度。

    二.更好地运用digest循环

    在Angularjs中,除了$apply可以触发digest循环外,还有其他的方法,也可以触发此循环。而且$apply往往时最坏的选择。下面推荐一些更好的选择。

    1.$digest

    $scope.$digest()的速度要比$apply要快,因为它只更新当前作用域和子作用域的值,对于父作用域时不管的。而$apply还要评估父作用域,这就大大消耗了性能。

    2.$timeout

    用$timeout去代替你的setTimeout,$timeout作为Angularjs的自带服务,当然时更契合Angularjs环境啦。它会隐性触发digest循环,而且它会延迟执行,会在上一个digest循环完成后的下一刻,触发digest循环,这样就不会出现上文所说的

    $digest already in progress
    

    我们把setTime的代码放到$timeout中

    $timeout(function(){
    $scope.name = '李四';
    },500)
    

    这就能正常工作了,看,没有讨厌的apply了!

    3.$evalAsync

    最推荐的应该时这个方法了。如果当前正好有一个digest循环在执行,那么它就会把导致digest循环的操作,放到当前digest循环中去执行。而$timeout是要等到当前digest循环执行完,再执行一次digest循环才可以。所以evalAsync执行更快,性能更好。我们可以像$timeout那样去调用它,即

    $scope.$evalAsync(
                        function( $scope ) {
                            console.log( "$evalAsync" );
                        }
                    );
    

    以上,就是今天要说的全部内容。Angularjs中还藏着许多奥秘,和更好的使用方法,希望大家可以深入地研究,分享出更好的文章。
    下面是可执行的代码,大家可以探究探究
    https://codepen.io/hanwolfxue/pen/yEZbYQ

  • 相关阅读:
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & ManacherK
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher J
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher I
    pat 1065 A+B and C (64bit)(20 分)(大数, Java)
    pat 1069 The Black Hole of Numbers(20 分)
    pat 1077 Kuchiguse(20 分) (字典树)
    pat 1084 Broken Keyboard(20 分)
    pat 1092 To Buy or Not to Buy(20 分)
    pat 1046 Shortest Distance(20 分) (线段树)
    pat 1042 Shuffling Machine(20 分)
  • 原文地址:https://www.cnblogs.com/yky-iris/p/9251230.html
Copyright © 2020-2023  润新知