[关闭]
@Belinda 2015-11-13T00:09:58.000000Z 字数 7724 阅读 1572

angular 编程风格引导(一)

未分类


一个好的编程风格有助于团队的协同开发,所以在做 angular 开发时,我们也有一些约定,本文章主要是针对于使用 angular 和 coffeescript 编程的团队

1.单一职责

原则1:一个文件只能定义一个组件
下面的例子定义了app模块和他的依赖,把控制器和服务都定义到一个文件了

--不推荐--
SomeController = ()->
someFactory = ()->
angular
    .module('app', ['ngRoute'])
    .controller('SomeController' , SomeController)
    .factory('someFactory' , someFactory)

同样的组建,现在我们把它分解到他们自己的文件中

--推荐方式--
--app.module.js--

angular
    .module('app', ['ngRoute'])
--推荐方式--
--someController.js--
SomeController = ()->
angular
    .module('app')
    .controller('SomeController' , SomeController)

--推荐方式--
--someFactory.js--
someFactory = ()->

angular
    .module('app')
    .factory('someFactory' , someFactory)

2.模块

原则2:定义模块的时候不要用变量来定义,用设置的语法来定义

因为我们使用的单一职责的原则,每一个组建一个文件,在定义组建的时候你已经用angular.module来介绍这个组建是哪个模块的了。

    -- 不推荐 --
    app = angular.module('app', [
        'ngAnimate'
        'ngRoute'
        'app.shared'
        'app.dashboard'
    ])

下面是不用变量的语法

    -- 推荐方式 --
    angular
        .module('app', [
        'ngAnimate'
        'ngRoute'
        'app.shared'
        'app.dashboard'
    ])

拓展:当使用模块的时候,不要用变量,要用链式定义的语法

这样做能够使代码的可读性更高,同时也能避免变量的泄露和碰撞(重名)

    -- 不推荐--
    app = angular.module('app')
    app.controller('SomeController' , SomeController)
    SomeController = ()->

    --推荐方式--
    SomeController = ()->

    angular
      .module('app')
      .controller('SomeController' , SomeController)

设置 vs 获取所有的实例只要设置一次

一个模块只要被创建一次,之后的模块获取只要通过这个切入点来获取就可以了

命名函数 vs 匿名函数:使用命名函数来代替向回掉函数传递匿名函数

这样能够是代码的可读性更高,更加容易debug,减少回调函数的桥套

    -- 不推荐 --
    angular
      .module('app')
      .controller('Dashboard', ()->)
      .factory('logger', ()-> )

    --推荐方式--
    --dashboard.js--
    Dashboard = ()->
      # logic goes here -->
      return

    angular
      .module('app')
      .controller('Dashboard', Dashboard)

    --推荐方式--
    -- logger.js --
    logger = ()->
      # logic goes here -->
      return

    angular
      .module('app')
      .factory('logger', logger)

IIFE:把angular 组建包裹在能够马上调用的函数表达式中

IIFE 把变量从全局作用域里解放出来,这样做能防止把变量和函数定义在全局作用句中从而造成变量的碰撞()变量重名造成的莫名其妙的bug)

    (->
      logger = ()->
        # logic goes here -->
        return

      angular
        .module('app')
        .factory('logger', logger);

    )()

主意:为了使代码更加简洁,下面的编程先省略 IIFE 语法

3.控制器

controller as view 语法:controllerAs语法来代替经典的把controller 绑定到 $scope 作用域的语法

  • 控制器是一个构造类,需要通过newed来创建一个新的实例,但是controllerAS语法更像 javascript 的构造函数

  • 这样的用法能够促进我们在视图中绑定对应对象的变量( 用customer.name来取代name)等等,这样能够是我们的代码更易读,避免我们没有指定对象的时候的一些参考问题。

  • 能够让我们避免在视图中的嵌套控制器中使用$parent来调用父级控制器

-- 不推荐 --
<div ng-controller="Customer">
  {{ name }}
</div>
-- 推荐方式 --
<div ng-controller="Customer as customer">
  {{ customer.name }}
</div>

controllerAs Controller Syntax:用controllerAs来代替传统的将控制器绑定到$scope的语法

controllerAS with vm当使用controllerAs语法时,用一个变量来代替 this ,找一个统一的变量名来代替视图模型例如 vm

this关键字是联系上下文的,当在控制器中使用函数的时候可能会改变上下文,为了避免这样的情况,最好用一个变量来捕获this

    --不推荐--
    (->
      Customer = ()->
        @name = {}
        @sendMessage = ()->
          # here @/this is not the same
          @stuff = "stuff"

        return
      angular
        .module('app')
        .controller('Customer', Customer)
    )()


    --推荐方式--
    (->
      Customer = ()->
        vm = @
        vm.name = {}
        vm.sendMessage = ()->

        return
      angular
        .module('app')
        .controller('Customer', Customer)
    )() 

