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。

小刀

Read more posts by this author.

Subscribe to cc log

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!
comments powered by Disqus