[Angular Tutorial] 4 - Directory and File Organization

时间:2023-03-09 00:29:49
[Angular Tutorial] 4 - Directory and File Organization

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

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

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

  ·将每一个实体置于他们自己的文件中(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)。让我们进入下一步来学习如何在应用中添加一个全文本搜索吧!