【tip】

    ### OR use the fat arrow in functions => ###
    (->
      Customer = ()->
        @name = {}
        @sendMessage = ()=>
          @stuff

        return
      angular
        .module('app')
        .controller('Customer', Customer)
    )()

【note】:你可以把下面这段代码放到你的代码的最前一行,来避免一些组建的语法检查

    ### jshint validthis: true ###
    vm = @

把变量放在控制器的最前面
把数据绑定的变量成员(按照字母顺序)放在控制器的最前面,而不是遍布在整个控制器中

把数据绑定的变量成员放在控制器的最前面,使程序更加易读,帮你立即分辨控制器中这个变量成员可以绑定在视图中

    -- 不推荐 --
    function SessionsController() {
        var vm = this;

        vm.gotoSession = function() {
          /* ... */
        };
        vm.refresh = function() {
          /* ... */
        };
        vm.search = function() {
          /* ... */
        };
        vm.sessions = [];
        vm.title = 'Sessions';
    }

    -- 推荐方式 --
    function SessionsController() {
        var vm = this;

        vm.gotoSession = gotoSession;
        vm.refresh = refresh;
        vm.search = search;
        vm.sessions = [];
        vm.title = 'Sessions';

        ////////////

        function gotoSession() {
          /* */
        }

        function refresh() {
          /* */
        }

        function search() {
          /* */
        }
    }

【note】 如果函数是一行的就把函数也放在控制器的前面,这样做对代码的阅读性不会有影响

    /* 不推荐*/
    function SessionsController(data) {
        var vm = this;

        vm.gotoSession = gotoSession;
        vm.refresh = function() {
            /**
             * lines
             * of
             * code
             * affects
             * readability
             */
        };
        vm.search = search;
        vm.sessions = [];
        vm.title = 'Sessions';
    }


    /* 推荐方法*/
    function SessionsController(sessionDataService) {
        var vm = this;

        vm.gotoSession = gotoSession;
        vm.refresh = sessionDataService.refresh; // 1 liner is OK
        vm.search = search;
        vm.sessions = [];
        vm.title = 'Sessions';
    }

函数声明,隐藏实现细节

函数声明,隐藏实现的细节。把用于数据绑定的变量成员的声明,放在控制器的前面,实现放在文件的后面。

主意上面的例子中把重要的函数声明分散在控制器中,但在下面的例子中重要的函数声明都被提升到了顶部。举个栗子,函数绑定变量如vm.avengersvm.title,把函数实现的细节放在后面,这样更加有利于代码的阅读

    /*  推荐方法 */
    function AvengersController(avengersService, logger) {
        var vm = this;
        vm.avengers = [];
        vm.getAvengers = getAvengers;
        vm.title = 'Avengers';

        activate();

        function activate() {
            return getAvengers().then(function() {
                logger.info('Activated Avengers View');
            });
        }

        function getAvengers() {
            return avengersService.getAvengers().then(function(data) {
                vm.avengers = data;
                return vm.avengers;
            });
        }
    }

把控制器的逻辑定义放到服务中

使控制器变得专注

为一个视图定义一个控制器,不要讲一个控制器为多个视图复用,要把复用的逻辑封装到服务中,保证一个控制器只专注于他的视图

给多个视图复用的控制器是很脆的,一个好的 E2E测试覆盖率是用来确定一个应用的稳定性的

路由分配

当一个控制器必须和某个视图绑定但也有可能和其他视图或者控制器复用,把控制器的定义和路由条状一起定义

【note】如果一个视图不是通过路由而是通过其他方式加载的,请用ng-controller="Avengers as vm"语法

在路由中分配控制器,允许不同的路由去调用不同的控制器和视图,当控制器是在视图中通过ng-controller 进行声明,那么这个视图就要和这个控制器一直关联

    /* 不推荐 */

    // route-config.js
    angular
        .module('app')
        .config(config);

    function config($routeProvider) {
            $routeProvider
            .when('/avengers', {
              templateUrl: 'avengers.html'
            });
    }

    <!-- avengers.html -->
    <div ng-controller="AvengersController as vm">
    </div>



    /* 推荐方式 */

    // route-config.js
    angular
        .module('app')
        .config(config);

    function config($routeProvider) {
            $routeProvider
            .when('/avengers', {
                templateUrl: 'avengers.html',
                controller: 'Avengers',
                controllerAs: 'vm'
            });
    }
    <!-- avengers.html -->
    <div>
    </div>

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注