在这一步中,我们将不会在我们的应用中添加任何新功能,相反,我们打算退回一步,重构我们的代码库,移动我们的代码和文件,以此来使我们的应用更具易扩展性和可维护性。

在先前的步骤中,我们已经见识到了如何将我们的应用构建得更具模块性和可测试性。另一种同样重要的思想是,用一种使得查看代码变得容易(无论对我们还是团队中的其他开发者)和能在我们应用中快速指定的某区域的相关代码块的方式来组织我们的代码库。

为此,下面我们将解释为何及如何:

  ·将每一个实体置于他们自己的文件中(own file)。

  ·通过特定区域(feature area)来组织我们的代码,而不是通过函数。

  ·将我们代码模块化以便其他模块可以依赖。

(我们会尽量简洁,不打算深入探讨每一个最佳实践和惯例的细节。这些原则在Angular风格手册中都被深入探讨过了,手册中也包含更多如何有效组织Angular代码库的技巧。)

最重要的不同将会在下面列出,你可以点击这里查看全部的不同。

特性和文件的一一对应

出于简洁性的目的,将所有的东西都放在一个文件中是很诱人的,或者为每种类型创建一个文件;比如:所有的控制器放在一个文件,所有的组件放到另一个文件,所有的服务又放在第三个文件中,诸如此类。这样做在开始时可能运行得不错,但随着我们应用的增长,这将成为维护的一个负担。随着我们添加越来越多的特性,我们的文件将越来越大,最后将会很难检索到我们在寻找的代码。

取而代之,我们应该将每一个特性/实体置于它自己的文件中。每一个独立的控制器会在它自己的文件中被定义,每一个组件将会在它自己的文件中被定义,诸如此类。

幸运的是,我们无需在我们的代码中做任何改变来遵循这条准则,因为我们已经定义了我们的phoneList组件和它自己的phone-list.component.js文件。做的不错!

随着我们增加更多特性,我们会将此牢记于心。

通过特性来组织

所以,既然我们已经学会了将任何特征来置它自己的文件中,我们的app/目录将会被充满大量的文件和规范(请牢记我们将单元测试文件紧靠于我们的源代码文件)。更重要的是,逻辑上相关的文件不会被组织到一起;这会使得定位应用中一个指定区域的所有相关文件,做一些改变,或者修一个bug,这些都变得困难。

那么,我们应该做什么呢?

嗯,我们打算通过特性来在目录中组织我们的文件。比如,由于我们应用中已经有一个用于展示电话的部分,我们会将所有相关文件放到app/目录下的phone-list/。我们马上将会发现一些指定的特征在应用中的不同部分被用到了,我们会把这些放到app/core/目录下。

(一些core目录下其他文件名有sharedcommon和components。最后一个有点思想上的误导,因为它也会包含除组件之外的其他东西。

这主要是由于历史遗留,那时“组件”仅仅是指应用中的通用构建模块。)

基于我们目前所讨论的,下面是我们对phoneList“特征”的目录/文件布局:

app/
phone-list/
phone-list.component.js
phone-list.component.spec.js
app.js

使用模块

正如先前提及的,采用模块化的架构的一个好处是代码复用--不仅仅在一个应用内,也包括跨应用间的复用。还要做一步来减少代码复用的阻力:

  ·每一个特性/区域应该声明其自己的模块并且所有相关的实体都应该在改模块中被注册。

让我们拿phoneList举个例子,之前,phoneList组件会在phonecatApp中被注册:

angular.
module('phonecatApp').
component('phoneList', ...);

相似的,附庸的规范文件phonecatApp会在每次测试前被加载(因为那是我们的组件被注册的地方)。现在,想象一下我们需要在另一个我们开发的项目中引入电话列表。多亏我们模块化的架构,我们不需要再造轮子啦;我们仅仅需要在其他项目中复制phone-list/目录并且在index.html中添加必要的脚本标记就搞定了,对吗?

好吧,没那么快。新项目对phonecatApp一无所知。所以,我们或许不得不把phonecatApp作为这个项目的主模块的名字。正如你想象的那样,这样既生硬,还容易犯错。

没错!想必你已经猜到了,有一种更好的方法!

每一个特征/区域都会声明其自己的模块并会在其中注册相关实体。主模块(phonecatApp)将会在每一个特征/区域声明一个依赖。现在,想要在一个新项目中复用相同代码,要做的就是将特征目录复制过来并且在新项目的主模块中添加特征模块,以此来形成一个依赖。

