全国服务热线:400-885-2225
最新活动

JAVA培训之AngularJS开发人员最常犯的10个错误

发布时间:2016-01-22 18:06 | 编辑:admin

 

简介
AngularJS是目前最为活跃的Javascript框架之一,AngularJS的目标之一是简化开发过程,这使得AngularJS非常善于构建小型app原型,但AngularJS对于全功能的客户端应用程序同样强大,它结合了开发简便,特性广泛和出众的性能,使其被广泛使用。然而,大量使用也会产生诸多误区。以下这份列表摘取了常见的一些AngularJS的错误用法,尤其是在app开发过程中。

1. MVC目录结构

AngularJS,直白地说,就是一个MVC框架。它的模型并没有像backbone.js框架那样定义的如此明确,但它的体系结构却恰如其分。当你工作于一个MVC框架时,普遍的做法是根据文件类型对其进行归类:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

templates/

    _login.html

    _feed.html

app/

    app.js

    controllers/

        LoginController.js

        FeedController.js

    directives/

        FeedEntryDirective.js

    services/

        LoginService.js

        FeedService.js

    filters/

        CapatalizeFilter.js

看起来,这似乎是一个显而易见的结构,更何况Rails也是这么干的。然而一旦app规模开始扩张,这种结构会导致你一次需要打开很多目录,无论你是使用sublime,Visual Studio或是Vim结合Nerd Tree,你都会投入很多时间在目录树中不断地滑上滑下。

与按照类型划分文件不同,取而代之的,我们可以按照特性划分文件:

1

2

3

4

5

6

7

8

9

10

11

12

13

app/

    app.js

    Feed/

        _feed.html

        FeedController.js

        FeedEntryDirective.js

        FeedService.js

    Login/

        _login.html

        LoginController.js

        LoginService.js

    Shared/

        CapatalizeFilter.js

这种目录结构使得我们能够更容易地找到与某个特性相关的所有文件,继而加快我们的开发进度。尽管将.html和.js文件置于一处可能存在争议,但节省下来的时间更有价值。

2. 模块

将所有东西都一股脑放在主模块下是很常见的,对于小型app,刚开始并没有什么问题,然而很快你就会发现坑爹的事来了。

1

2

3

4

5

6

7

var app = angular.module('app',[]);

app.service('MyService'function(){

    //service code

});

app.controller('MyCtrl'function($scope, MyService){

    //controller code

});

在此之后,一个常见的策略是对相同类型的对象归类。

1

2

3

4

5

6

7

8

9

10

11

var services = angular.module('services',[]);

services.service('MyService'function(){

    //service code

});

 

var controllers = angular.module('controllers',['services']);

controllers.controller('MyCtrl'function($scope, MyService){

    //controller code

});

 

var app = angular.module('app',['controllers''services']);

这种方式和前面第一部分所谈到的目录结构差不多:不够好。根据相同的理念,可以按照特性归类,这会带来可扩展性。

1

2

3

4

5

6

7

8

var sharedServicesModule = angular.module('sharedServices',[]);

sharedServices.service('NetworkService'function($http){});

 

var loginModule = angular.module('login',['sharedServices']);

loginModule.service('loginService'function(NetworkService){});

loginModule.controller('loginCtrl'function($scope, loginService){});

 

var app = angular.module('app', ['sharedServices''login']);

当我们开发一个大型应用程序时,可能并不是所有东西都包含在一个页面上。将同一类特性置于一个模块内,能使跨app间重用模块变得更容易。

3. 依赖注入

依赖注入是AngularJS最好的模式之一,它使得测试更为简单,并且依赖任何指定对象都很明确。AngularJS的注入方式非常灵活,最简单的方式只需要将依赖的名字传入模块的function中即可:

1

2

3

4

5

6

7

var app = angular.module('app',[]);

 

app.controller('MainCtrl'function($scope, $timeout){

    $timeout(function(){

        console.log($scope);

    }, 1000);

});

这里,很明显,MainCtrl依赖$scope和$timeout。

直到你准备将其部署到生产环境并希望精简代码时,一切都很美好。如果使用UglifyJS,之前的例子会变成下面这样:

1

2

var app=angular.module("app",[]);

app.controller("MainCtrl",function(e,t){t(function(){console.log(e)},1e3)})

现在AngularJS怎么知道MainCtrl依赖谁?AngularJS提供了一种非常简单的解决方法,即将依赖作为一个数组传入,数组的最后一个元素是一个函数,所有的依赖项作为它的参数。

1

2

3

4

5

