angular问题总结与反思

因为工作中实际开发需要,才开始接触angular框架。从当初的比葫芦画瓢,被各种问题、概念折磨摧残,到现在有一定的了解认识,觉得有必要将自己的认识进行简单的总结。不到位的地方还望多多包涵。

1.双向数据绑定

目前业内盛行各种MV**框架,相关的框架不断涌现,而angular就是其中的一种(MVVM)。MV**框架其实最核心的问题就是将view层和model分离开来,降低代码的耦合性,做到数据和表现的分离,MVC、MVP、MVVM均有相同的目标,而他们之间的不同就在于如何把model层和view关联起来。

数据在model、view层如何流动就成了问题的关键,angular通过dirty-check实现了数据的双向绑定。所谓的双向绑定,就是view的变化可以反映到model层,而model数据的变化可以在view体现出来。那么angular是如何做到双向绑定的?为何成为dirty-check呢?还是前端的一个原始问题出发吧:

 1 html:
 2 <input type="button" value="increase 1" id="J-increase" />
 3 <span id="J-count"></span>
 4
 5 js:
 6 <script>
 7     var bindDate = {
 8         count: 1,
 9         appy: function () {
10             document.querySelector(‘#J-count‘).innerHTML = this.count;
11         },
12         increase: function () {
13             var _this = this;
14             document.querySelector(‘#J-increase‘).addEventListener(‘click‘, function () {
15                 _this.count++;
16                 appy();
17             }, true);
18         },
19         initialize: function () {
20             // 初始化
21             this.appy();
22             //
23             this.increase();
24         }
25     };
26     bindDate.initialize();
27 </script>

在上面的例子中,存在两个过程:

  • view层影响model层: 页面上点击button,造成数据count的数量增加1
  • model层反应view层: count发生完变化以后,通过apply函数来反映到view层上

这是以前使用jquery、YUI等类库实现的数据处理,这里面存在的问题很明显: 1. 涉及到了大量的DOM操作; 2. 过程繁琐;3.代码耦合性太高,不便于写单元测试。下面来看看angular是如何进行数据处理的:

第一步. 添加watcher:就是当数据发生变化的时候,需要检测哪些对象,需要先进行注册

 1 // 对angular里面的源码进行了精简
 2 $watch: function(watchExp, listener, objectEquality) {
 3     var scope = this,
 4         array = scope.$$watchers,
 5         watcher = {
 6             fn: listener,
 7             last: initWatchVal,
 8             get: get,
 9             exp: watchExp,
10             eq: !!objectEquality
11         };
12     if (!array) {
13         array = scope.$$watchers = [];
14     }
15     array.unshift(watcher);
16 }

第二步. dirty-check:就是当有某个scope作用域下的数据发生变化后,需要遍历检测注册的$$watchers = [...]

1 $digest: function() {
2     while (length--) {
3         watch = watchers[length];
4         watch.fn(value, lastValue, scope);
5     }
6 }

这样就实现了数据的双向绑定,上面的实现是否跟自定义事件很像呢?可以看到使用了观察者设计模式或(publisher-subscriber)。

2.依赖注入

使用过spring框架的同学都知道,Ioc、AOP是spring里面最重要的两个概念,而Ioc就可以里面为注入依赖(DI),很明显angular带有非常浓厚的后端色彩。

同样,首先来看下不使用DI,是如何解决对象相互依赖的:

 1 function Car() {
 2     ...
 3 }
 4 Car.prototype = {
 5     run: function () {...}
 6 }
 7
 8 function Benz() {
 9     var cat = new Car();
10 }
11 Benz.prototype = {
12     ...
13 }

在上面的例子中,类Benz依赖于类Car,直接通过内部New来解决这种依赖关系。这样做的弊端非常明显,代码耦合性变高,不利于维护。后端框架很早就意识到了这个问题,spring早期通过在xml文件中注册对象之间的依赖关系,后来有通过anotation的方式更加方便地解决DI问题,COS端的同学可以看看后端的代码。

js语言本身是不具有注解(annotation)机制的,那angular是如何实现的呢?

1.模拟注解

 1 // 注解的模拟
 2 function annotate(fn, strictDi, name) {
 3     var $inject;
 4     if (!($inject = fn.$inject)) {
 5         $inject = [];
 6         $inject.push(name);
 7     }else if (isArray(fn)) {
 8         $inject = fn.slice(0, last);
 9     }
10     return $inject;
11 }
12 createInjector.$$annotate = annotate;