经过这些改变,下面是我们的phoneList特征看起来的样子:

/:

app/
phone-list/
phone-list.module.js
phone-list.component.js
phone-list.component.spec.js
app.module.js

app/phone-list/phone-list.module.js:

// Define the `phoneList` module
angular.module('phoneList', []);

app/phone-list/phone-list.component.js:

// Register the `phoneList` component on the `phoneList` module,
angular.
module('phoneList').
component('phoneList', {...});

app/app.module.js:

由于app/app.js现在仅仅包含主模块的声明,我们添加一个.module后缀

// Define the `phonecatApp` module
angular.module('phonecatApp', [
// ...which depends on the `phoneList` module
'phoneList'
]);

通过在定义phonecatApp模块时遍历phoneList里的依赖数组,Angular会使所有在phoneList中注册的实体在phonecatApp中也同样可用。

(别忘了更新你的index.html,为我们创建的每一个JavsScript文件添加一个<script>标签,这很无聊,但绝对值得做。

在生产就绪的应用中,你无论如何(考虑到性能因素)都会将你的JavsScript文件串联并压缩),所以这不会再是一个问题。)

(注意到定义模块的文件(比如.module.js)需要被其他添加该模块中特性(比如:组件,控制器,服务,过滤器)提前引入。)

外部模板

既然我们在重构,让我们更进一步。正如我们已经学到的,组件包含模板,模板本质上就是用于阐述我们的代码如何布局和展示给用户的HTML代码片段。在第三步中,我们已经看到了如何使用CDO的template特性来以字符串形式为组件指定模板。将HTML代码写入一个字符串可不怎么优雅,尤其是对于更大的模板来说。如果我们将HTML代码放在.html文件中将会好很多。通过这种方法,我们会得到所有我们的IDE/编辑器提供的支持(比如:HTML-特定的颜色高亮和自动补全),并且将我们的组件定义得更整洁。

所以,保持我们组件模板的内联(使用CDO提供的template特性)是非常好的,我们打算在我们的phoneList组件中使用外部模板。为了表示我们将使用一个外部模板,我们使用templateUrl特性并且指定我们模板将被加载的URL。既然我们想要将我们的模板和组件定义的地方放在一起,我们将它放在app/phone-list/目录下.

我们将template特性中的内容(HTML代码)放到app/phone-list/phone-list.template.html中并且修改我们的CDO,像这样:

app/phone-list/phone-list.component.js:

angular.
module('phoneList').
component('phoneList', {
// Note: The URL is relative to our `index.html` file
templateUrl: 'phone-list/phone-list.template.html',
controller: ...
});

一旦Angular在运行时想要创建一个phoneList组件的实体,它将发送一个HTTP请求来从app/phone-list/phone-list.template.html中获取模板。

(我们通过为外部模板添加.template后缀来和我们的风格保持一致,另一种风格是仅仅添加.html扩展名(比如:phone-list.html)。)

最终的目录/文件布局

最终我们完成了重构,下面是我们的应用从外边看起来的样子:

/:

app/
phone-list/
phone-list.component.js
phone-list.component.spec.js
phone-list.module.js
phone-list.template.html
app.css
app.module.js
index.html

总结 

即使我们没有在我们的应用中添加任何新功能,但我么已经向一个架构优良和可维护的应用迈进了一大步。时间使得事情变得更好玩( Time to spice things up)。让我们进入下一步来学习如何在应用中添加一个全文本搜索吧!

