angularjs styleguide

2025-12-07 0 536

angularjs styleguide (ES2015)

使用AngularJS 1.6最佳实践最新。架构,文件结构,组件,单向数据流,生命周期钩。


想要一个示例结构作为参考吗?查看我的基于组件的体系结构1.5应用程序


@toddmotto的团队的明智风格指导

该体系结构和StyleGuide已从ES2015进行了重写,AngularJS 1.5+的变化将来将您的应用程序升级为Angular。本指南包括单程数据流,事件授权,组件体系结构和组件路由的新最佳实践。

您可以在这里找到旧的styleguide,以及在这里新的推理。

加入Ultimate Angularjs学习体验,以完全掌握初学者和高级AngularJS功能,以构建快速且扩展的现实世界应用程序。

目录

  1. 模块化体系结构
    1. 理论
    2. 根模块
    3. 组件模块
    4. 通用模块
    5. 低级模块
    6. 文件命名约定
    7. 可扩展的文件结构
  2. 成分
    1. 理论
    2. 支持的属性
    3. 控制器
    4. 单向数据流和事件
    5. 状态组件
    6. 无状态组件
    7. 路由组件
  3. 指令
    1. 理论
    2. 推荐的属性
    3. 常数或类
  4. 服务
    1. 理论
    2. 服务课程
  5. 样式
  6. ES2015和工具
  7. 国家管理
  8. 资源
  9. 文档
  10. 贡献

模块化体系结构

Angular应用中的每个模块都是模块组件。模块组件是封装逻辑,模板,路由和子女组件的该模块的根定义。

模块理论

模块中的设计直接映射到我们的文件夹结构,这使事物可维护和可预测。理想情况下,我们应该有三个高级模块:根,组件和常见。根模块定义了引导我们的应用程序的基本模块和相应的模板。然后,我们将组件和公共模块导入根模块中,以包括我们的依赖项。然后,组件和通用模块需要较低级别的组件模块,该模块包含每个可重复使用功能的组件,控制器,服务,指令,过滤器和测试。

回到顶部

根模块

根模块以一个根组件开头,该根组件为整个应用程序定义了基本元素,并使用ui-routerui-view显示了路由出口定义的示例。

 // app.component.js
export const AppComponent = {
  template : `
    <header>
        Hello world
    </header>
    <div>
        <div ui-view></div>
    </div>
    <footer>
        Copyright MyApp 2016.
    </footer>
  `
} ;

然后创建一个根模块,并在AppComponent导入并在.component(\'app\', AppComponent)中注册并注册。提出的进一步的子模块(组件和公共模块)包括与应用程序相关的所有组件。您会注意到此处的样式也正在进口,我们将在本指南的稍后章节中进行此操作。

 // app.module.js
import angular from \'angular\' ;
import uiRouter from \'angular-ui-router\' ;
import { AppComponent } from \'./app.component\' ;
import { ComponentsModule } from \'./components/components.module\' ;
import { CommonModule } from \'./common/common.module\' ;
import \'./app.scss\' ;

export const AppModule = angular
  . module ( \'app\' , [
    ComponentsModule ,
    CommonModule ,
    uiRouter
  ] )
  . component ( \'app\' , AppComponent )
  . name ;

回到顶部

组件模块

组件模块是所有可重用组件的容器参考。请参阅上面的内容,我们如何导入ComponentsModule并将它们注入根模块中,这为我们提供了一个单个地方,可以导入应用程序的所有组件。我们需要的这些模块与所有其他模块分离,因此可以轻松地将其移至任何其他应用程序中。

 // components/components.module.js
import angular from \'angular\' ;
import { CalendarModule } from \'./calendar/calendar.module\' ;
import { EventsModule } from \'./events/events.module\' ;

export const ComponentsModule = angular
  . module ( \'app.components\' , [
    CalendarModule ,
    EventsModule
  ] )
  . name ;

回到顶部

通用模块

通用模块是我们不想在另一个应用程序中使用的所有应用程序组件的容器参考。这可以是布局,导航和页脚。请参阅上面的方式,我们如何将CommonModule导入根模块中,这为我们提供了一个单个地方,可以将所有常见组件导入应用程序。

 // common/common.module.js
import angular from \'angular\' ;
import { NavModule } from \'./nav/nav.module\' ;
import { FooterModule } from \'./footer/footer.module\' ;

export const CommonModule = angular
  . module ( \'app.common\' , [
    NavModule ,
    FooterModule
  ] )
  . name ;

