Angular 1.5 Styleguide (ES2015)

使用 ES2015 在 Angular 1.5 中的最佳实践

说到关于 Angular Styleguide,很多人可能会想到这篇经典的文章。的确,它是一篇非常棒的文章,甚至已经被翻译成许多种语言(包括中文),在 github 上更是拥有将近 1.9w 个 star。

然而,这次谈论的不是它。因为随着 ES6 的广泛应用,以及 Angular 1.5 的发布,它有那么一点点不够时髦(也谈不上过时哈~)。

本文的大部分观点都来自这篇文章(以下简称原文),但个人根据工作上积累的一些经验添并不是完全认同原文的所有想法,并想去除些繁冗的例子,于是就没有直接翻译原文。

言归正传,下面就来看看使用 ES6 来编写基于 Angular 1.5 的代码有哪些最佳实践。

模块架构

在 Angular 体系中,所有代码都是基于模块的,它来封装模块内部的逻辑、模板、路由和子模块。

模块划分

原文将模块分为 3 大类,分别是:root, component 和 common,并创建相应的文件夹来储存。

  • root:根模块组件,用来启动应用和相应模板
  • component:包含所有可重用的模块,模块中可以包含 components, controllers, services, directives, filters and tests
  • common:包含所有业务的模块(即不可重用,和 component 最大的区别),它可以是页面布局、导航和页脚等等。

原文中有详细的例子,但就如文章开头所说,在这里就不贴了。

但是,我并不完全认同原文观点。

因为,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 等。

我完全同意第一个观点,但第二个中的模块名就没有添加的必要,因为文件夹名已经很好的体现了模块名这个含义。

再附一张项目中的模块结构图

组件(Component)

组件是 Angular 1.5 新提出的,是一种特殊的指令,Augular 的源码中也彰显了这一点。

它相比指令更多的是数据的单向绑定和生命周期钩子,尽管我认为所谓的生命周期钩子只是语法糖,甚至组件它本身就是个语法糖,但这不妨碍它成为 Angular 体系中重要的一部分。因为,它的推出明确的区分了指令和组件,解决了原先指令划分不清、承担过多工作的问题。

组件属性

Property Support
bindings Yes, 只使用 @, <, &,避免使用 =
controller Yes
controllerAs Yes, 默认为 $ctrl
require Yes
template Yes
templateUrl Yes
transclude Yes

控制器(controller)

控制器只应在组件中使用,如果你只想创建一个控制器,那你应创建一个无状态组件来管理它。

使用 class 关键字来创建控制器时要注意以下几点:

  • 使用 constructor 处理依赖注入
  • 之前提到过,导出模型名,而并不是直接导出模型
  • 使用箭头函数
  • 使用 $onInit, $onChanges, $postLink$onDestroy 生命周期
    (注意:$onChanges 会在 $onInit之前被调用)
  • 使用默认的控制器 $ctrl,不使用 controllerAs 修改控制器的别名

单向数据流

  • 总是使用 < 单向数据绑定来代替 = 双向数据绑定
  • 使用 $onChanges 来监听数据的变化
  • 父组件的方法使用 $event 作为参数传递的名字
  • 子组件调用时返回一个包含有 $event 属性的对象

这是不是看上去很像 Redux?没错,原文的作者也是推荐使用 Angular Redux 来管理状态。

状态组件(Stateful components)和无状态组件(Stateless components)

状态组件和无状态组件其实分别对应了 Redux 中的容器组件(Smart/Container Components)和展示组件(Dumb/Presentational Components),这部分原作者主要也是表达了在 Angular 中实现单向数据流的理念,但原作者提供的例子并不是完整的 Redux,它没有单一的 Store 和 Reducer。

指令(Directive)

相信指令大家都很熟悉了,但自从 Angular 1.5 提供了组件,指令的选择就应当慎重考虑,它应当只在装饰 DOM 时使用。

  • 不使用 template, templateUrl, scope, bindToControllercontroller 等相关的属性,如果想用,考虑是不是它可以用 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 使用组件替代

服务(Service)

服务主要用于封装一些不应在组件中处理的业务逻辑和请求。

Angular 提供 2 种创建服务的方式 servicefactory。在 ES6 引入了 class 关键字后,它能非常友好地同 service一起工作,所以,无论何时都使用 service 来创建服务。

类 or 方法

原文的标题是常量或类(Constants or Classes),容许我自作主张的修改一下标题,因为我认为原文的实现的区别更主要的在于是使用类或方法去定义一个服务或控制器等。

当然这两种方法都可以,因为类它本身就是方法的一个语法糖。但是,Angular 2 是重度依赖 class 关键字的,所以,我认为还是全部统一使用 class 关键字来声明服务、控制器、过滤器、指令和组件的定义等。

值得注意的是,Angular 组件和指令定义的参数是一个对象,所以在使用 class 定义时,要手动实例化它。

工具

最后,原文作者还推荐了一些工具

  • Babel:编译工具,这就不多说了,必备神器
  • TypeScript:还是为了 A2
  • Webpack:打包工具,用过都说好
  • ngAnnotate:自动依赖注入,和打包工具一起服用效果更好
  • Angular Redux:状态管理

以上为个人观点,欢迎交流。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容