1.3Angular 项目结构 1.3 Angular 项目结构:构建清晰、可维护的应用基石 在 Angular 应用开发的旅程中,掌握项目结构至关重要。一个良好组织的项目结构不仅能提升开发效率,还能显著增强代码的可维护性和可扩展性,尤其是在团队协作和应用规模增长的情况下。本章节将深入探讨 Angular 项目的默认结构,并提供最佳实践,助您构建健壮且易于管理的 Angular 应用。 1.3.1 Angular 项目结构概览 当我们使用 Angular CLI 创建一个新的 Angular 项目时,CLI 会自动生成一套预定义的项目结构。这种结构并非强制性的,但它代表了 Angular 团队的最佳实践,并被广泛应用于社区中。
在 Angular 应用开发的旅程中,掌握项目结构至关重要。一个良好组织的项目结构不仅能提升开发效率,还能显著增强代码的可维护性和可扩展性,尤其是在团队协作和应用规模增长的情况下。本章节将深入探讨 Angular 项目的默认结构,并提供最佳实践,助您构建健壮且易于管理的 Angular 应用。
当我们使用 Angular CLI 创建一个新的 Angular 项目时,CLI 会自动生成一套预定义的项目结构。这种结构并非强制性的,但它代表了 Angular 团队的最佳实践,并被广泛应用于社区中。理解并遵循这种结构,能让我们更快地融入 Angular 生态,并与其他 Angular 开发者高效协作。
以下是一个典型的 Angular 项目结构(基于 Angular CLI 生成的项目):
graph TD subgraph 项目根目录 node_modules --> package.json & package-lock.json src --> app & assets & environments & index.html & main.ts & polyfills.ts & styles.scss & test.ts & tsconfig.app.json & tsconfig.spec.json e2e --> protractor.conf.js & tsconfig.json & ... (E2E 测试相关文件) angular.json --> 项目配置文件 package.json --> 项目依赖和脚本 package-lock.json --> 依赖版本锁定 README.md --> 项目说明文档 .gitignore --> Git 忽略文件 tsconfig.json --> TypeScript 配置文件 (根目录) end app --> components & modules & services & app.component.* & app.module.ts & app-routing.module.ts components --> 各种组件文件夹 (feature1, feature2, shared, ui 等) modules --> 各种模块文件夹 (feature1, feature2, shared, core 等) services --> 各种服务文件夹 (auth, data, util 等) environments --> environment.ts & environment.prod.ts
目录结构说明:
node_modules/: 存放项目依赖的 npm 包。通常无需手动修改此目录,由 npm install 或 yarn install 管理。
src/: 核心源代码目录,包含应用的所有业务逻辑、组件、模块、服务、资源文件等。
app/: 应用的核心模块,包含应用的组件、模块、服务等核心代码。这是我们开发的主要阵地。
components/: 存放各种组件的目录,通常按照功能或模块进行组织。
modules/: 存放各种特性模块、共享模块、核心模块等。
services/: 存放各种服务的目录,如数据服务、认证服务、工具服务等。
app.component.*: 应用的根组件相关文件 (HTML, CSS/SCSS, TS, Spec)。
app.module.ts: 应用的根模块定义文件。
app-routing.module.ts: 应用的根路由模块定义文件(如果使用了路由)。
assets/: 存放静态资源文件,如图片、字体、JSON 数据等。这些资源在构建后会被复制到最终的 dist 目录。
environments/: 存放不同环境(开发、生产等)的配置信息。
environment.ts: 默认开发环境配置。
environment.prod.ts: 生产环境配置。
index.html: 应用的入口 HTML 文件。Angular 应用会挂载到这个文件的 <app-root> 标签中。
main.ts: 应用的启动文件,负责启动 Angular 应用模块。
polyfills.ts: 用于提供浏览器兼容性补丁,以支持旧版本浏览器。
styles.scss 或 styles.css: 全局样式文件。
test.ts: 单元测试的入口文件。
tsconfig.app.json: 应用代码的 TypeScript 配置文件。
tsconfig.spec.json: 单元测试代码的 TypeScript 配置文件。
e2e/: 端到端 (End-to-End) 测试目录,包含 E2E 测试相关文件。
protractor.conf.js: Protractor (E2E 测试框架) 的配置文件。
tsconfig.json: E2E 测试代码的 TypeScript 配置文件。
... (其他 E2E 测试相关文件)
根目录文件:
angular.json: Angular CLI 配置文件,定义了项目的构建、测试、部署等配置信息。这是非常重要的配置文件。
package.json: npm 包管理配置文件,列出项目依赖的 npm 包、脚本命令等。
package-lock.json 或 yarn.lock: 锁定项目依赖的具体版本,确保团队成员使用一致的依赖版本。
README.md: 项目说明文档,用于介绍项目信息、安装步骤、运行方式等。
.gitignore: Git 版本控制忽略文件,指定不需要纳入版本控制的文件或目录。
tsconfig.json: 根目录的 TypeScript 配置文件,通常用于配置编辑器和 TypeScript 语言服务。
src/ 目录详解:应用的源代码核心src/ 目录是 Angular 项目的核心,我们绝大部分的开发工作都将围绕这个目录展开。深入理解 src/ 目录下的各个子目录和文件,对于高效开发 Angular 应用至关重要。
src/app/ 目录:应用的核心模块src/app/ 目录是 Angular 应用的核心模块,它包含了应用的组件、模块、服务等核心代码。良好的 app/ 目录结构是构建可维护 Angular 应用的关键。
推荐的 src/app/ 目录结构:
graph TD subgraph src/app/ app.component.* --> 根组件文件 app.module.ts --> 根模块文件 app-routing.module.ts --> 根路由模块文件 core/ --> 核心模块 shared/ --> 共享模块 features/ --> 特性模块 services/ --> 应用级服务 models/ --> 数据模型 enums/ --> 枚举类型 constants/ --> 常量 utils/ --> 工具函数 guards/ --> 路由守卫 interceptors/ --> HTTP 拦截器 end core --> core.module.ts & services & ... shared --> shared.module.ts & components & directives & pipes & ... features --> feature1 & feature2 & ... feature1 --> feature1.module.ts & components & services & ... feature2 --> feature2.module.ts & components & services & ...
app.component.*: 应用的根组件文件,是整个应用的入口组件。通常只负责最外层的布局和路由出口。
app.component.html: 根组件的模板。
app.component.ts: 根组件的组件类。
app.component.scss 或 app.component.css: 根组件的样式文件。
app.component.spec.ts: 根组件的单元测试文件。
app.module.ts: 应用的根模块文件,是 Angular 应用的入口模块。在这里声明和导入应用需要的模块、组件、服务等。
app-routing.module.ts: 应用的根路由模块文件(如果使用了路由)。在这里定义应用的路由配置。
core/ 模块: 核心模块,通常包含应用级别的单例服务、全局配置、拦截器、守卫等。这些服务和功能在整个应用中共享,且只应该被加载一次。
core.module.ts: 核心模块的定义文件。
services/: 核心模块的服务,例如认证服务、配置服务、日志服务等。
interceptors/: HTTP 拦截器,用于处理 HTTP 请求和响应。
guards/: 路由守卫,用于控制路由访问权限。
shared/ 模块: 共享模块,包含在多个特性模块中共享的组件、指令、管道等。共享模块不应该依赖于任何特性模块或核心模块的服务。
shared.module.ts: 共享模块的定义文件。
components/: 共享组件,例如通用按钮、对话框、表单控件等。
directives/: 共享指令,例如自定义属性指令、结构指令等。
pipes/: 共享管道,例如日期格式化管道、货币格式化管道等。
features/ 目录: 特性模块目录,按照应用的功能特性进行划分。每个特性模块负责一个特定的业务功能,例如用户管理、订单管理、产品管理等。
feature1/, feature2/, ...: 不同的特性模块文件夹。
feature1.module.ts: 特性模块的定义文件。
components/: 特性模块的组件。
services/: 特性模块的服务(通常只在特性模块内部使用)。
routing.module.ts 或 feature1-routing.module.ts: 特性模块的路由模块(如果需要)。
services/ 目录: 存放应用级别的服务,这些服务通常不是单例的,并且可能被多个模块或组件使用,但与核心模块的单例服务有所区别,例如数据服务、API 服务等。
models/ 目录: 存放应用中使用的数据模型 (Interfaces 或 Classes)。
enums/ 目录: 存放应用中使用的枚举类型。
constants/ 目录: 存放应用中使用的常量。
utils/ 目录: 存放通用的工具函数。
guards/ 目录: 存放路由守卫。
interceptors/ 目录: 存放 HTTP 拦截器。
代码实践:src/app/app.module.ts (根模块)
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { CoreModule } from './core/core.module'; import { SharedModule } from './shared/shared.module'; import { Feature1Module } from './features/feature1/feature1.module'; import { Feature2Module } from './features/feature2/feature2.module'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule, CoreModule, // 导入核心模块 SharedModule, // 导入共享模块 Feature1Module, // 导入特性模块 Feature1 Feature2Module // 导入特性模块 Feature2 ], providers: [], // 应用级服务通常在 CoreModule 中提供 bootstrap: [AppComponent] }) export class AppModule { }
代码实践:src/app/core/core.module.ts (核心模块)
import { NgModule, Optional, SkipSelf } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AuthService } from './services/auth.service'; import { ConfigService } from './services/config.service'; import { LoggerService } from './services/logger.service'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthInterceptor } from './interceptors/auth.interceptor'; import { EnsureModuleLoadedOnceGuard } from './guards/ensure-module-loaded-once.guard'; @NgModule({ imports: [ CommonModule // 导入 CommonModule,提供 *ngIf, *ngFor 等指令 ], providers: [ AuthService, // 提供单例服务 ConfigService, LoggerService, { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true // 允许多个拦截器 } ] }) export class CoreModule extends EnsureModuleLoadedOnceGuard { // 确保 CoreModule 只被加载一次 constructor(@Optional() @SkipSelf() parentModule: CoreModule) { super(parentModule); } }
代码实践:src/app/shared/shared.module.ts (共享模块)
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; // 导入表单模块 import { ButtonComponent } from './components/button/button.component'; import { InputComponent } from './components/input/input.component'; import { MyCustomDirective } from './directives/my-custom.directive'; import { MyCustomPipe } from './pipes/my-custom.pipe'; @NgModule({ imports: [ CommonModule, FormsModule, // 导入 FormsModule ReactiveFormsModule // 导入 ReactiveFormsModule ], declarations: [ ButtonComponent, InputComponent, MyCustomDirective, MyCustomPipe ], exports: [ // 导出共享组件、指令、管道、模块,以便其他模块使用 CommonModule, FormsModule, ReactiveFormsModule, ButtonComponent, InputComponent, MyCustomDirective, MyCustomPipe ] }) export class SharedModule { }
代码实践:src/app/features/feature1/feature1.module.ts (特性模块)
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Feature1RoutingModule } from './feature1-routing.module'; // 特性模块的路由模块 import { ComponentAComponent } from './components/component-a/component-a.component'; import { ServiceForFeature1Service } from './services/service-for-feature1.service'; import { SharedModule } from '../../shared/shared.module'; // 导入 SharedModule @NgModule({ imports: [ CommonModule, Feature1RoutingModule, SharedModule // 导入 SharedModule,可以使用共享组件和模块 ], declarations: [ ComponentAComponent ], providers: [ ServiceForFeature1Service // 特性模块的服务 ] }) export class Feature1Module { }
src/assets/ 目录:静态资源src/assets/ 目录用于存放静态资源文件,例如图片、图标、JSON 数据文件、字体文件等。这些文件在构建过程中会被复制到最终的 dist 目录,并且可以通过相对路径或绝对路径在应用中访问。
代码实践:访问 assets/ 目录下的资源
在组件的模板中,可以使用相对路径访问 assets/ 目录下的图片:
<img src="assets/https://www.aiknowledge.cn/images/Angular/logo.png" alt="Logo">
在组件的 TypeScript 代码中,可以使用 HttpClient 加载 assets/ 目录下的 JSON 数据文件:
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class DataService { constructor(private http: HttpClient) { } getData() { return this.http.get<any>('assets/data/data.json'); } }
src/environments/ 目录:环境配置src/environments/ 目录用于存放不同环境(例如开发环境、生产环境、测试环境)的配置信息。Angular CLI 默认提供了 environment.ts (开发环境) 和 environment.prod.ts (生产环境) 两个配置文件。
代码实践:src/environments/environment.ts (开发环境配置)
export const environment = { production: false, // 指示是否为生产环境 apiUrl: 'http://localhost:3000/api' // 开发环境 API 地址 };
代码实践:src/environments/environment.prod.ts (生产环境配置)
export const environment = { production: true, // 指示是否为生产环境 apiUrl: 'https://api.example.com/api' // 生产环境 API 地址 };
在应用代码中,可以通过导入 environment 对象来访问当前环境的配置信息:
import { environment } from 'src/environments/environment'; @Injectable({ providedIn: 'root' }) export class MyService { constructor(private http: HttpClient) { console.log('Current API URL:', environment.apiUrl); // 根据环境输出不同的 API 地址 } getData() { return this.http.get(`${environment.apiUrl}/data`); } }
Angular CLI 的构建命令会根据 --configuration 参数选择不同的环境配置文件。例如,ng build --configuration=production 会使用 environment.prod.ts 作为生产环境配置进行构建。
src/index.html:应用入口 HTMLsrc/index.html 是 Angular 应用的入口 HTML 文件。浏览器会加载这个文件,然后 Angular 应用会挂载到 <app-root> 标签中。
代码实践:src/index.html
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>MyApp</title> <!-- 应用标题 --> <base href="/"> <!-- 应用的基础 URL --> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <!-- 网站图标 --> </head> <body> <app-root></app-root> <!-- Angular 应用挂载点 --> </body> </html>
通常情况下,我们不需要修改 index.html 文件,除非需要添加一些全局性的 meta 标签、第三方 CSS 库或 JavaScript 脚本等。
src/main.ts:应用启动文件src/main.ts 是 Angular 应用的启动文件。它负责启动 Angular 应用模块,通常是 AppModule。
代码实践:src/main.ts
import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); // 在生产环境下启用生产模式,提高性能 } platformBrowserDynamic().bootstrapModule(AppModule) // 启动 AppModule .catch(err => console.error(err));
main.ts 文件主要完成以下工作:
根据 environment.production 判断是否启用生产模式 (enableProdMode())。生产模式会禁用一些开发环境的检查和警告,从而提高应用性能。
使用 platformBrowserDynamic().bootstrapModule(AppModule) 启动 AppModule,从而启动整个 Angular 应用。
src/polyfills.ts:浏览器兼容性补丁src/polyfills.ts 文件用于提供浏览器兼容性补丁 (polyfills)。由于不同的浏览器对 Web 标准的支持程度不同,为了让 Angular 应用在各种浏览器上都能正常运行,可能需要引入一些 polyfills 来弥补旧版本浏览器缺失的功能。
代码实践:src/polyfills.ts
/** * This file includes polyfills needed by Angular to run in different browsers. * ... (省略默认的 polyfills) ... */ /*************************************************************************************************** * APPLICATION IMPORTS */
通常情况下,Angular CLI 已经配置了一些常用的 polyfills,我们无需手动修改 polyfills.ts 文件。如果需要支持更旧版本的浏览器,或者使用了某些新的 Web API,可能需要根据实际情况添加额外的 polyfills。
src/styles.scss 或 src/styles.css:全局样式src/styles.scss 或 src/styles.css 是全局样式文件。在这里定义的样式会应用到整个 Angular 应用。通常用于定义全局样式变量、通用样式规则、第三方 CSS 库的导入等。
代码实践:src/styles.scss
/* 全局样式变量 */ $primary-color: #3f51b5; $accent-color: #e91e63; /* 全局样式规则 */ body { font-family: Roboto, Arial, sans-serif; margin: 0; } /* 导入第三方 CSS 库 */ @import 'node_modules/normalize.css/normalize.css';
建议使用 SCSS 或其他 CSS 预处理器来编写全局样式,以便使用变量、mixin、嵌套等高级特性,提高样式代码的可维护性。
src/test.ts:单元测试入口src/test.ts 是单元测试的入口文件。它负责配置测试环境、加载测试用例,并启动测试运行器 (Karma)。
代码实践:src/test.ts
// ... (省略默认的测试环境配置) ... declare const require: any; // 自动加载所有以 `.spec.ts` 结尾的文件 const context = require.context('./', true, /\.spec\.ts$/); context.keys().map(context);
通常情况下,我们不需要修改 test.ts 文件,除非需要自定义测试环境配置或测试用例加载方式。
src/tsconfig.app.json, src/tsconfig.spec.json, tsconfig.json:TypeScript 配置文件TypeScript 配置文件 (tsconfig.json) 用于配置 TypeScript 编译器的行为,例如指定编译选项、包含/排除文件等。Angular 项目中通常包含多个 tsconfig.json 文件,用于不同的编译场景。
tsconfig.json (根目录): 根目录的 tsconfig.json 文件通常用于配置编辑器和 TypeScript 语言服务,提供代码提示、错误检查等功能。
src/tsconfig.app.json: src/tsconfig.app.json 文件用于配置应用代码的 TypeScript 编译选项。Angular CLI 构建应用时会使用这个配置文件。
src/tsconfig.spec.json: src/tsconfig.spec.json 文件用于配置单元测试代码的 TypeScript 编译选项。单元测试运行器会使用这个配置文件。
e2e/tsconfig.json: e2e/tsconfig.json 文件用于配置端到端测试代码的 TypeScript 编译选项。
代码实践:src/tsconfig.app.json (应用代码 TypeScript 配置)
{ "extends": "../tsconfig.json", // 继承根目录的 tsconfig.json "compilerOptions": { "outDir": "../out-tsc/app", // 输出目录 "types": [] }, "files": [ "main.ts", "polyfills.ts" ], "include": [ "**/*.ts" // 包含所有 .ts 文件 ], "exclude": [ "**/*.spec.ts" // 排除 .spec.ts 文件 (单元测试文件) ] }
通常情况下,我们不需要手动修改 tsconfig.json 文件,除非需要自定义 TypeScript 编译选项,例如修改 target (目标 ECMAScript 版本)、module (模块系统)、baseUrl (模块解析根路径) 等。
e2e/ 目录:端到端测试e2e/ 目录包含端到端 (End-to-End) 测试相关文件。端到端测试也称为集成测试或 UI 测试,它模拟用户在浏览器中的操作,测试整个应用的功能是否正常。Angular CLI 默认使用 Protractor 框架进行 E2E 测试。
protractor.conf.js: Protractor 框架的配置文件,用于配置测试环境、测试用例路径、浏览器驱动等。
tsconfig.json: E2E 测试代码的 TypeScript 配置文件。
... (其他 E2E 测试相关文件,例如测试用例 .e2e-spec.ts 文件)
代码实践:运行 E2E 测试
在项目根目录下,可以使用 Angular CLI 命令 ng e2e 运行 E2E 测试。
除了 src/ 和 e2e/ 目录,Angular 项目根目录还包含一些重要的配置文件和说明文件。
angular.json:Angular CLI 配置文件angular.json 是 Angular CLI 的核心配置文件,定义了项目的构建、测试、部署等配置信息。理解 angular.json 的结构和配置项,对于自定义 Angular 项目的构建流程至关重要。
angular.json 主要配置项:
projects: 定义项目列表,通常只有一个默认项目。
[项目名称]: 项目配置。
projectType: 项目类型,例如 "application" (应用) 或 "library" (库)。
architect: 定义项目构建器 (builders) 和配置。
build: 构建配置。
builder: 使用的构建器,例如 @angular-devkit/build-angular:browser (浏览器应用构建器)。
options: 构建选项,例如 outputPath (输出目录)、index (入口 HTML 文件)、main (入口 TypeScript 文件)、tsConfig (TypeScript 配置文件)、assets (静态资源复制配置)、styles (全局样式文件)、scripts (全局脚本文件) 等。
configurations: 不同环境的构建配置,例如 production (生产环境配置)。
serve: 开发服务器配置。
builder: 使用的开发服务器构建器,例如 @angular-devkit/build-angular:dev-server。
options: 开发服务器选项,例如 browserTarget (目标构建配置)、port (端口号)、proxyConfig (代理配置) 等。
test: 单元测试配置。
builder: 使用的单元测试构建器,例如 @angular-devkit/build-angular:karma。
options: 单元测试选项,例如 main (测试入口文件)、tsConfig (TypeScript 配置文件)、karmaConfig (Karma 配置文件) 等。
e2e: 端到端测试配置。
builder: 使用的端到端测试构建器,例如 @angular-devkit/build-angular:protractor。
options: 端到端测试选项,例如 protractorConfig (Protractor 配置文件)、devServerTarget (开发服务器目标) 等。
schematics: 定义 Angular Schematics 的默认配置。
architect: 定义项目构建器和配置(与 projects.[项目名称].architect 相同,但可以定义多个构建器)。
defaultProject: 默认项目名称。
cli: Angular CLI 的全局配置。