2. 注入对象的创建

 1 function createInjector(modulesToLoad, strictDi) {
 2     //通过singleton模式创建对象
 3     var providerCache = {
 4         $provide: {
 5             provider: supportObject(provider),
 6             factory: supportObject(factory),
 7             service: supportObject(service),
 8             value: supportObject(value),
 9             constant: supportObject(constant),
10             decorator: decorator
11           }
12       },
13     instanceCache = {},
14     instanceInjector = (instanceCache.$injector =
15         createInternalInjector(instanceCache, function(serviceName, caller) {
16             var provider = providerInjector.get(serviceName + providerSuffix, caller);
17             return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
18         }));
19     return instanceInjector;
20 }

3. 获取注入对象

 1 function invoke(fn, self, locals, serviceName) {
 2     var args = [],
 3         $inject = annotate(fn, strictDi, serviceName);
 4
 5     for (...) {
 6         key = $inject[i];
 7         // 替换成依赖的对象
 8         args.push(
 9           locals && locals.hasOwnProperty(key)
10           ? locals[key]
11           : getService(key, serviceName)
12         );
13     }
14     if (isArray(fn)) {
15         fn = fn[length];
16     }
17     return fn.apply(self, args);
18 }

到这里,是否是看到很多后端框架设计的思路,没有anotation就模拟一个,难怪PPK要说angular是" a front-end framework by non-front-enders for non-front-enders"

3.controller通信

在实际开发中,应用系统会非常庞大,一个应用app不可能只存在一个controller,那么不同controller之间就存在通信的可能,如何解决这个常见问题,主要有两种方法:

1.事件机制: 把事件注册在$rootScope上,这样做的问题就是会在$rootScope上注册太大事件,会引起一些列后续问题

 1 //controller1
 2 app.controller(‘controller1‘, function ($rootScope) {
 3     $rootScope.$on(‘eventType‘, function (arg) {
 4         ......
 5     })
 6 })
 7
 8 // controller2
 9 app.controller(‘controller2‘, function ($rootScope) {
10     $rootScope.$emit(‘eventType‘,arg);
11     or
12     $rootScope.$broadcast(‘eventType‘,arg);
13 })

2.通过service: 充分利用angular的DI特性,利用service是单例的特点,在不同controller之间起到桥梁作用

 1 // 注册service
 2 app.service(‘Message‘, function () {
 3     return {
 4         count: void(0);
 5     }
 6 })
 7
 8 // controller1,修改service的count值
 9 app.controller(‘controller1‘, function ($scope, Message) {
10     $scope.count = 1;
11     Message.count = $scope.count;
12 });
13
14 // controller2, 获取service的count值
15 app.controller(‘controller2‘, function ($scope, Message) {
16     $scope.num = Message.count;
17 });

4.service的特点

1. 单例(singleton): angular里面只有service才可以进行DI诸如,controller、directive这些均不具有这些功能,service字面上就是提供一些基本的服务,跟具体的业务没有关联,而controller、directive则与具体业务紧密相关联,所以需要保证service的唯一性。

2. lazy new:angular首先会生成service的provider,但是并没有立即生成对应的service,只有到需要这些服务的时候才会进行实例化操作。

3. provider)的分类: provider()、factory、service、value、constant,其中provider是最底层的实现,其他方式都是在其基础上的语法糖(sugar),需要注意的是这些服务最终均要添加$get方法,因为具体service是通过执行$get方法生成的。

5. directive的实现

directive的编译(compiler)包括两个阶段: compile、link。简单来讲compile阶段主要处理template DOM,此时并不涉及作用域问题,也就是没有进行数据渲染,例如ngRepeate指令就是通过compile进行template修改的,执行compile后会返回link函数,覆盖后面定义的link函数;而link主要是进行数据渲染,分为pre-link和post-link两个环节,这两个环节解析的顺序是相反,post-link是先解析内部,然后才是外部,这样对directive的解析就是安全的,因为directive内部还可以包括directive,同时link是对真正DOM的处理,会涉及DOM操作的性能问题。

参考

https://angularjs.org/

https://github.com/xufei/blog/issues/10

http://teropa.info/blog/2013/11/03/make-your-own-angular-part-1-scopes-and-digest.html

http://www.quirksmode.org/blog/archives/2015/01/the_problem_wit.html

http://www.jvandemo.com/the-nitty-gritty-of-compile-and-link-functions-inside-angularjs-directives/

时间: 2024-12-29 23:23:08

angular问题总结与反思的相关文章

angular中使用promise

