zoukankan      html  css  js  c++  java
  • AngularJS 常用模块书写建议

    本文是依据 Angular Style Guide 对 Angular 常用模块书写建议的翻译和总结,仅供参考。

    IIFE

    使用 立即执行函数表达式(Immediately Invoked Function Expression)将 Angular 组件包裹起来,防止污染全局作用域 Style Y010 。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    /* avoid */
    // storage.js
    angular
    .module('app')
    .factory('storage', storage);

    // storage function is added as a global variable
    function storage() { }

    /**
    * recommended
    *
    * no globals are left behind
    */
    // storage.js
    (function() {
    'use strict';

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

    function storage() { }
    })();

    Modules

    使用 setter 定义 Modules ,避免使用变量 [Style Y021]。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /* avoid */
    var app = angular.module('app', [
    'ngAnimate',
    'ngRoute',
    'app.shared',
    'app.dashboard'
    ]);
    /* recommended */
    angular
    .module('app', [
    'ngAnimate',
    'ngRoute',
    'app.shared',
    'app.dashboard'
    ]);

    使用链式的 getter 来获取 Modules ,避免使用变量 [Style Y022] 。尽量不直接使用匿名函数,而是把一个函数名作为回调传进去 [Style Y024] 。

    1
    2
    3
    4
    5
    angular
    .module('app')
    .controller('SomeController', SomeController);

    function SomeController() { }

    Controller

    使用 controllerAs (和 vm 一起)

    因为 this 是上下文相关的,为了避免 Controlller 内部的函数在使用 this 时导致上下文改变,应该在一开始使用一个变量(最好统一为 viewModel 的缩写 - vm)来捕获 this [Style Y032] 。

    建议书写方式:

    1
    2
    3
    4
    5
    function CustomerController() {
    var vm = this;
    vm.name = {};
    vm.sendMessage = function() {};
    }

    使用 controllerAs 语法,在把 Controller 和 View 配对 [Style Y038] 时,使用这种方式:

    1
    2
    3
    4
    5
    6
    7
    8
    function config($routeProvider) {
    $routeProvider
    .when('/avengers', {
    templateUrl: 'avengers.html',
    controller: 'Avengers',
    controllerAs: 'vm'
    });
    }

    仅在需要使用 $emit、 $broadcast、 $on、 $watch 等 $scope 下的方法,再使用 $scope[Style Y031] ,书写方式如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function SomeController($scope, $log) {
    var vm = this;
    vm.title = 'Some Title';

    $scope.$watch('vm.title', function(current, original) {
    $log.info('vm.title was %s', original);
    $log.info('vm.title is now %s', current);
    });
    }

    把页面绑定成员放在上面

    把可绑定成员放在 Controller 最前面一部分,按字母顺序排列,并且不让代码蔓延。这样能让代码更易读、易查找 [Style Y33] 。

    虽然类似于下面这种写法很简便,但是那些超过一行代码的函数会降低可读性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /* avoid */
    vm.refresh = function() {
    /**
    * lines
    * of
    * code
    * affects
    * readability
    */
    };

    建议书写方式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /* recommended */
    function SessionsController() {
    var vm = this;

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

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

    function gotoSession() {
    /* */
    }

    function search() {
    /* */
    }
    }

    其他

    • 将 Controller 中的部分逻辑放在 Service 或 Factory 中,保持 Controller 的简洁 [Style Y035]
    • 不要对多个 Views 使用同一 Controller,如果有可复用代码,应该放到 Factory 中,保持 Controller 专注于它自己的 View [Style Y037]

    Service && Factory

    Service

    Angular 中的 Service 会通过 new 关键字被实例化,其中的方法和属性会被直接添加在 this 上。因此,通常可以使用 Factory 代替 Service [Style Y040] 。

    所有的 Angular Service 都是单例的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // service
    angular
    .module('app')
    .service('logger', logger);

    function logger() {
    this.logError = function(msg) {
    /* */
    };
    }

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

    function logger() {
    return {
    logError: function(msg) {
    /* */
    }
    };
    }

    Factory

    Factory 的创建应该符合单一职责原则 [Style Y050] ,和 Service 一样,Factory 也是单例的,它返回一个包含 Service 中成员的对象,Factory 和 Service 的区别可参见 AngularJS 中 Provider 们 一文。

    建议将 Factory 中可访问的成员放在顶部 [Style Y052] :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function logger($log) {
    var service = {
    error: error,
    info: info
    }
    return service;

    ////////////
    function error() {
    /* */
    };
    function info() {
    /* */
    };
    }

    Data Service

    将产生数据和与数据交互的操作放在一个 DataService 的 Factory 中,让其负责 XHR 调用、local storage 等任何与数据相关的操作。这样能让 Controller 专注于展示和为 View 层收集信息上 [Style Y060] 。

    Controller 不需要关心数据是怎么得到的,而只应该知道从谁那里拿数据。

    一种建议的 dataservice 书写方式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    angular
    .module('app.core')
    .factory('dataservice', dataservice);

    dataservice.$inject = ['$http', 'logger'];

    function dataservice($http, logger) {
    return {
    getAvengers: getAvengers
    };

    function getAvengers() {
    return $http.get('/api/maa')
    .then(getAvengersComplete)
    .catch(getAvengersFailed);

    function getAvengersComplete(response) {
    return response.data.results;
    }

    function getAvengersFailed(error) {
    logger.error('XHR Failed for getAvengers.' + error.data);
    }
    }
    }

    当调用一个返回 promise 的 dataservice 时,在调用函数中,也返回一个 promise,方便后续的链式处理 [Style Y061]。 调用 dataservice 的 Controller 的写法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    angular
    .module('app.avengers')
    .controller('AvengersController', AvengersController);

    AvengersController.$inject = ['dataservice', 'logger'];

    function AvengersController(dataservice, logger) {
    var vm = this;
    vm.avengers = [];

    activate();

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

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

    Directive

    将跟 DOM 相关的操作都放在 Directive 中。在能使用 CSS 设置样式,使用 animation services 设置动画及使用 Angular templating、ngShow 或者 ngHide 的情况下,尽量避免使用 Directive [Style Y072] 。

    为每个指令单独创建一个文件,这样能方便跨应用共享,并且便于查找 [Style Y070]。还有,为指令提供一个简短唯一的前缀 [Style Y072] 。

    将指令限定为 Elements 和 Attributes,这(EA) 在 Angular 1.3+ 中已经是默认设置 [Style Y074] 。

    为保持一贯性,在 Directive 中同样应该使用 controllerAs 语法来将 Controller 和 View 配对 [Style Y075] 。由于 Directive 的 Controller 是在 Directive 闭包外面的,所以,如果想将外层 scope 和 Directive 中 Controller 的 scope 绑定,(Angular 1.3+)可以设置 bindToController = true[Style Y076]。

    下面是一个完整的示例。

    主文件:

    1
    <div my-example max="77"></div>

    example.directive.js 文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    angular
    .module('app')
    .directive('myExample', myExample);

    function myExample() {
    var directive = {
    restrict: 'EA',
    templateUrl: 'app/feature/example.directive.html',
    scope: {
    max: '='
    },
    controller: ExampleController,
    controllerAs: 'vm',
    bindToController: true
    };

    return directive;
    }

    function ExampleController() {
    var vm = this;
    vm.min = 3;
    console.log('CTRL: vm.min = %s', vm.min);
    console.log('CTRL: vm.max = %s', vm.max);
    }

    example.directive.html 文件:

    1
    2
    3
    <div>hello world</div>
    <div>max={{vm.max}}<input ng-model="vm.max"/></div>
    <div>min={{vm.min}}<input ng-model="vm.min"/></div>

    Dependency Injection

    由于AngularJS是通过构造函数的参数名字来推断依赖服务名称的。所以如果要压缩JS代码,它所有的参数也同时会被压缩,这时候依赖注入系统就不能正确的识别出服务了 [Style Y090] 。

    有两种方法可以解决这个问题:

    方法一

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    angular
    .module('app')
    .controller('DashboardController',
    ['$routeParams', 'dataservice',
    function Dashboard($routeParams, dataservice) {}
    ]);
    ```

    方法二

    ```javascript
    angular
    .module('app')
    .controller('DashboardController', DashboardController);

    DashboardController.$inject = ['$routeParams', 'dataservice'];

    function DashboardController($routeParams, dataservice) {
    }

    从易于阅读的角度考虑,建议第二种 [Style Y091] 。

    当然,如果使用自动化构建工具 Gulp 或 Grunt 的话,还有一种更好的办法,使用 ng-annotate,自动生成 DashboardController.$inject 部分的代码 [Style Y100] ,如下:

    1
    2
    3
    4
    5
    6
    7
    angular
    .module('app')
    .controller('DashboardController', DashboardController);

    /* @ngInject */
    function DashboardController($routeParams, dataservice) {
    }
  • 相关阅读:
    [Redis-CentOS7]Redis设置连接密码(九)
    [Redis-CentOS7]Redis数据持久化(八)
    PAT Advanced 1101 Quick Sort (25分)
    PAT Advanced 1043 Is It a Binary Search Tree (25分)
    HTTP协议
    PAT Advanced 1031 Hello World for U (20分)
    自然码双拼快速记忆方案
    macOS 原生输入法设置自然码
    PAT Advanced 1086 Tree Traversals Again (25分)
    PAT Advanced 1050 String Subtraction (20分)
  • 原文地址:https://www.cnblogs.com/ilinuxer/p/5240562.html
Copyright © 2011-2022 走看看