前端开发框架总结之Angular实用技巧(二)

                                前端开发框架总结之Angular实用技巧(二)

上文讲了Angular自定义指令,今天我们再来介绍下Angular数据绑定相关的知识。很多都是个人理解,大家选择性参考吧。

  • 数据绑定、监听、页面刷新

想要理解Angular数据双向绑定原理,那就要去看ng-model指令的实现过程。在实际项目中我们要理解两个重要变量$viewValue,$modelValue和两个个重要方法$render,$setViewValue。其中$viewValue是与页面相关的变量(和页面值不一定相等),$modelValue是与数据模型相关的变量(不一定相等),$render方法是把$viewValue的值同步到页面元素上,利用页面元素事件的监听会触发$setViewValue方法把页面的值同步到$viewValue,$modelValue,然后是同步到ctrl中的scope的属性。在自定义指令中我们可以使用require:‘?^ngModel’的方式获取到指令所在元素的ngModelCtrl对象。还有元素属性获取,自定义校验,页面刷新方法重写等方式在下面的例子中贴出。

一个页面加载大致过程如下(只写一些我关注的阶段):

HTML页面加载-----> HTML中的DOM元素加载-----> js加载------->根据页面内容生成相应的watcher------>controler加载----->记录controler中的watcher------->自定义指令link方法----->$scope的$apply方法------>scope的$digest方法----->遍历所有watcher--->-------->根据规则,对所有watcher的回调函数调用。------->ngModelwatch中会对$scope中的变量,$modelValue,$viewValue进行同步,然后调用ngModel的render方法,把值同步到页面。(其他类型的绑定比如ng-bind,{{}},他们的同步方式类似,只是在最后一步的watcher回调中走了不同的listener。)。

对于双向绑定还有把页面元素的值同步到$scope变量中的过程。这个是由页面元素的时间触发的,比如input的输入等,这个会触发ngModel的$setViewValue方法。把页面的值通过给viewValue,modelValue,进而同步给$scope中的变量。

为了监听一些$scope变量的变化,我们可以使用$watch方法,注意:如果监听的变量是一个对象,那么我们只改变对象的某个属性是不会触发watch方法的,因为对象的引用并没有发生变化,如果想要触发watch,就要在添加watch的时候指明需要深度比较。另外watch方法想要触发,一定是变量值改变之后,触发了digest数据同步,才会触发watch方法的。

最后来讲一下,主动触发把$scope值同步到页面的几种方式。这里会用到4个函数。$apply(),$digest(),$applyAsync(),$evalAsync();

$apply:遍历所有的scope的watcher,把他们的数据同步至页面。本质上最终调用的还是$digest。

$digest:遍历当前scope中的watcher,把他们的数据同步至页面。

$applyAsync:跟apply功能相同。只不过是他的“异步方法”,把发同步的过程延迟触发。在http请求频繁的页面可以使用$httpProvider.useApplyAsync(true).这样可以把页面刷新合并,不会频繁的触发apply方法。

扫描二维码关注公众号,回复: 4044491 查看本文章

$evalAsync:跟apply的功能也类似,只不过如果一次apply触发的遍历如果还没有结束的话,使用$evalAsync会把这次要更新的内容在本次遍历中进行执行,而不是等上一个apply循环执行完成之后再发起一遍遍历了。

真正理解了以上4中方法的意思,就可以在合适的场景中使用合适的方法,进而提高页面效率。

另外比如ng-click等指令、包括$http方法的回调完成后,框架中都是会主动调用digest或者apply的,所以一般不需要我们手动触发,但如果元素的click事件是我们手动绑定的,那么就是不会触发同步操作的。$timeout方法也会触发digest过程的。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
  <title>angular-test</title>
</head>
<body ng-app="ExampleApp">

  <div ng-controller="testCtrl">
      <input ng-model="inputValue" test-render name="testInput">
      <button id="btn" >手动绑定的点击事件</button>
      <button ng-click="change()" >指令绑定的点击事件</button>
  </div>

  <script src="jquery-3.2.1.min.js"></script>
  <script src="angular.js"></script>

  <script>
      var app = angular.module('ExampleApp', []);

      app.controller('testCtrl', function ($scope,$timeout) {
          $scope.inputValue = '初值';
          $scope.$watch('inputValue',function (newVal, oldVal) {
              console.log("watch:inputValue,newVal:" + newVal + ",oldVal:" + oldVal );
          });

          $scope.$watch('obj',function (newVal, oldVal) {
              console.log("watch:obj,newVal:" + newVal + ",oldVal:" + oldVal );
          },true);

          $scope.obj = new Object();

          $('#btn').on('click',btnClick);

          $scope.change = function () {
              $scope.obj.name = 'test';
              $scope.inputValue = '修改后的input值';
          }

          function btnClick() {
              $scope.obj.name = 'test';
              $scope.inputValue = '修改后的input值';

              //$timeout方法是不需要调用apply的,它自己会主动触发。
              /*$timeout(function () {
                  $scope.obj.name = 'test';
                  $scope.inputValue = '修改后的input值';
              });*/

              //$scope.$apply();
              //$scope.$applyAsync();
              //$scope.$evalAsync();
              $scope.$digest();

          }
      });

      app.directive('testRender',function () {
          return {
              restrict: 'A',
              require:'?^ngModel',
              link:function (scope,element,attrs,ngModel) {
                  //获取地址应属性所在元素的属性
                  console.log('获取属性值name:' + attrs.name);
                  console.log('获取属性值ngModel:' + scope.$eval(attrs.ngModel));

                  //可以主动设置viewValue,但是必须手动调用render方法刷新界面。
                  //ngModel.$setViewValue('主动设置的input');

                  //可以重写render方法,对render过程进行拦截。
                  /*ngModel.$render = function () {
                      console.log("这是我的刷新逻辑");
                  };*/

                  //手动触发的刷新。
                  //ngModel.$render();
              }
          }
      });
  </script>
</body>
</html>

 

 

 

 

猜你喜欢

转载自blog.csdn.net/wiki_Wei/article/details/83544584
今日推荐