zoukankan      html  css  js  c++  java
  • angularjs实践

    整个angular的开发过程都是可以使用yeoman构建的。譬如以下的命令。以coffescript形式生成angular的各个组件

    yo angular:route myroute --coffee
    yo angular:controller user --coffee
    yo angular:directive myDirective --coffee
    yo angular:filter myFilter --coffee
    yo angular:view user --coffee
    yo angular:service myService --coffee
    yo angular:decorator serviceName --coffee

     

    理解ngModel中的$parsers和$formatters

    formatters改变model中的值如何显示在view上

    parsers改变view上的如何存储在model中

    下面是一个例子

      //format text going to user (model to view)
      ngModel.$formatters.push(function(value) {
        return value.toUpperCase();
      });
    
      //format text from the user (view to model)
      ngModel.$parsers.push(function(value) {
        return value.toLowerCase();
      });

    在下面的地址中查看: http://plnkr.co/UQ5q5FxyBzIeEjRYYVGX

    <input type="button" value="set to 'misko'" ng-click="data.name='misko'"/>
    <input type="button" value="set to 'MISKO'" ng-click="data.name='MISKO'"/>
    <input changecase ng-model="data.name" />

    When you type a name in (view to model), you will see that the model is always lowercase. But, when you click a button and programatically change the name (model to view), the input field is always uppercase.

    $apply & $digest应该什么时候使用:

    官方给出了明确的答案,那就是在数据模型的改变不是通过angularjs自身的api来调用的时候就需要我们手动调用$apply或者$digest,例如:

    setTimeout(function(){scope.name='ken';},1000);

    这个时候模型数据改变了,但是不会反映到视图中,所以需要我们手动添加$apply,可以像下面这样:

    setTimeout(function(){scope.$apply(scope.name='ken';);},1000);

    或者也可以使用$timeout,在angularjs提供的$timeout里面会帮我们自动调用$apply;

     一段代码弄清楚$apply跟$digest的关系

    1 Scope.prototype.$apply = function(expr) {
    2   try {
    3     this.$beginPhase("$apply");
    4     return this.$eval(expr);
    5   } finally {
    6     this.$clearPhase();
    7     this.$digest();
    8   }
    9 };

    $interpolate的使用方式:

    var getFullName = $interpolate('{{first}}{{last}}');
    var scope = { first:'Pete',last:'Bacon Darwin' };
    var fullName = getFullName(scope);

    绑定验证反馈信息

    为了在我们的字段模板中显示错误信息,我们可以像下面这样做:
    <span class="help-inline" ng-repeat="error in $fieldErrors">
    {{$validationMessages[error](this)}}
    </span>
    this指向的是当前的scope

    在angularjs的routes中使用resolve

    .when('/admin/users/:userid', {
    templateUrl: 'tpls/users/edit.html'
    controller: 'EditUserCtrl',
    resolve: {
    user: function($route, Users) {
    return Users.getById($route.current.params.userid);
    }
    }
    })
    这里resolve的作用是返回内容提供给controller,返回的内容可以通过参数的形式传递到controllerz中

    .controller('EditUserCtrl', function($scope, user){
    $scope.user = user;
    ...
    })
    对于这种模式有一个益处,就是我们可以设计不同的路由来对应同一个controller,只要resolve中返回不同的值就可以了

    $routeProvider.when('/admin/users/new', {
    templateUrl:'admin/users/users-edit.tpl.html',
    controller:'UsersEditCtrl',
    resolve:{
    user: function (Users) {
    return new Users();
    }
    }
    });
    $routeProvider.when('/admin/users/:userId', {
    templateUrl:'admin/users/users-edit.tpl.html',
    controller:'UsersEditCtrl',
    resolve:{
    user: function ($route, Users) {
    return Users.getById($route.current.params.userId);
    }
    }
    });

    使用ng-include来处理多UI模板的情况

    $routeProvider.when('/admin/users/new', {
    templateUrl:'admin/admin.tpl.html',
    contentUrl:'admin/users/users-edit.tpl.html',
    menuUrl:'admin/menu.tpl.html',
    controller:'UsersEditCtrl',
    ...
    });

    在页面中,我们就可以通过$route.current得到对应的模板URL了

    <div>
    <div ng-include='$route.current.contentUrl'>
    <!--menu goes here -->
    </div>
    <div ng-include='$route.current.menuUrl'>
    <!--content goes here -->
    </div>
    </div>

    如何加快$digest循环

    1.减少$watch的表达式取值时间

    $watch的用法如下:$scope.$watch(watchExpression, modelChangeCallback);

    我们应当尽量减少watchExpression的计算时间

    2.减少console.log()的使用

    对比如下两段代码

    $scope.getName = function () {
    return $scope.name;
    };
    $scope.getNameLog = function () {
    console.log('getting name');
    return $scope.name;
    };

    他们所花费的时间对比:

     因此,我们应该尽量减少在产品阶段的代码中使用console.log打印日志

    3.尽量避免在watch-expression中访问DOM

    引用“mastering web application development with angularjs中的一段话:

    Any DOM operation is slow and computed properties are extra slow.
    The real problem is that DOM is implemented in C++. It turns out that
    it is very expensive to call a C++ function from JS. So any DOM access
    is magnitudes slower than JavaScript object access.

    4.限制watches的执行数量

    可通过删除不必要的watch来实现

    重新审视UI,在布局上是否够轻量级,有没有必要存在大量的DOM,我们应该化繁为简

    慎重使用watch来显示隐藏DOM,例如当我们使用ng-show来隐藏某个DOM的时候,如果DOM里面绑定了某个模型数据,在每次input变化的时候模型数据都会变化,$digest循环都会对其进行计算,因此而浪费资源,在这种时候我们使用ng-switch可能会更加适合。

    5.删除不再使用的watches

    var watchUnregisterFn = $scope.$watch('name', function (newValue,
    oldValue) {
    console.log("Watching 'name' variable");
    ...
    });
    //later on, when a watch is not needed any more:
    watchUnregisterFn();
    如上代码所示,$scope.$watch()返回一个函数,这个函数可以用来取消我们的监控,只要将这个函数执行一次即可。

    6.减少$digest的使用频率

    类似于做一个定时器,每秒钟更新一下时间,我们会用到timeout, 但更好是使用angularjs给我们提供的$timeout服务,代码如下:

     1 .directive('clock', function ($timeout, dateFilter) {
     2  return {
     3      restrict: 'E',
     4      link: function (scope, element, attrs) {
     5          function update() {
     6              // get current time, format it and update DOM text
     7              element.text(dateFilter(new Date(), 'hh:mm:ss'));
     8              //repeat in 1 second
     9              $timeout(update, 1000);
    10          }
    11          update();
    12      }
    13  };
    14 })

    但是这样有一个问题,每过一秒中,$digest都会执行一次,还好angularjs给我们的$timeout提供了第三个参数来决定是否调用$digest,代码可以改为:

    function update() {
    element.text(dateFilter(new Date(), 'hh:mm:ss'));
    $timeout(update, 1000, false);
    }


    7.无论什么时候都应该避免深度的watch

    例如有这样一个user对象:

    $scope.user = {
    firstName: 'AngularJS',
    lastName: 'Superhero',
    age: 4,
    superpowers: 'unlimited',
    // many other properties go here…
    };
    我们可以通过如下方式来实现深度监控,就是在$watch中传入第三个值为true的参数

    $scope.$watch('user', function (changedUser) {
    $scope.fullName =
    changedUser.firstName + ' ' + changedUser.lastName;
    }, true);
    但是这种方式非常不友好,占用内存,计算复杂,我们可以使用更好的方式:

    $scope.$watch(function(scope) {
    return scope.user.firstName + ' ' + scope.user.lastName;
    }, function (newFullName) {
    $scope.fullName = newFullName;
    });
    还有另外一种避免使用$watch的方式,只需要我们修改模板即可

    在模板中绑定方法,{{ fullName() }}
    然后controller中定义方法

    $scope.fullName = function () {

      return $scope.user.firstName + ' ' + $scope.user.lastName;
    };

    8.分析被watch的expression表达式

    分析如下一段代码:

    <p>This is very long text that refers to one {{variable}} defined on a
    scope. This text can be really, really long and occupy a lot of space
    in memory. It is so long since… </p>
    在angularjs中,angularjs会将整段代码保存到内存中,而不仅仅是{{variable}}这个变量,如果我们希望真个表达式变得更短,消耗内存更小,我们可以给变量添加一个标签:

    <p>This is very long text that refers to one <span ngbind='variable'></span> defined on a scope. This text can be really,
    really long and occupy a lot of space in memory. It is so long since…
    </p>

     如何添加依赖

    我们可以很简单的注入依赖就像下面这样

    angular.module('projects', []).controller('ProjectCtrl', function($scope){//todo...});

    但是这样存在一个后期隐患,因为代码压缩后,我们就无法得知传入的参数是什么了,$scope可能会变成a或者b;

    因此我们需要换一种方式来添加依赖

    angular.module('projects',[]).controller('ProjectCtrl', ['$scope', function($scope){//todo}]);

    还有config以及run方法我们都可以用同样的方法来添加依赖

    angular.module('app').config(['$routeProvider', '$localtionProvider', function($routeProvider, $locationProvider){

    $locationProvider.html5Mode(true);

    $routeProvider.otherwise({redirectTo: '/projectsinfo'});

    }]);

    angular.module('app').run(['security', function(security){

    security.requestCurrentUsesr();

    }]);

    其他provider, derective依葫芦画瓢

    预加载模板技术

    一般的,模板的加载过程会消耗一个网络延时,angularjs给我们提供了两种解决方案来实现预加载模板

    第一种方法是使用script标签来预加载模板

    将模板用script标签嵌套起来,添加angularjs可识别的属性,另外,该模板需要写在ng-app范围之内;

    <script type="text/ng-template" id="tpls/users/list.html">

    <div class="hello">hello world</div>

    </script>

    应该注意一点,id值与我们的template url一致

    <html ng-app>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.js"></script>
    <body>
    <div ng-include='"test"'></div>
    <script type="text/ng-template" id="test">
      This is the content of the template
    </script>
    </body>
    </html>
    ng-include的值必须用'""'或者"''",只有一个单引号或者双引号的时候会无效

    第二种方法是使用$templateCache服务 

    var myApp = angular.module('myApp', []);
    myApp.run(function($templateCache) {
      $templateCache.put('templateId.html', 'This is the content of the template');
    });
    通过下面的方式来倒入模板
    <div ng-include=" 'templateId.html' "></div>
    或者也可以通过javascript的方式拿到模板
    $templateCache.get('templateId.html');

    理解$compile
    angularjs中的$compile服务可以提供编译服务,如果是$compile()这样使用的话会返回一个link方法,例如
    var link = $compile('<p>hello {{name}}</p>');
    再传入一个scope的话就能将dom跟scope进行连接返回一个angular element;返回的element我们可以插入DOM树中

    代码片段,将模板保存在$templateCache中:

    hello.run(function($templateCache) {
    $templateCache.put('templateId.html', '<a>this is the content of the template{{name}}</a>');
    });

    上面的代码需要注意一点,保存到$templateCache中的模板必须有标签包围,否则报错,例如上面有a标签包围;

    代码片段,从$templateCache中获取模板并编译插入DOM树中:

    $scope.showmyhtml = function() {
    //$scope.myhtml = $templateCache.get('templateId.html');
    var element = $compile($templateCache.get('templateId.html'))($scope);
    console.log(element);
    angular.element(document.querySelector('#myhtml')).append(element);
    }

    注意一点的是angularjs中自带的jqlite不能想jquery那样直接通过$符号来使用,可以通过angular.element来将dom转化为jq对象;

    angular装饰者decorator

    decorator在$provider的命名空间下面,该方法传递两个参数,一个需要装饰的对象,一个是装饰函数体

     1 var Mail = function() {
     2     this.receiver = '';
     3     this.body = '';
     4     this.cc = [];
     5 };
     6 
     7 Mail.prototype.setReceiver = function(receiver) {
     8     this.receiver = receiver;
     9 };
    10 
    11 Mail.prototype.setBody = function(body) {
    12     this.body = body;
    13 };
    14 
    15 angular.module('A', []).service('Mail', Mail);
     1 angular.module('B', ['A']).config(function($provide) {
     2     $provide.decorator('Mail', function($delegate) {
     3         $delegate.addCC = function(cc) {
     4             this.cc.push(cc);
     5         };
     6         return $delegate;
     7     });
     8 })
     9 .controller('TestCtrl', function($scope, Mail) {
    10     Mail.addCC('jack');
    11     console.log(Mail);
    12 });

    angular的调试

    常用到的调试工具类似于console.log(), angular提供了$log服务

    1 angular.module('app', [])
    2 
    3 .controller('MainCtrl', ['$log', function($log){
    4     $log.debug('Hello Debug!');
    5 }]);

    当然我们可以设置是否打开日志的功能,在config中进行配置:

    1 angular.module('app', [])
    2 
    3 .config(['$logProvider', function($logProvider){
    4     $logProvider.debugEnabled(false);
    5 }])
    6 
    7 .controller('MainCtrl', ['$log', function($log){
    8     $log.debug('Hello Debug!');
    9 }])

    同时,我们还可以利用上面说到的decorator来对$log进行装饰:

     1 angular.module('app', [])
     2 
     3 .config(['$provide', function ($provide) {
     4     $provide.decorator('$log', ['$delegate', function ($delegate) {
     5         // Keep track of the original debug method, we'll need it later.
     6         var origDebug = $delegate.debug;
     7         /*
     8          * Intercept the call to $log.debug() so we can add on 
     9          * our enhancement. We're going to add on a date and 
    10          * time stamp to the message that will be logged.
    11          */
    12         $delegate.debug = function () {
    13             var args = [].slice.call(arguments);
    14             args[0] = [new Date().toString(), ': ', args[0]].join('');
    15             
    16             // Send on our enhanced message to the original debug method.
    17             origDebug.apply(null, args)
    18         };
    19 
    20         return $delegate;
    21     }]);
    22 }])
    23 
    24 .controller('MainCtrl', ['$log', function ($log) {
    25     $log.debug('Hello Debug!');
    26 }]);

     内存管理的重要性

    angularjs在销毁一个scope和把一个scope从它的父级移除之前会广播一个$destroy事件,监听这个事件对清理任务和资源很重要,例如一个timeout的例子:

    1 module.controller("MyController", function($scope, $timeout) {
    2     var timeout = function() {
    3         $scope.value += 1;
    4         $timeout(timeout, 100);
    5     };
    6     $timeout(timeout, 100);
    7     $scope.value = 0;
    8  
    9 });

    如果用户来回导航到一个view来加载这个controller,那每次导航将会添加另一个永远运行的计时器,监听$destroy,我们可以取消掉timeout来移除资源的消耗:

     1 module.controller("MyController", function($scope, $timeout) {
     2 var timeout = function() {
     3     $scope.value += 1;
     4     timer = $timeout(timeout, 100);
     5 };
     6  
     7     var timer = $timeout(timeout, 100);
     8     $scope.value = 0;
     9     $scope.$on("$destroy", function() {
    10  
    11         if (timer) {
    12             $timeout.cancel(timer);
    13         }
    14     });
    15 });

    angularjs中对img的src属性应该使用ng-src来代替

    angularjs中应该尽可能用promise来处理回调

    第三方的回调应该使用$apply来包裹,以便通知angularjs关于环境的变化

    如果我们不想让用户在angularjs未加载之前显示html,可以使用ng-cloak来隐藏html

    <div class="ng-cloak">...... .ng-cloak{display: none;}

    angularjs加载完后会将.ng-cloak变为block;

    编写我们自己的directive,最好的实践不是使用ng前缀,可以使用类似于<my-component>的方式

     

  • 相关阅读:
    Code First 迁移----官方 应用程序启动时自动升级(MigrateDatabaseToLatestVersion 初始值设定项)
    使用 Entity Framework Core 时,通过代码自动 Migration
    泛型反射获取特性值
    使用Expression动态创建lambda表达式
    ElasticSearch + Logstash + Kibana 搭建笔记
    PHP获取指定函数定义在哪个文件中及行号
    Wordpress中文章的特色图像Featured Image究竟存在哪里?
    PhpSpreadsheet生成Excel时实现单元格自动换行
    Composer使用体验
    PHP命名空间学习笔记
  • 原文地址:https://www.cnblogs.com/moyiqing/p/4421620.html
Copyright © 2011-2022 走看看