
AngularJS 1 教程
toc
Intro
为什么需要前端框架
随着浏览器性能提升,更多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 面向模型,思路要转变。
同样的一个简单需求,可以明显看得出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脏检查就是入门到精通的核心。
从使用角度来说脏检查
上面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-if
, ng-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 函数,一般用作指令间的调用。
来自官网 AngularJS的Tab例子可以很好的说明controller的使用。
上述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 Directives, Refactoring Angular Apps to Component Style
再次多说一点的是,指令中能够精准定义scope交互的功能,从脏检查的角度来说也能在很大程度上减少$$watchers
的数量,以此来增强性能。
Promise
Promise的相关可以通过这篇文章来看,译用漫画来解说AngularJs中的Promises 。
AngularJS 1其实还有蛮多概念,不过毕竟是有点过时的框架,上述所写便是Angular 1值得关注留意的知识点了,应该能够足够了解Angular 1。