[Angular Tutorial] 4 - Directory and File Organization的更多相关文章

  1. [Angular Tutorial]PhoneCat Tutorial App

    (注:曾经在<不敢止步>一书中看到学到一个观点,作者认为学习一门技术最好的方法就是翻译某部领域书籍.这里我决定做一次尝试,接下来花1个月左右时间,将Angular Tutorial Pho ...

  2. [Angular Tutorial] 0-Bootstraping

    在这一节的tutorial中,您将会逐渐熟悉AngularJS phonecat app的最重要的源代码文件.您也将学到如何将开发服务器与angular-seed绑定到一起,并且在浏览器中运行应用. ...

  3. 解决clang: error: no such file or directory: such file or directory:的问题

    一,详细问题描述 clang: error: no such file or directory: 'xxx/src/GGBaCollectionViewCell.m' clang: error: n ...

  4. Non-Programmer's Tutorial for Python 3/File IO

    File I/O Here is a simple example of file I/O (input/output): # Write a file with open("test.tx ...

  5. [Angular Tutorial] 3-Components

    在先前的步骤中,我们看到了一个控制器和一个模板如何一起工作来将一个静态的HTML文件转化为动态页面(view).一般说来,这在单页应用中一种非常常见的模式(在Angular应用中尤其是这样): ·客户 ...

  6. [Angular Tutorial] 7-XHRs & Dependency Injection

    我们受够了在应用中用硬编码的方法嵌入三部电话!现在让我们用Angular内建的叫做$http的服务来从我们的服务器获取更大的数据集吧.我们将会使用Angular的依赖注入来为PhoneListCtrl ...

  7. [Angular Tutorial] 14 -Animations

    在这一步中,我们将会通过在我们先前创建的模板代码中添加CSS和JavaScript动画效果来扩展我们的web应用. ·我们现在使用ngAnimate模块来允许动画效果贯穿整个应用. ·我们也依赖于自带 ...

  8. [Angular Tutorial] 13 -REST and Custom Services

    在这一步中,我们将会改变我们获取数据的方式. ·我们定义一个代表RESTful客户端的自定义服务.使用这个客户端,我们可以用一种更简单的方法向服务端请求数据,而不用处理更底层的$httpAPI,HTT ...

  9. [Angular Tutorial] 12 -Event Handlers

    在这一步中,您将会在电话细节页面添加一个可点击的电话图片转换器. ·电话细节页面展示了当前电话的一张大图片和几张相对较小的略图.如果我们能仅仅通过点击略图就能把大图片换成略图就好了.让我们看看用Ang ...

随机推荐

  1. JQuery选择所有标题的元素

    $(":header") 参考:http://www.w3school.com.cn/jquery/selector_header.asp

  2. ONES 安装、配置以及初始化配置

    环境依赖 bower composer php 5.5.9+ mysql 5.6.5+ PHP和MySQL版本均为最低要求版本,安装前请先确认. 通过CLI安装 $ git clone http:// ...

  3. PHP程序员学习路线

    注:本文是@黑夜路人的旧文,假设PHP程序员基础不是非常扎实,简单梳理了每个阶段PHP程序员的技术要求,来帮助很多PHP程序做对照设定学习成长目标.再次分享,共勉,欢迎补充. 第一阶段:基础阶段(基础 ...

  4. Windows应用程序要点

    一个完整的Windows应用程序除了WinMain函数外,还包含用于处理用户动作和窗口消息的窗口函数.  Windows应用程序具有的一些特性: 消息驱动机制 图形设备接口(GDI) 基于资源的程序设 ...

  5. Nginx 502/504 Gateway time-out错误完美解决方案【转发】

      在安装完Nginx+PHP-fpm+Mysql后,跑PHP的应用会经常出现504 Gateway Time-out 或者502 Bad Gateway的情况. Nginx 504 Gateway ...

  6. 转 ogg组件介绍

    应用场景:数据分发   ogg的组件: (1) OGG 程序和工具说明 convchk   转换ogg版本的信息 ,该程序可以将checkpoint files 转换成新版本: convprm :OG ...

  7. 阿里云CentOS安装firefox闪退

    安装完vnc远程连接,接着安装firefox,点firefox木有任何提示就退 直接命令下运行,发现错误如下: /usr/lib/firefox/firefox: symbol lookup erro ...

  8. python WEB接口自动化测试之requests库详解

    由于web接口自动化测试需要用到python的第三方库--requests库,运用requests库可以模拟发送http请求,再结合unittest测试框架,就能完成web接口自动化测试. 所以笔者今 ...

  9. 深究Xcode的bitcode设置

    深究Xcode的bitcode设置 转发至:http://www.jianshu.com/p/f42a33f5eb61 前言 做iOS开发的朋友们都知道,目前最新的Xcode7,新建项目默认就打开了b ...

  10. STM32 IAP 在线升级详解(转)

    源:http://blog.csdn.net/yx_l128125/article/details/12992773 (扩展-IAP主要用于产品出厂后应用程序的更新作用,考虑到出厂时要先烧写IAP   ...