AngularJS 风格指南 (ES2015)
# AngularJS 风格指南 (ES2015) AngularJs1.6.x的最佳实践.涵盖体系结构,文件结构,组件,单向数据流和生命周期。目录
模块化体系结构Angular应用程序中的每个模块都应该是组件模块。组件模块用来封装逻辑、模板、路由和子组件。 模块概述模块的设计直接反应了我们的文件结构,从而保持了可维护性和可预测性。我们最好有三个高级模块: root、component和common。root模块是用来启动我们应用和相应模板的基础模块,root模块中导入component和common模块作为依赖模块。component和common模块则负责引入Low-level modules,Low-level modules通常包含可重用的组件,控制器,服务,指令,过滤器和测试. Back to top Root 模块root模块中定义整个应用的根组件,根组件中通常包含路由组件,比如 // app.component.js export const AppComponent = { template: ` <header> Hello world </header> <div> <div ui-view></div> </div> <footer> Copyright MyApp 2016. </footer> ` }; // 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; 使用 Back to top Components 模块所有的可重用组件应该注册在component模块上。上面例子中展示了我们是如何导入 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; Back to top Common module所有我们不希望用在其他应用中的组件应该注册在common模块上。比如布局、导航和页脚之类的内容。 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; Back to top Low-level modulesLow-level module是包含独立功能的组件模块,将会被导入到像component module或者是 common module这样的higher-level module中,下面是一个例子。一定要记住在每个 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; Back to top 文件命名约定保持文件名简单,并且使用小写字母,文件名使用 ' - '分割,比如 calendar.module.js calendar.component.js calendar.service.js calendar.directive.js calendar.filter.js calendar.spec.js calendar.html calendar.scss Back to top 可伸缩文件结构文件结构是非常重要的,这描述了一个可伸缩和可预测的结构,下面是一个例子。 ├── 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 顶级文件夹仅包含 Back to top 组件组件概述组件是带有控制器的模板,组件可以通过 Back to top 支持的属性这些是
Back to top 控制器控制器应该只和组件一起使用,如果你感觉需要一个控制器,可能你需要的是一个管理这个特定行为的组件。 这是一些使用
Back to top 单向数据流和事件AngularJS 1.5中引入了单向数据流,这重新定义了组件之间的通信. 这里有一些使用单向数据流的建议:
<!-- * Bonus: Use an
Back to top 有状态组件让我们定义一个有状态组件
<!-- * Renders child components that mutate state
一个包括low-level module定义的有状态组件的例子(为了简洁省略了一些代码) /* ----- 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; 这个例子展示了一个有状态组件,在控制器中通过服务获取数据,然后传递给无状态子组件。注意我们在模板中并没有使用 Back to top 无状态组件让我们第一我们所谓的无状态组件:
<!-- * Mutates state,passes data back up on-demand (such as a click or submit event) -->
一个无状态组件的例子( /* ----- 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; 注意, Back to top 路由组件让我们定义一个路由组件。
在这个例子中,我们将使用路由定义和 /* ----- todo/todo.component.js ----- */ import templateUrl from './todo.html'; export const TodoComponent = { bindings: { todoData: '<' },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; Back to top 指令指令概述指令有 这有一些使用指令的建议:
Back to top 推荐的属性由于指令支持
Back to top Constants or Classes在ES2015中有几种方式使用指令,要么通过箭头函数,要么使用 下面是一个使用箭头函数的例子: /* ----- 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',TodoComponent) .directive('todoAutofocus',TodoAutoFocus) .name; 或者使用 ES2015 /* ----- todo/todo-autofocus.directive.js ----- */ import angular from 'angular'; export class TodoAutoFocus { constructor($timeout) { 'ngInject'; this.restrict = 'A'; this.$timeout = $timeout; } link($scope,$attrs) { $scope.$watch($attrs.todoAutofocus,oldValue) => { if (!newValue) { return; } this.$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',($timeout) => new TodoAutoFocus($timeout)) .name; Back to top 服务服务概述服务本质上是业务逻辑的容器。服务包含其他内置或外部服务例如 Back to top Classes for Service这是一个使用 ES2015 /* ----- 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 { TodoComponent } from './todo.component'; import { TodoService } from './todo.service'; import './todo.scss'; export const TodoModule = angular .module('todo',TodoService) .name; Back to top 样式使用 Webpack 我们可以在 如果你有一些变量或全局使用的样式,比如表单输入元素,那么这些文件仍然应该放在根目录 Back to top ES2015 and ToolingES2015
Tooling
Back to top 状态管理考虑使用redux管理你应用的状态.
Back to top 资源
Back to top (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |