说到关于 Angular Styleguide,不少人可能会想到这篇经典的文章。的确,它是一篇很是棒的文章,甚至已经被翻译成许多种语言(包括中文),在 github 上更是拥有将近 1.9w 个 star。html
然而,此次谈论的不是它。由于随着 ES6 的普遍应用,以及 Angular 1.5 的发布,它有那么一点点不够时髦(也谈不上过期哈~)。webpack
本文的大部分观点都来自这篇文章(如下简称原文),但我的根据工做上积累的一些经验添并非彻底认同原文的全部想法,并想去除些繁冗的例子,因而就没有直接翻译原文。git
言归正传,下面就来看看使用 ES6 来编写基于 Angular 1.5 的代码有哪些最佳实践。angularjs
在 Angular 体系中,全部代码都是基于模块的,它来封装模块内部的逻辑、模板、路由和子模块。github
原文将模块分为 3 大类,分别是:root, component 和 common,并建立相应的文件夹来储存。web
root:根模块组件,用来启动应用和相应模板typescript
component:包含全部可重用的模块,模块中能够包含 components, controllers, services, directives, filters and testsredux
common:包含全部业务的模块(即不可重用,和 component 最大的区别),它能够是页面布局、导航和页脚等等。api
原文中有详细的例子,但就如文章开头所说,在这里就不贴了。babel
可是,我并不彻底认同原文观点。
由于,common 的翻译是公共的,在 common 中存放业务代码也和咱们一直以来的作法相悖;其次是,在 Angular 的开发过程当中,仍是存在一些能够在业务逻辑中公用的代码,好比 service 和 filter。因此,我更倾向于将它分为 4 部分,分别是 root, app, component 和 common。
root:和原文的做法同样,依旧是用来启动应用,并包含了应用的模板(并不必定要一个文件夹,能够是根目录下的一个 app.js 文件)
app:相似于以前的 common 模块,包含全部的业务模块组件
component:同原文的同样,包含全部可重用的模块组件
common:公用代码模块,包含可公用的代码,如 service 和 filter
使用 ES6 确定会使用强大的模块语法,在同 Angular 一同使用时,必定要注意导出的是模块的名字,而非是 Angular 的模块对象,这样才能再另外一处被其余模块注入。
// 精简了原文的代码,去除了一些和这节无关的代码 import angular from 'angular'; import CalendarComponent from './calendar.component'; const calendar = angular .module('calendar', []) .component('calendar', CalendarComponent) .name; export default calendar;
首先,为每一个模块添加 index.js
文件来定义整个模块,这样再别的模块中能够经过文件夹直接引入。
原文使用模块名.文件内容.文件类型
的方式来命名一个文件,如 calendar.controller.js 等。
我彻底赞成第一个观点,但第二个中的模块名就没有添加的必要,由于文件夹名已经很好的体现了模块名这个含义。
组件是 Angular 1.5 新提出的,是一种特殊的指令,Augular 的源码中也彰显了这一点。
它相比指令更多的是数据的单向绑定和生命周期钩子,尽管我认为所谓的生命周期钩子只是语法糖,甚至组件它自己就是个语法糖,但这不妨碍它成为 Angular 体系中重要的一部分。由于,它的推出明确的区分了指令和组件,解决了原先指令划分不清、承担过多工做的问题。
Property | Support |
---|---|
bindings | Yes, 只使用 @ , < , & ,避免使用 = |
controller | Yes |
controllerAs | Yes, 默认为 $ctrl |
require | Yes |
template | Yes |
templateUrl | Yes |
transclude | Yes |
控制器只应在组件中使用,若是你只想建立一个控制器,那你应建立一个无状态组件来管理它。
使用 class
关键字来建立控制器时要注意如下几点:
使用 constructor
处理依赖注入
以前提到过,导出模型名,而并非直接导出模型
使用箭头函数
使用 $onInit
, $onChanges
, $postLink
和 $onDestroy
生命周期
(注意:$onChanges
会在 $onInit
以前被调用)
使用默认的控制器 $ctrl
,不使用 controllerAs
修改控制器的别名
老是使用 <
单向数据绑定来代替 =
双向数据绑定
使用 $onChanges
来监听数据的变化
父组件的方法使用 $event
做为参数传递的名字
子组件调用时返回一个包含有 $event
属性的对象
这是否是看上去很像 Redux?没错,原文的做者也是推荐使用 Angular Redux 来管理状态。
状态组件和无状态组件其实分别对应了 Redux 中的容器组件(Smart/Container Components)和展现组件(Dumb/Presentational Components),这部分原做者主要也是表达了在 Angular 中实现单向数据流的理念,但原做者提供的例子并非完整的 Redux,它没有单一的 Store 和 Reducer。
相信指令你们都很熟悉了,但自从 Angular 1.5 提供了组件,指令的选择就应当慎重考虑,它应当只在装饰 DOM 时使用。
不使用 template
, templateUrl
, scope
, bindToController
或 controller
等相关的属性,若是想用,考虑是否是它能够用 component
来实现
老是使用 restrict: 'A'
Property | 是否使用 | Why |
---|---|---|
bindToController | No | 使用组件替代 |
compile | Yes | DOM 操做/事件的预处理 |
controller | No | 使用组件替代 |
controllerAs | No | 使用组件替代 |
link functions | Yes | DOM 操做/事件的处理 |
multiElement | Yes | See docs |
priority | Yes | See docs |
require | No | 使用组件替代 |
restrict | Yes | 老是使用 restrict: 'A' |
scope | No | 使用组件替代 |
template | No | 使用组件替代 |
templateNamespace | Yes (若是必须) | See docs |
templateUrl | No | 使用组件替代 |
transclude | No | 使用组件替代 |
服务主要用于封装一些不该在组件中处理的业务逻辑和请求。
Angular 提供 2 种建立服务的方式 service
和 factory
。在 ES6 引入了 class
关键字后,它能很是友好地同 service
一块儿工做,因此,不管什么时候都使用 service
来建立服务。
原文的标题是常量或类(Constants or Classes),允许我自做主张的修改一下标题,由于我认为原文的实现的区别更主要的在因而使用类或方法去定义一个服务或控制器等。
固然这两种方法均可以,由于类它自己就是方法的一个语法糖。可是,Angular 2 是重度依赖 class
关键字的,因此,我认为仍是所有统一使用 class
关键字来声明服务、控制器、过滤器、指令和组件的定义等。
值得注意的是,Angular 组件和指令定义的参数是一个对象,因此在使用 class
定义时,要手动实例化它。
最后,原文做者还推荐了一些工具
Babel:编译工具,这就很少说了,必备神器
TypeScript:仍是为了 A2
Webpack:打包工具,用过都说好
ngAnnotate:自动依赖注入,和打包工具一块儿服用效果更好
Angular Redux:状态管理
以上为我的观点,欢迎交流。