/ AngularJS

AngularJS 1 教程

为什么需要前端框架

随着浏览器性能提升,更多Web Page演变为Web App,特别是在中大型的项目中,就需要一个 前端框架 来:

  • 解耦应用的逻辑,数据模型,和界面视图
  • 更加方便的多人协作
  • 基本组件的抽离复用
  • 相对低成本的性能保证
  • 方便测试
  • ……

为什么2016年的今天仍然可以学习Angular 1

眼下潮流的框架太过于现代,入门门槛过高,学习React,Vue 2,Angular 2,需要首先学习npm、webpack、jsx、ES6、甚至Typescript。而且变化非常快,一些需要写前端的后端人员可能力不从心😂

而学习AngularJS 1 只需要基础的前端知识即可,Angular 1 属于经典的MVC类框架,API已经非常稳定,社区成熟,对低版本浏览器支持好(1.2以前版本支持IE6),性能依然满足大部分场景。

和jQuery 的不同

jQuery是库,面向DOM,Angular 面向模型,思路要转变。


JS Bin on jsbin.com

同样的一个简单需求,可以明显看得出jQuery中业务代码,直接操作DOM代码揉杂在一块,而Angular中JS代码关心业务逻辑,HTML描述界面非常的清晰。

一般而言,使用jQuery的弊病在于,

  • 用作中大型应用jQuery相对简陋,容易执着于DOM操作这种原子类问题。
  • 代码不好模块化,变量,方法处在全局作用域下面容易相互污染。
  • 代码不容易随着业务更改,扩展。
  • 还有相对反直觉的一点是, 如果页面交互复杂,而开发人员对DOM操作不精通,jQuery遍地$()的使用方式很容易造成性能问题
  • 遍地所谓的jQuery插件严重使得代码膨胀,性能低下!!!

当然框架本身的学习成本,是对项目后期的投资,不过项目本身不复杂,完全没必要使用前端框架,用了反而适得其反。

学习AngularJS 1

AngualrJs则通过数据双向绑定屏蔽了DOM操作,MVC解耦代码,依赖注入,自定义指令来复用代码,然后配合强大的路由,本地化,安全特性等,成功地成为了前ES6时代最流行的前端框架。

作用域、数据双向绑定、模块

作用域(scope)是AngualrJs中的基础概念,一般而言,一个controller一个scope , 每个controller中内置一个数据模型对象$scope。而 $scope对象是定义应用业务逻辑、控制器方法和视图属性的地方

上面的Demo中,业务变量number$scope的一个属性,然后通过数据绑定的方式链接到view。

<div>{{number}}</div>
<input type="number" ng-model="number">

{{ }}语法绑定到view中,这个符号还可以修改。

angular.module('app',[],function($interpolateProvider) {
    $interpolateProvider.startSymbol('(%');
    $interpolateProvider.endSymbol('%)');
})

ng-model就是 AngularJS 1中的一大特色: 数据双向绑定 ,model中数据变化了view中就会自动改变,而相应的view中(表单)变化了,也会自动同步到model。

Angular 1.3 之后支持了 controller as的语法,上面Demo就可以这样写了

<div ng-app="app" ng-controller="MainCtl as vm">
  <div>
    {{vm.number}}
  </div>
  <input type="number" ng-model="vm.number" require>
  <button ng-click="vm.addOne()">
    +1
  </button>
</div>
angular.module('app', [])
    .controller('MainCtl', function() {
      this.number = 0;
      this.addOne = function() {
        this.number += 1;
      }
    })

这样controller中不在显示的依赖$scope,完全就是普通的函数,干净,好测试,并且也有利于避规一些scope的原型继承导致的双向同步的bug,推荐这样书写。

需要注意的是controller中只操作数据即可,不要试图操作DOM,这点jQuery的同学一定要忍住😄,如果需要操作DOM,请使用指令,后续会讲到。

简单说一下模块

//声明模块
angular.module(‘app’, []);

相对独立的功能块可以声明为一个模块,然后通过依赖注入相互引用,这样达到方便的复用,控制,一般第三方插件都是通过模块方式引入到你的应用代码,而自身的业务代码也可以根据实际情况切分不同的模块。

到这一步已经可以开始写一定的Angualr应用了,按照一定功能粒度划分模块,然后纯粹js业务代码,之后数据绑定到view。

实际上之后Angular 1的种种概念都是围绕上述的展开和补充。

Angualr 1实现双向绑定的脏检查

AngualrJS 1中数据模型对象 $scope,就是普通的javascript对象(POJO),你在上面任意的添加属性和方法,Angular都支持并且能够实时双向绑定的“黑魔法”就是脏检查。

脏检查字面理解就是循环对比前后值,如果不相同说明就是“脏”的然后执行相应的操作,直到所有值相同,或者超出循环次数范围

如果说scope是入门的核心,那么Angualr脏检查就是入门到精通的核心。

从使用角度来说脏检查


JS Bin on jsbin.com

上面Demo timeout的例子中,通过原生setTimeout方法修改的变量,并没有更新到视图上,而1000毫秒setTimeout的能够更新。说明:

  • 脏检查需要一个契机触发,这也是AngualrJs 1中提供大量自己包装过的js原生就有的方法,典型的如 $timeout$http都是为了能够出发脏检查的 $digest,所以尽量使用AngularJS提供的方法。
  • 一次脏检查会便利App中所有的需要被观察的对象,有一个全局的$$watchers。1000毫秒setTimeout的能够更新是因为,这个时间点,恰好由$timeout方法触发了一次检查。因此这也就导致了从另一个角度分析脏检查。

从性能角度来说脏检查

上面例子说明了AngularJS脏检查的特性,手动触发,全局检查。

每次循环都要全部遍历一边$$watchers的值,而且如果被检测的值相互有依赖,还要循环多次。因此AngularJS脏检查很容易导致性能问题。因此

  • 限制不必要的监控数量,建议不超过2000个
  • 避免避免深度比较、复杂的逻辑。
  • 只绑定一次,Angular 1.3之后 {{::number}}语法有助于减少监控数量,因为 :: 开头的表达式都被认为是一次性表达式。一次性表达式一经赋值就会移除监控。
  • 必要时候使用指令 directive

指令 directive,以及用指令写组件

指令是Angular中相对低层,却又非常强大的功能。如果一般使用并不需要了解,使用内置的指令已经可以完成绝大多数功能。

AngularJs中本身以及内置了大量的指令,例如, ng-ifng-repeat , 甚至ng-controller

也可以通过下面方式来自定义指令。

angular.module('myApp', [])
.directive('myDirective', function() {
    return {
        restrict: String,
        priority: Number,
        terminal: Boolean,
        template: String or Template Function:
        function(tElement, tAttrs) (...},
        templateUrl: String,
        replace: Boolean or String,
        scope: Boolean or Object,
        transclude: Boolean,
        controller: String or function(scope, element, attrs, transclude, otherInjectables) { ... },
        controllerAs: String,
        require: String,
        link: function(scope, iElement, iAttrs) { ... },
        compile: // 返回一个对象或连接函数,如下所示:
            function(tElement, tAttrs, transclude) {
                return {
                    pre: function(scope, iElement, iAttrs, controller) { ... },
                    post: function(scope, iElement, iAttrs, controller) { ... }
                }
                // 或者
                return function postLink(...) { ... }
            }
    };
});

自定义指令相对复杂难懂,算是AngularJS中高阶能够,可以从下面三点简单理解的是:

  • scope字段,设定指令的作用域。
  • link 函数,如果需要接触DOM,代码在此函数中。
  • controller 函数,一般用作指令间的调用。

JS Bin on jsbin.com

来自官网 AngularJS的Tab例子可以很好的说明controller的使用。


JS Bin on jsbin.com

上述Nestlist Demo中使用指令的渲染速度明显快过使用Angular模版方式。

原因在于DOM写入是种相当耗时操作,大批量数据最好拼好HTML字符串一次性 innerHTML到页面中,这样的速度远快于逐步展开插入(Angualr 模版渲染方式)的速度 ,这也是AngualrJs中指令在现在看来也是很强大有用的功能。

  • 有机会直接操作DOM,这样也就
    • 有机会书写高效的渲染代码
    • 可以在此使用一些第三方的非AngularJS系js插件。
  • 能够隔离scope,甚至能够灵活的方式和其他scope交互,既可以使用=强大的双向绑定,而且AngularJs 1.5 scope中 <带了目前流行类似单向绑定的功能。
  • 扩展来说,在 **今天主流组件化的潮流之下,Angular 1完全可以依赖directive来按照component-based的方式书写框架,**甚至这点已经是目前Angualr 1社区中潮流用法:Component-Based AngularJS DirectivesRefactoring Angular Apps to Component Style

再次多说一点的是,指令中能够精准定义scope交互的功能,从脏检查的角度来说也能在很大程度上减少$$watchers的数量,以此来增强性能。

Promise

Promise的相关可以通过这篇文章来看,译用漫画来解说AngularJs中的Promises


AngularJS 1其实还有蛮多概念,不过毕竟是有点过时的框架,上述所写便是Angular 1值得关注留意的知识点了,应该能够足够了解Angular 1。