app.controller('MainCtrl', ['$scope''$timeout'function($scope, $timeout){

    $timeout(function(){

        console.log($scope);

    }, 1000);

}]);

这样做能够精简代码,并且AngularJS知道如何解释这些明确的依赖:

1

app.controller("MainCtrl",["$scope","$timeout",function(e,t){t(function(){console.log(e)},1e3)}])

3.1 全局依赖

在编写AngularJS程序时,时常会出现这种情况:某个对象有一个依赖,而这个对象又将其自身绑定在全局scope上,这意味着在任何AngularJS代码中这个依赖都是可用的,但这却破坏了依赖注入模型,并会导致一些问题,尤其体现在测试过程中。

使用AngularJS可以很容易的将这些全局依赖封装进模块中,所以它们可以像AngularJS标准模块那样被注入进去。

Underscrore.js是一个很赞的库,它可以以函数式的风格简化Javascript代码,通过以下方式,你可以将其转化为一个模块:

1

2

3

4

5

6

7

8

9

10

11

12

13

var underscore = angular.module('underscore', []);

underscore.factory('_'function() {

  return window._; //Underscore must already be loaded on the page

});

var app = angular.module('app', ['underscore']);

 

app.controller('MainCtrl', ['$scope''_'function($scope, _) {

    init = function() {

          _.keys($scope);

      }

 

      init();

}]);

这样的做法允许应用程序继续以AngularJS依赖注入的风格进行开发,同时在测试阶段也能将underscore交换出去。

这可能看上去十分琐碎,没什么必要,但如果你的代码中正在使用use strict(而且必须使用),那这就是必要的了。

4. 控制器膨胀

控制器是AngularJS的肉和土豆,一不小心就会将过多的逻辑加入其中,尤其是刚开始的时候。控制器永远都不应该去操作DOM,或是持有DOM选择器,那是我们需要使用指令和ng-model的地方。同样的,业务逻辑应该存在于服务中,而非控制器。

数据也应该存储在服务中,除非它们已经被绑定在$scope上了。服务本身是单例的,在应用程序的整个生命周期都存在,然而控制器在应用程序的各状态间是瞬态的。如果数据被保存在控制器中,当它被再次实例化时就需要重新从某处获取数据。即使将数据存储于localStorage中,检索的速度也要比Javascript变量慢一个数量级。

AngularJS在遵循单一职责原则(SRP)时运行良好,如果控制器是视图和模型间的协调者,那么它所包含的逻辑就应该尽量少,这同样会给测试带来便利。

5. Service vs Factory

几乎每一个AngularJS开发人员在初学时都会被这些名词所困扰,这真的不太应该,因为它们就是针对几乎相同事物的语法糖而已!

以下是它们在AngularJS源代码中的定义:

1

2

3

4

5

6

7

8

9

function factory(name, factoryFn) { 

    return provider(name, { $get: factoryFn }); 

}

 

function service(name, constructor) {

    return factory(name, ['$injector'function($injector) {

      return $injector.instantiate(constructor);

    }]);

}

从源代码中你可以看到,service仅仅是调用了factory函数,而后者又调用了provider函数。事实上,AngularJS也为一些值、常量和装饰提供额外的provider封装,而这些并没有导致类似的困惑,它们的文档都非常清晰。

由于service仅仅是调用了factory函数,这有什么区别呢?线索在$injector.instantiate:在这个函数中,$injector在service的构造函数中创建了一个新的实例。

以下是一个例子,展示了一个service和一个factory如何完成相同的事情:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

var app = angular.module('app',[]);

 

app.service('helloWorldService'function(){

    this.hello = function() {

        return "Hello World";

    };

});

 

app.factory('helloWorldFactory'function(){

    return {

        hello: function() {

            return "Hello World";

        }

    }

});

当helloWorldService或helloWorldFactory被注入到控制器中,它们都有一个hello方法,返回”hello world”。service的构造函数在声明时被实例化了一次,同时factory对象在每一次被注入时传递,但是仍然只有一个factory实例。所有的providers都是单例。

既然能做相同的事,为什么需要两种不同的风格呢?相对于service,factory提供了更多的灵活性,因为它可以返回函数,这些函数之后可以被新建出来。这迎合了面向对象编程中工厂模式的概念,工厂可以是一个能够创建其他对象的对象。

1

2

3

4

5

6

7

8

9

app.factory('helloFactory'function() {

    return function(name) {

        this.name = name;

 

        this.hello = function() {

            return "Hello " + this.name;

        };

    };

});

 
点击QQ沟通