回到顶部

低级模块

低级模块是包含每个特征块逻辑的单个组件模块。这些每个人都将定义一个模块,将导入到一个高级模块,例如组件或公共模块,下面的一个示例。始终记住在创建模块时,而不是引用一个导出时,将.name后缀添加到每个export时。您会注意到此处也存在路由定义,我们将在本指南的稍后章节中进行此操作。

 // calendar/calendar.module.js
import angular from \'angular\' ;
import uiRouter from \'angular-ui-router\' ;
import { CalendarComponent } from \'./calendar.component\' ;
import \'./calendar.scss\' ;

export const CalendarModule = angular
  . module ( \'calendar\' , [
    uiRouter
  ] )
  . component ( \'calendar\' , CalendarComponent )
  . config ( ( $stateProvider , $urlRouterProvider ) => {
    \'ngInject\' ;
    $stateProvider
      . state ( \'calendar\' , {
        url : \'/calendar\' ,
        component : \'calendar\'
      } ) ;
    $urlRouterProvider . otherwise ( \'/\' ) ;
  } )
  . name ;

回到顶部

文件命名约定

保持简单且小写,使用组件名称,例如calendar.*.js* calendar-grid.*.js使用*.module.js作为模块定义文件,因为它使其保持冗长并与Angular保持一致。

 calendar.module.js
calendar.component.js
calendar.service.js
calendar.directive.js
calendar.filter.js
calendar.spec.js
calendar.html
calendar.scss

回到顶部

可扩展的文件结构

文件结构非常重要,这描述了可扩展且可预测的结构。一个示例文件结构,以说明模块化组件体系结构。

 ├── app/
│   ├── components/
│   │  ├── calendar/
│   │  │  ├── calendar.module.js
│   │  │  ├── calendar.component.js
│   │  │  ├── calendar.service.js
│   │  │  ├── calendar.spec.js
│   │  │  ├── calendar.html
│   │  │  ├── calendar.scss
│   │  │  └── calendar-grid/
│   │  │     ├── calendar-grid.module.js
│   │  │     ├── calendar-grid.component.js
│   │  │     ├── calendar-grid.directive.js
│   │  │     ├── calendar-grid.filter.js
│   │  │     ├── calendar-grid.spec.js
│   │  │     ├── calendar-grid.html
│   │  │     └── calendar-grid.scss
│   │  ├── events/
│   │  │  ├── events.module.js
│   │  │  ├── events.component.js
│   │  │  ├── events.directive.js
│   │  │  ├── events.service.js
│   │  │  ├── events.spec.js
│   │  │  ├── events.html
│   │  │  ├── events.scss
│   │  │  └── events-signup/
│   │  │     ├── events-signup.module.js
│   │  │     ├── events-signup.component.js
│   │  │     ├── events-signup.service.js
│   │  │     ├── events-signup.spec.js
│   │  │     ├── events-signup.html
│   │  │     └── events-signup.scss
│   │  └── components.module.js
│   ├── common/
│   │  ├── nav/
│   │  │     ├── nav.module.js
│   │  │     ├── nav.component.js
│   │  │     ├── nav.service.js
│   │  │     ├── nav.spec.js
│   │  │     ├── nav.html
│   │  │     └── nav.scss
│   │  ├── footer/
│   │  │     ├── footer.module.js
│   │  │     ├── footer.component.js
│   │  │     ├── footer.service.js
│   │  │     ├── footer.spec.js
│   │  │     ├── footer.html
│   │  │     └── footer.scss
│   │  └── common.module.js
│   ├── app.module.js
│   ├── app.component.js
│   └── app.scss
└── index.html

高级文件夹结构仅包含index.htmlapp/ ,这是一个目录,其中我们所有的根,组件,常见和低级模块都可以使用每个组件的标记和样式。

回到顶部

成分

组成理论

组件本质上是带有控制器的模板。它们不是指令,除非您使用控制器升级“模板指令”,否则您也不应用组件替换指令,这些指令最适合作为组件。组件还包含定义数据和事件,生命周期钩子的输入和输出的绑定,以及使用单向数据流和事件对象的能力,将数据备份到父组件中。这些是AngularJS 1.5及更高版本中的新DefaTSO标准。我们创建的所有模板和控制器驱动的所有模板和控制器驱动可能都是一个状态,无状态或路由组件。您可以将“组件”视为完整的代码,而不仅仅是.component()定义对象。让我们探索一些组件的一些最佳实践和咨询,然后深入研究如何通过状态,无状态和路由组件概念进行构造。

回到顶部

支持的属性

这些是您可以/应该使用的.component()的支持属性:

财产 支持
绑定 是的,仅使用\'@\'\'<\'\'&\'
控制器 是的
控制器 是的,默认为$ctrl
要求 是(新对象语法)
模板 是的
TemplateUrl 是的
transclude 是的

回到顶部

控制器

控制器只能与组件一起使用,从来没有其他任何地方。如果您觉得需要一个控制器,那么您真正需要的可能是管理该特定行为的无状态组件。

以下是一些用于控制器的Class的建议:

  • 删除名称“控制器”,即使用controller: class TodoComponent {...}来帮助未来的Angular迁移
  • 始终将constructor用于依赖注入目的
  • 使用NG-Annotate的\'ngInject\'; $inject注释的语法
  • 如果您需要访问词汇范围,请使用箭头功能
  • 或者,对于箭头功能, let ctrl = this;也可以接受,并且可能会根据用例更有意义
  • 将所有公共功能直接绑定到Class
  • 利用适当的生命周期钩, $onInit$onChanges$postLink$onDestroy
    • 注意: $onChanges$onInit之前被调用,请参阅资源部分,以获取更深入的详细信息
  • 使用require$onInit一起参考任何继承的逻辑
  • 不要覆盖controllerAs语法的默认$ctrl别名,因此请勿在任何地方使用controllerAs

回到顶部

单向数据流和事件

在AngularJS 1.5中引入了单向数据流,并重新定义了组件通信。

以下是使用单向数据流的一些建议:

  • 在接收数据的组件中,始终使用单向数据框语法\'<\'
  • 不要再使用\'=\'双向数据贴语语法
  • 具有bindings的组件应使用$onChanges克隆单向绑定数据,以通过参考并更新父级数据来破坏对象
  • 在父方法中使用$event作为函数参数(请参见$ctrl.addTodo($event)的状态示例)
  • 传递$event: {}对象从无状态组件备份(请参阅this.onAddTodo的无状态示例。
    • 奖励:使用带有.value() EventEmitter包装器来镜像Angular,避免手动$event oksent创建
  • 为什么?这反映了角度并保持每个组件内部的一致性。这也使国家可预测。

回到顶部

状态组件

让我们定义我们称之为“状态组件”的内容。

  • 获取状态,实质上通过服务与后端API通信
  • 不直接突变状态
  • 使儿童组件变异状态
  • 也称为智能/容器组件

具有状态组件的一个示例,配有其低级模块定义(这仅用于演示,因此为简洁而省略了一些代码):

 /* ----- todo/todo.component.js ----- */
import templateUrl from \'./todo.html\' ;

export const TodoComponent = {
  templateUrl ,
  controller : class TodoComponent {
    constructor ( TodoService ) {
      \'ngInject\' ;
      this . todoService = TodoService ;
    }
    $onInit ( ) {
      this . newTodo = {
        title : \'\' ,
        selected : false
      } ;
      this . todos = [ ] ;
      this . todoService . getTodos ( ) . then ( response => this . todos = response ) ;
    }
    addTodo ( { todo } ) {
      if ( ! todo ) return ;
      this . todos . unshift ( todo ) ;
      this . newTodo = {
        title : \'\' ,
        selected : false
      } ;
    }
  }
} ;

/* ----- todo/todo.html ----- */
< div class = \"todo\" >
  < todo-form
    todo = \"$ctrl.newTodo\"
    on-add-todo = \"$ctrl.addTodo($event);\" > </ todo-form >
  < todo-list
    todos = \"$ctrl.todos\" > </ todo-list >
</ div >

/* ----- todo/todo.module.js ----- */
import angular from \'angular\' ;
import { TodoComponent } from \'./todo.component\' ;
import \'./todo.scss\' ;

export const TodoModule = angular
  . module ( \'todo\' , [ ] )
  . component ( \'todo\' , TodoComponent )
  . name ;

此示例显示了一个状态组件,该组件通过服务在控制器内部获取状态,然后将其传递到无状态的子女组件中。请注意,如何在模板中没有使用ng-repeat和朋友等指令。取而代之的是,数据和功能被委派成<todo-form><todo-list>无状态组件。

回到顶部

无状态组件

让我们定义所谓的“无状态组件”。

  • 使用bindings: {}
  • 数据通过属性绑定(输入)进入组件
  • 数据通过事件(输出)离开组件
  • 突变状态,按需传递数据(例如单击或提交事件)
  • 不在乎数据的来源 – 它是无状态的
  • 是高度可重复使用的组件
  • 也称为愚蠢/呈现组件

一个无状态组件的示例(让我们使用<todo-form>作为一个示例),并使用其低级模块定义(这仅用于演示,因此为简短而省略了一些代码):

 /* ----- todo/todo-form/todo-form.component.js ----- */
import templateUrl from \'./todo-form.html\' ;

export const TodoFormComponent = {
  bindings : {
    todo : \'<\' ,
    onAddTodo : \'&\'
  } ,
  templateUrl ,
  controller : class TodoFormComponent {
    constructor ( EventEmitter ) {
        \'ngInject\' ;
        this . EventEmitter = EventEmitter ;
    }
    $onChanges ( changes ) {
      if ( changes . todo ) {
        this . todo = Object . assign ( { } , this . todo ) ;
      }
    }
    onSubmit ( ) {
      if ( ! this . todo . title ) return ;
      // with EventEmitter wrapper
      this . onAddTodo (
        this . EventEmitter ( {
          todo : this . todo
        } )
      ) ;
      // without EventEmitter wrapper
      this . onAddTodo ( {
        $event : {
          todo : this . todo
        }
      } ) ;
    }
  }
} ;

/* ----- todo/todo-form/todo-form.html ----- */
< form name = \"todoForm\" ng-submit = \"$ctrl.onSubmit();\" >
  < input type = \"text\" ng-model = \"$ctrl.todo.title\" >
  < button type = \"submit\" > Submit </ button >
</ form >

/* ----- todo/todo-form/todo-form.module.js ----- */
import angular from \'angular\';
import { TodoFormComponent } from \'./todo-form.component\';
import \'./todo-form.scss\';

export const TodoFormModule = angular
  .module(\'todo.form\', [])
  .component(\'todoForm\', TodoFormComponent)
  .value(\'EventEmitter\', payload = > ( { $event : payload } ))
  .name;

请注意, <todo-form>组件如何获取状态,它只是接收它,通过与之关联的控制器逻辑来突变对象,并通过属性绑定将其传递回父组件。在此示例中, $onChanges生命周期钩使最初的this.todo绑定对象成为克隆,并重新分配它,这意味着直到我们提交表单,并与单向数据流新的绑定构想\'<\'一起提交表单,直到我们提交表单后才受到影响。

回到顶部

路由组件

让我们定义所谓的“路由组件”。

  • 这本质上是一个具有路由定义的状态组件
  • 没有更多router.js
  • 我们使用路由组件来定义自己的路由逻辑
  • 该组件的数据“输入”是通过路由解析完成的(可选,仍在控制器中可用服务调用)

在此示例中,我们将采用现有的<todo>组件,重构它在接收数据的组件上使用路由定义和bindings (在此处使用ui-router秘密是我们创建的resolve属性,在这种情况下, todoData直接映射到为我们绘制的bindings )。我们将其视为路由组件,因为它本质上是“视图”:

 /* ----- todo/todo.component.js ----- */
import templateUrl from \'./todo.html\' ;

export const TodoComponent = {
  bindings : {
    todoData : \'<\'
  } ,
  templateUrl ,
  controller : class TodoComponent {
    constructor ( ) {
      \'ngInject\' ; // Not actually needed but best practice to keep here incase dependencies needed in the future
    }
    $onInit ( ) {
      this . newTodo = {
        title : \'\' ,
        selected : false
      } ;
    }
    $onChanges ( changes ) {
      if ( changes . todoData ) {
        this . todos = Object . assign ( { } , this . todoData ) ;
      }
    }
    addTodo ( { todo } ) {
      if ( ! todo ) return ;
      this . todos . unshift ( todo ) ;
      this . newTodo = {
        title : \'\' ,
        selected : false
      } ;
    }
  }
} ;

/* ----- todo/todo.html ----- */
< div class = \"todo\" >
  < todo-form
    todo = \"$ctrl.newTodo\"
    on-add-todo = \"$ctrl.addTodo($event);\" > </ todo-form >
  < todo-list
    todos = \"$ctrl.todos\" > </ todo-list >
</ div >

/* ----- todo/todo.service.js ----- */
export class TodoService {
  constructor ( $http ) {
    \'ngInject\' ;
    this . $http = $http ;
  }
  getTodos ( ) {
    return this . $http . get ( \'/api/todos\' ) . then ( response => response . data ) ;
  }
}

/* ----- todo/todo.module.js ----- */
import angular from \'angular\' ;
import uiRouter from \'angular-ui-router\' ;
import { TodoComponent } from \'./todo.component\' ;
import { TodoService } from \'./todo.service\' ;
import \'./todo.scss\' ;

export const TodoModule = angular
  . module ( \'todo\' , [
    uiRouter
  ] )
  . component ( \'todo\' , TodoComponent )
  . service ( \'TodoService\' , TodoService )
  . config ( ( $stateProvider , $urlRouterProvider ) => {
    \'ngInject\' ;
    $stateProvider
      . state ( \'todos\' , {
        url : \'/todos\' ,
        component : \'todo\' ,
        resolve : {
          todoData : TodoService => TodoService . getTodos ( )
        }
      } ) ;
    $urlRouterProvider . otherwise ( \'/\' ) ;
  } )
  . name ;

回到顶部

指令

指令理论

指令为我们提供templatescope绑定, bindToControllerlink和许多其他内容。在存在.component()的情况下,应仔细考虑这些用法。指令不应再声明模板和控制器,也不应通过绑定接收数据。指令应仅用于装饰DOM。这样,它意味着扩展使用.component()创建的现有HTML-。从简单意义上讲,如果您需要自定义的DOM事件/API和逻辑,请使用指令并将其绑定到组件内的模板。如果您需要明智的DOM操作,还需要考虑$postLink Lifecycle挂钩,但是,这不是迁移所有DOM操纵的地方,如果可以的话,请使用指令。

以下是使用指令的一些建议:

  • 切勿使用模板,范围,绑定器或控制器
  • 总是restrict: \'A\'
  • 在必要时使用编译和链接
  • 记住要在$scope.$on(\'$destroy\', fn);

回到顶部

推荐的属性

由于指令支持.component()大部分内容(模板指令是原始组件),因此我建议将您的指令对象定义限制为仅这些属性,以免错误地使用指令:

财产 用它吗? 为什么
bindtocontroller 在组件中使用bindings
编译 是的 用于预编译的DOM操纵/事件
控制器 使用组件
控制器 使用组件
链接功能 是的 用于预/后操作/事件
多元素 是的 见文档
优先事项 是的 见文档
要求 使用组件
限制 是的 定义指示用法,始终使用\'A\'
范围 使用组件
模板 使用组件
TemplateNamespace 是(如果必须) 见文档
TemplateUrl 使用组件
transclude 使用组件

回到顶部

常数或类

有几种方法可以使用ES2015和指令,要么具有箭头功能和更容易的分配,要么使用ES2015 Class 。选择最适合您或您的团队的东西,请记住Angular用途Class

这是一个使用箭头函数常数的示例,一个expression wrapper () => ({})返回对象文字(请注意.directive()内部的用法差异()):

 /* ----- todo/todo-autofocus.directive.js ----- */
import angular from \'angular\' ;

export const TodoAutoFocus = ( $timeout ) => {
  \'ngInject\' ;
  return {
    restrict : \'A\' ,
    link ( $scope , $element , $attrs ) {
      $scope . $watch ( $attrs . todoAutofocus , ( newValue , oldValue ) => {
        if ( ! newValue ) {
          return ;
        }
        $timeout ( ( ) => $element [ 0 ] . focus ( ) ) ;
      } ) ;
    }
  }
} ;

/* ----- todo/todo.module.js ----- */
import angular from \'angular\' ;
import { TodoComponent } from \'./todo.component\' ;
import { TodoAutofocus } from \'./todo-autofocus.directive\' ;
import \'./todo.scss\' ;

export const TodoModule = angular
  . module ( \'todo\' , [ ] )
  . component ( \'todo\' , TodoComponent )
  . directive ( \'todoAutofocus\' , TodoAutoFocus )
  . name ;

或使用ES2015 Class (注册指令时手动调用new TodoAutoFocus )来创建对象:

下载源码

通过命令行克隆项目:

git clone https://github.com/toddmotto/angularjs-styleguide.git

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 开发教程 angularjs styleguide https://www.zuozi.net/32035.html

svg sanitizer
上一篇: svg sanitizer
sqids php
下一篇: sqids php
常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务