promise是一种用异步的方式处理值的方法,promise是对象,代表了一个函数最终可能的返回值或者抛出的异常,在与远程对象打交道时我们可以把他看作是远程对象的一个代理. 如果说是promise也是异步处理方式的一种,那么我们会想起它和XHR和$.ajax有啥区别呢? 习惯上js使用闭包或者回调来相应非同步返回的数据,比如页面加载之后的XHR请求.我们可以跟数据进行正常交互,就好像它已经返回了一样,而不需要依赖回调函数的触发. 那么ng提出的promise是为了解决什么问题呢? 回调已经被使用

Angular vs React 最全面深入对比

如今,Angular和React这两个JavaScript框架可谓红的发紫,同时针对这两个框架的选择变成了当下最容易被问及或者被架构设计者考虑的问题,本文或许无法告诉你哪个框架更优秀,但尽量从更多的角度去比较两者,尽可能的为你在选择时提供更多的参考意见. 选择的方法 在选择之前,我们尝试带着一些问题去审视你将要选择的框架(或者是任何工具),尝试用这些问题的答案来帮助我们更加了解框架,也更加让选择变得更容易 框架本身的问题: 是否成熟?谁在背后支持呢? 具备的功能? 采用什么架构和模式? 生态系统

Angular JS中 Promise用法

一.Promise形象讲解A promise不是angular首创的,作为一种编程模式,它出现在1976年,比js还要古老得多.promise全称是 Futures and promises. 而在javascript世界中,一个广泛流行的库叫做Q 地址是https://github.com/kriskowal/q 而angular中的$q就是从它引入的.promise解决的是异步编程的问题,对于生活在同步编程世界中的程序员来说,它可能比较难于理解,这也构成了angular入门门槛之一,以下将用

Angular for MVC—项目搭建(1)

今天准备用angular+mvc创建一个项目,发现上次研究关于angular的东西全忘记了,突然想起上次我事后补了一篇博客的,看了一下也没太回忆起来细节.无奈,重新研究一边,这次是一边实践一边记录的,希望再过段时间我再来看的时候,希望能有帮助.我想记录博客目的也在于此.利于温故.你们要是觉得有用点个赞,没用勿喷,谢谢! 1.创建MVC项目 2.创建angular项目 2.1首先安装angular环境 下载安装node.js和npm,下载地址:https://nodejs.org/en/downl

angular中模板

<!DOCTYPE html><!--调用模块--><html lang="en" ng-app="myApp"><head> <meta charset="UTF-8"> <script src="js/angular.js"></script> <title>Title</title></head><

利用angular打造管理系统页面

1 创建一个新的angular应用 ng new adminSystem 2 利用WebStorm打开adminSystem应用 3 借助AdminLTE这个开源项目来辅助开发 AdminLTE项目:点击前往 将AdminLTE项目的精简版本源代码复制到adminSystem应用主模块的主组件的模板中 AdminLTE项目的精简版本效果图 <!DOCTYPE html> <!-- This is a starter template page. Use this page to star

Angular基础(二) 组件的使用

? 一.简单操作 a) 使用Angular CLI可以快速创建项目框架,先运行 $ npm install –g @angular/[email protected]安装CLI,为CLI的位置设置环境变量,然后就可以全局使用ng命令了. 执行ng new –ng4 angular-hello-world可以创建Angular4项目,会自动生成基础的文件夹和文件,还会自动进行npm i操作,下载并安装所需的依赖. 然后执行ng serve就可以编译并启动这个程序了,但ng并不会自动打开浏览器. b

Angular 2基础(一) 环境搭建

Angular2是一款开源JavaScript库,由Google维护,用来创建页面应用程序.正式发布于2016年9月,基于ES6开发. 一.准备工作 使用Angular2开发,需要预先做一些配置上的配置,如NPM.Node.Js.Git等,还要对TypeScript有个大概的了解. a)NPM是随同NodeJS一起安装的包管理器,那应该类似NuGet的作用了.NPM集成在了NodeJS安装包,可以一起安装,在CMD输入npm –v和node –v可以验证是否已安装. b)TypeScript则是

angular踩坑之路:初探webpack

之前费了一番力气安装好了angular开发环境,后面的几天都是在angular中文官网上看文档,照着英雄教程一步一步操作,熟悉了angular的一些基本特性,这部分没有遇到什么大问题,还比较顺利.这两天在看官方文档中的Webpack简介,想跟着文档做一遍,了解一下如何用Webpack打包angular项目,结果遇到了一些问题,因为是初学angular和Webpack的小白,这些问题一时难以解决,花费了不少时间,想在这里记录一下. 首先跟着文档将相关的文件都添加到项目中,目录是这样子的: 根据文档