`
PIEr_mx
  • 浏览: 741 次
  • 性别: Icon_minigender_1
  • 来自: 成都
最近访客 更多访客>>
社区版块
存档分类
最新评论

[译]AngularJs 编码风格指导

阅读更多

AngularJs 团队编码风格指南 (个人观点 by @john_papa

 

如果你正在寻找一个固定的关于语法和约定的编码风格并且正在构建AngularJs应用,那么就进来看看吧,以下包含的这些内容是基于我在AngularJs、展示、Pluralsight 培训课程和团队工作中获得的经验总结出来的。

 

写这篇编码指南的目的是提供一个关于如何构建AngularJs的引导。我将通过展示这些我正在用的一些约定来说明,更重要的是我为什么这样用他们。

 

很棒的社区和声明

 

独木不成林。我发现AngularJs社区是一个不可思议的社区,人人都很热情的分享他们的经验。正因如此一个朋友、AngularJs专家Todd Motto 和我才能才能合作完成很多风格和约定。我们在大部分内容上意见统一,以小部分有分歧。我建议你去看一下Todd's guidelines 来体会一下他的观点,来比较着学习。

 

我的很多风格来自与Ward Bell 一起的结对编程。最然我们不总是意见统一,但是我的朋友Ward's在某种程度上在本指南的最终完善提供了帮助。

 

用一个简单的程序来了解这个风格

这篇指南只说明了是什么,为什么,怎样做,但是我发现在实际练习中更加有帮助。这篇指南伴有一个简单的程序:你可以在modular文件夹下找到它地址。当然,它是开源的。

 

目录

 

  1. 单一职责
  2. 模块
  3. 控制器
  4. 服务
  5. 工厂
  6. 指令
  7. 为控制器预载入保证数据
  8. 手动依赖注入
  9. @
  10. 异常处理
  11. 命名
  12. 应用结构
  13. 模块化
  14. Angular $ 封装服务
  15. 注释
  16. AngularJs 文档
  17. 贡献
  18. 许可声明
 

单一职责

 

  • 规则1:每个文件定义一个组件。

       下面的例子定义了app模块和它的依赖项、控制器和工厂在同一个文件中。

          

/* 避免 */
angular
    .module('app', ['ngRoute'])
    .controller('SomeController' , SomeController)
    .factory('someFactory' , someFactory);

function SomeController() { }

function someFactory() { }

          

          相同的组件分别定义到同一个文件中。

          

/* 建议 */

// app.module.js
angular
    .module('app', ['ngRoute']);
/* 建议 */

// someController.js
angular
    .module('app')
    .controller('SomeController' , SomeController);

function SomeController() { }
/* 建议 */

// someFactory.js
angular
    .module('app')
    .factory('someFactory' , someFactory);

function someFactory() { }

 

 

回到目录

 

模块

  • 定义(亦称setter):声明模块不要用变量而是用setter语法
/* 避免 */
var app = angular.module('app', [
    'ngAnimate',
    'ngRoute',
    'app.shared'
    'app.dashboard'
]); 

        最好简单的使用getter语句

/* 建议*/
angular
    .module('app', [
    'ngAnimate',
    'ngRoute',
    'app.shared'
    'app.dashboard'
]);

 

  • Getter: 当使用模块时,避免使用定义变量的方式,而是用对应的getter语法

        为什么?:这会产生更多可读性强的代码,并且避免了变量的冲突和泄露(leaks)

/* 避免 */
var app = angular.module('app');
app.controller('SomeController' , SomeController);

function SomeController() { }
/* 建议 */
angular
  .module('app')
  .controller('SomeController' , SomeController);

function SomeController() { }

 

  • Setting VS Getting:一次声明多处获取

       为什么?:一个模块只能被创建一次,然后可以从上一个时间点获取到。

              ·使用angular.module('app‘, []);来声明一个模块

              ·使用angular.module('app');来获取一个模块

  • 命名函数和匿名函数:使用命名函数而不是传递一个匿名函数作为回调

       为什么?:代码可读性强,更易于调试,并且减少了嵌套的回调函数的数量。

 

/* 避免 */
angular
  .module('app')
  .controller('Dashboard', function () { });
  .factory('logger', function () { });

 

 

/* 建议 */
// dashboard.js
angular
  .module('app')
  .controller('Dashboard', Dashboard);

function Dashboard () { }
// logger.js
angular
  .module('app')
  .factory('logger', logger);

function logger () { }

 

  •  IIFE:把AngularJs组件包装在立即执行的函数表达式中(IIFE)

        为什么?:一个IIFE移除了全局作用域中的变量,也会避免变量冲突。

 

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

  function logger () { }
})();

 

  •  注意:为简单起见,指南剩下的例子中会省略IIFE语法

 

控制器

  • controllerAs 视图语法:使用controllerAs语句替代传统的带$scope的控制器语句

       为什么?:控制器已经被创建了,提供了一个新的单例,并且controllerAs语法比传统的$scope语法更接近Javascript构造器。

 

       为什么?:你可以在视图中绑定一个“点”访问的对象(例:用customer.name代替name),这样看起来和上下文关联更紧密,更易读,并且避免了可能因为缺少“点"而出现的引用问题。

       为什么?:避免在嵌套控制器视图中调用$parent。

 

<!-- 避免 -->
<div ng-controller="Customer">
  {{ name }}
</div>
<!-- 建议 -->
<div ng-controller="Customer as customer">
  {{ customer.name }}
</div>

 

 

 

  •  controllerAs语法在控制器中使用this,它是和$scope绑定的

          为什么?:controllerAs是代替$scope的语法糖。你仍然可以绑定到视图,使用$scope函数。

 

          为什么?:当$scope方法可以不用或者可以写在factory中时,帮助你避免在控制器中使用它。考虑在factory中使用$scope或者在需要时使用在controller中。例如,当使用$emit、$broadcast或者$on发布和订阅事件时,考虑把这些使用移动到factory中,然后再controller中执行。

 

/* 避免 */
function Customer ($scope) {
  $scope.name = {};
  $scope.sendMessage = function () { };
}
/* 建议 */
function Customer ($scope) {
  $scope.name = {};
  $scope.sendMessage = function () { };
}
  • controllerAs中使用vm:当使用controllerAs语法时,使用this的捕获变量。选择一个始终一致的变量名例如vm,代表ViewModel(视图模型)。

          为什么?:关键词this是上下文相关的,当在controller内部的一个函数中使用时可能会改变它的上下文。在一开始捕获this的上下文可以避免这个问题。

 

/* 避免 */
function Customer () {
  this.name = {};
  this.sendMessage = function () { };
}
/* 建议 */
function Customer () {
  var vm = this;
  vm.name = {};
  vm.sendMessage = function () { };
}
  •  注意:通过在这行代码上面加注释,你可以你可以避免一些jshin的t警告。

 

 

 

 

/* jshint validthis: true */
var vm = this;

 

  •  绑定的成员变量放到最前面:把要绑定的成员变量有序地放到控制器的最开始位置,不要分散到controller的实现过程中。

          为什么?:把绑定的成员放到最前面使它读起来容易并且帮助你立刻看出那个成员变量可以被绑定或者用在视图中。

 

          为什么?:写一个匿名函数很容易,但那时当这些函数的内容超过一行时会使函数的可读性降低。在下面定义命名函数,然后再上面通过成员变量的方式进行赋值可以提高程序可读性。

 

/* 避免 */
function Sessions() {
    var vm = this;

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

 

 

/* 建议 */
function Sessions() {
    var vm = this;

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

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

    function gotoSession() {
      /* */
    }

    function refresh() {
      /* */
    }

    function search() {
      /* */
    }
  •  延迟控制器逻辑:通过委托给services和factories来延迟控制器中的逻辑

          为什么?:逻辑写在服务中通过函数暴露出来可以被多个控制器重用。

 

          为什么?:service中的逻辑可以更容易被在单元测试中被隔离,虽然可调用的逻辑可以在控制器中被模拟。

 

          为什么?:从控制器总移除依赖并隐藏并隐藏执行细节

 

/* 避免 */
function Order ($http, $q) {
  var vm = this;
  vm.checkCredit = checkCredit;
  vm.total = 0;

  function checkCredit () { 
    var orderTotal = vm.total;
    return $http.get('api/creditcheck').then(function (data) {
        var remaining = data.remaining;
        return $q.when(!!(remaining > orderTotal));
    });
  };
}

 

/* 建议 */
function Order (creditService) {
  var vm = this;
  vm.checkCredit = checkCredit;
  vm.total = 0;

  function checkCredit () { 
    return creditService.check();
  };
}
  •  分配控制器:当一个控制器必须配有一个视图并且每一个组件都可能会被被其他控制器或者views重用,在它的route下单独定义控制器

         ·注意:如果一个视图通过其他途径加载,则可以使用ng-controller="Avengers as vm"语法

        为什么?:将route中的控制器配对允许不同的routes去执行不同的控制器和视图。当使用ng-controller把控制器分配到视图中时,视图总是会和同一个控制器相关联。

 

 

 

/* 避免 - when using with a route and dynamic pairing is desired */
// route-config.js
angular
  .module('app')
  .config(config);

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

 

<div ng-controller="Avengers as vm">
</div>

 

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

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

 

<div>
</div>

 

 

 服务(Service)

  • 单例:服务使用new关键字实例化,并使用this关键字来调用公共方法和变量。我一贯建议使用Factory。
  • 注意:所有的AngularJs的Service都是单例。这意味着在每个注入器中,一个Service只有一个实例。
// 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)

  • 单一职责原则(SRP):Factory应该具有单一职责,根据上下文封装。一个Factory不能有多个目的,你要另外创建一个Factory才行。
  • 单例:Factory是一个单例,并返回一个包含服务的成员的对象。
  • 注意:所有的AngularJs服务都是单例。
  • 公共成员定义在代码段顶部:使用从Revealing Module Pattern派生的技术,把可服务中调用的公共成员写在代码段顶部。

        为什么?:这样可以使代码更具可读性,并且帮助你马上找出服务的哪些成员可以被调用和进行单元测试(或者叫它模拟)

        为什么?:当代码过长的时候很有用,可以避免通过下拉滚动条来找到公共成员。

        为什么?:随处定义代码可能轻松些,但是当这些函数超过一行,就会降低可读性还有无谓的拖动滚动条。使用带返回值的服务把课调用的接口的内容定义在底部,变量的定义放在顶部,更具可读性。

 

/* 避免 */
function dataService () {
  var someValue = '';
  function save () { 
    /* */
  };
  function validate () { 
    /* */
  };

  return {
    save: save,
    someValue: someValue,
    validate: validate
  };
}
 
/* 建议 */
function dataService () {
  var someValue = '';
  var service = {
    save: save,
    someValue: someValue,
    validate: validate
  };
  return service;

  ////////////
  function save () { 
    /* */
  };

  function validate () { 
    /* */
  };
}
 
  • This way bindings are mirrored across the host object, primitive values cannot update alone using the revealing module pattern(译者:求大神翻译==!)
  •  通过定义函数来隐藏细节代码:可访问的成员变量写在Factory代码段的顶部。函数的具体实现写在后面。

        为什么?:把var a 移到var b前面程序会坏掉因为a 依赖b?像上面这样做你将不会再因此烦恼。

        为什么?:函数表达式中顺序是有影响的。

 

/**
 * 避免
 * 使用函数表达式
 */
 function dataservice($http, $location, $q, exception, logger) {
    var isPrimed = false;
    var primePromise;

    var getAvengers = function() {
      // implementation details go here
    };

    var getAvengerCount = function() {
      // implementation details go here
    };

    var getAvengersCast = function() {
      // implementation details go here
    };

    var prime = function() {
      // implementation details go here
    };

    var ready = function(nextPromises) {
      // implementation details go here
    };

    var service = {
        getAvengersCast: getAvengersCast,
        getAvengerCount: getAvengerCount,
        getAvengers: getAvengers,
        ready: ready
    };

    return service;
}
 
/**
 * 建议
 * 使用函数声明
 * 并且把可用的变量写在顶部
 */
function dataservice($http, $location, $q, exception, logger) {
    var isPrimed = false;
    var primePromise;

    var service = {
        getAvengersCast: getAvengersCast,
        getAvengerCount: getAvengerCount,
        getAvengers: getAvengers,
        ready: ready
    };

    return service;

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

    function getAvengers() {
      // implementation details go here
    }

    function getAvengerCount() {
      // implementation details go here
    }

    function getAvengersCast() {
      // implementation details go here
    }

    function prime() {
      // implementation details go here
    }

    function ready(nextPromises) {
      // implementation details go here
    }
}

 

 

 数据服务(Data Services)

 

  • 分离数据获取过程:把数据操作和交互交给Factory。让数据服务(Data Services)来负责XHR调用、本地内存、或者其它的数据操作。
        为什么?:控制器的责任是展示和收集视图中的信息。它不需要关心怎么获取源数据,只需要知道从哪获取这些数据。分离数据获取过程可以让控制器更加简洁并专注于视图。
        为什么?:这种方式让对数据的测试(模拟或者真实)更轻松。
        为什么?:数据服务的实现可能带有非常具体的数据源操作。有可能包括http头、怎样和数据交互或者其他服务如$http。分离控制器(或其它地方)中这样的逻辑到数据服务中去并把它们放在一个地方更加利于改变代码细节。
/* 建议 */

// 数据服务Factory
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);
        }
    }
}
  •  注意:下面为你展示对上面Factory的调用

 

/* 建议 */

// 控制器调用dataservice
angular
    .module('app.avengers')
    .controller('Avengers', Avengers);

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

function Avengers(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;
          });
    }
}
  •  从数据调用返回一个Promise:当调用数据服务时使用了会返回Promise的服务比如$http,也让它作为你调用的函数的返回值。

        为什么?:你可以把这些Promise连在一起,然后当数据调用完成并且Promise被解决或者拒绝后进行下一步操作。

 

/* 建议 */

activate();

function activate() {
    /**
     * 第一步
     * 向getAvengers函数索要avenger数据并等待Promise
     */
    return getAvengers().then(function() {
      /**
       * 第四步
       * 在最后一个Promise完成后做些什么事情
       */
      logger.info('Activated Avengers View');
    });
}

function getAvengers() {
    /**
     * 第二步
     * 向数据服务索要数据并等待Promise
     */
    return dataservice.getAvengers()
      .then(function (data) {
          /**
           * 第三步
           * 设置数据并解决Promise
           */
          vm.avengers = data;
          return vm.avengers;
      });
}
(译者:未完待续。。。)
由于译者能力有限,有不足之处,请看官慷慨指明。
分享到:
评论

相关推荐

    Angularjs 合集 Angularjs 合集

    这个合集包含了三本书籍,分别是《AngularJS by Example》、《AngularJS Web Application Development Cookbook》以及《Apress.Pro.AngularJS》,它们分别从不同的角度深入探讨了AngularJS的使用和开发。 ...

    AngularJS书两本

    AngularJS,作为一款由Google维护的前端JavaScript框架,自2010年发布以来,一直在Web开发领域占据重要地位。这两本书《AngularJS_权威教程》和《AngularJS_深度剖析与最佳实践》将帮助我们深入理解并掌握这一强大的...

    AngularJS 风格指南.pdf

    **AngularJS 风格指南** 是一份详细指导开发者如何高效、规范地使用AngularJS框架进行Web应用开发的文档。AngularJS,作为一个强大的MVC(Model-View-Controller)JavaScript框架,广泛应用于现代前端开发,它提供了...

    angularJs权威和精通angularjs

    AngularJS,作为一款由Google维护的前端JavaScript框架,自2010年发布以来,一直在Web开发领域占据着重要的地位。它以其强大的双向数据绑定、依赖注入和模块化设计,极大地提高了开发效率,使得构建复杂的单页应用...

    angularjs-style-guide-github

    AngularJS的代码风格指南是用来指导开发者如何编写一致、可维护、高效的代码的一套规则和建议。本知识点将详细介绍一个在GitHub上广泛流传的AngularJS风格指南——mgechev/angularjs-style-guide的内容。 ### 标题...

    AngularJS Eclipse 1.2.0 插件下载

    1. **代码自动完成**:在编写AngularJS代码时,插件会提供智能提示,帮助开发者快速输入指令、属性和服务名等,提高编码速度和准确性。 2. **语法高亮和错误检查**:Eclipse会根据AngularJS语法规则进行实时高亮...

    AngularJS中文版pdf+精通AngularJS PDF

    两本书的pdf:AngularJS中文版+精通AngularJS,提供方式:百度云链接+密码

    AngularJS学习 chm 文件

    AngularJS,是由Google维护的开源JavaScript框架,用于构建单页应用程序(SPA)。它极大地扩展了HTML的功能,使得在客户端能够更高效地处理数据和逻辑,提供了丰富的开发工具和API,帮助开发者构建动态、交互性强的...

    speedseed-styleguide-angularjs:AngularJS 风格指南

    AngularJS 风格指南指数模块化启动逻辑角度包装服务 $ 测试动画注释常数模板和片段路由任务自动化 开始 有关 jade、stylus 和 js (es6) + 使用 gulp 启动项目的模板的参考指南: ... - ...

    angularjs管理系统框架

    5. **自定义**:开发者可以根据需求自定义 Bootstrap 的样式和配置,以适应项目风格。 ### 自主设计开发的插件 此模板中的插件可能包括以下部分: 1. **数据表格**:自定义的数据展示组件,可能包含排序、过滤、...

    angularjs_patterns:各种angularjs编码模式

    各种angularjs编码模式 火力基地 一个如何使用angularfire访问firebase数据库的示例,它演示了:获取所有记录-$ firebasearray服务添加记录-$ fb.push删除记录-$ remove manual_bootstrap 演示了如何手动引导角度...

    angularJs demo 各种基本用法

    AngularJS 是一个强大的JavaScript框架,主要用于构建动态web应用。它通过数据绑定和依赖注入机制,极大地简化了前端开发。在"angularJs demo 各种基本用法"中,我们可以深入探讨以下几个关键概念:路由(router)、...

    AngularJS的介绍与研究中文PPT版

    与jquery等前端框架不同,AngularJS不仅仅是一个工具,而是一个可以指导我们开发的框架。AngularJS通过数据视图双向绑定、模块化、依赖注入等方式来帮助我们开发、管理前端项目。AngularJS是协助搭建 资源太大,传...

    AngularJs 1.5.8

    AngularJS 1.5.8 是一个非常重要的版本,它是Google开发的JavaScript框架AngularJS的稳定版本之一。这个框架主要用于构建动态web应用,通过MVC(Model-View-Controller)架构模式来简化前端开发,提供了丰富的特性以...

    AngularJS开发ASP.NET MVC

    ### AngularJS 开发 ASP.NET MVC #### 知识点概览 - **AngularJS与ASP.NET MVC结合的优势** - **AngularJS的特点与优势** - **AngularJS的基本概念(如数据绑定、指令等)** - **如何在ASP.NET MVC项目中引入...

    Mastering Web Application Development with AngularJS

    《精通AngularJS》深入浅出地讲解了AngularJS的开发概念和原理,并通过丰富的开发实例向读者展示了构建复杂应用的完整过程,包括学习使用AngularJS特有的基于DOM的模板系统,实现复杂的后端通信,创建漂亮的表单,...

    AngularJS高级程序设计

    AngularJS是一款优秀的前端JS框架,已经被用于Google的多款产品当中,并且得到众多前端工程师的青睐。, 《AngularJS高级程序设计》是AngularJS程序设计的高级指南。全书共25章,分为3个部分,每部分覆盖一组相关话题...

    (精通AngularJS)Mastering Web Application Development with AngularJS code 源代码

    (精通AngularJS)Mastering Web Application Development with AngularJS code 源代码

    最新AngularJS开发宝典视频教程 后盾网AngularJS培训视频教程 后盾网.txt

    &lt;最新AngularJS开发宝典视频教程&gt; ├最新AngularJS开发宝典—第001讲 什么是Angular及与Jquery实例对比分析.mp4 ├最新AngularJS开发宝典—第002讲 模块、控制器、视图模型、双向数据绑定模型、双向数据绑定.mp4 ├...

    Angularjs_in_action

    ### AngularJS概述与基础知识 #### 一、AngularJS简介 AngularJS是一款由Google维护的开源JavaScript框架,旨在通过HTML扩展来解决单页面应用(SPA, Single Page Application)开发中的常见问题。它允许开发者以...

Global site tag (gtag.js) - Google Analytics