WinddSnow

Engineering-Practice-Angular-Project-Initialization-and-Directory-Structure

字数统计: 5.1k阅读时长: 24 min
2026/08/02

第123课:工程化实践——Angular 项目初始化与目录结构

前两课我们分别搭建了 React 和 Vue 3 的企业级项目骨架。本节课将使用 Angular 17+(Standalone 模式),从零构建一个具备工程化约束的 Angular 单页应用骨架。Angular 与其他框架最大的不同在于它由 Angular CLI 统一管理项目的创建、开发、构建和测试,并提供了严格的项目结构约定。我们将覆盖 Angular CLI 项目初始化、TypeScript 严格配置、ESLint + Prettier 规范(Angular 默认使用自定义的 angular.json 管理 lint)、Husky + lint-staged 提交拦截、环境变量与 HttpClient 封装、以及 Angular 特有的目录结构分层设计原则——包括 Standalone Components、Services、Guards、Resolvers 和惰性加载路由。学完本课,你将能够快速初始化一个结构清晰、约束到位、可立即投入团队开发的 Angular 工程模板。


1. 项目初始化:Angular CLI 与 Standalone 模式

1.1 使用 Angular CLI 创建项目

1
2
3
4
5
# 全局安装 Angular CLI(如已安装可跳过)
npm install -g @angular/cli

# 创建项目(Standalone 模式,SCSS,带路由)
ng new my-angular-app --style=scss --routing=true --standalone=true --ssr=false

参数说明

  • --style=scss:使用 SCSS 作为样式预处理器。
  • --routing=true:自动生成路由配置(app.routes.ts)。
  • --standalone=true:使用 Standalone 模式(Angular 17+ 默认)。每个组件都是独立的,无需 NgModule 包裹。本课基于 Standalone 模式。
  • --ssr=false:不启用服务端渲染(保持为纯客户端 SPA)。

1.2 初始清理

Angular CLI 生成的模板包含一些演示代码。清理后准备骨架:

1
2
3
# 删除默认生成的演示组件(可选,根据需要)
# Angular 17+ 的默认模板已非常精简,通常包含 app.component.ts 和 app.routes.ts
# 无需额外清理,仅需调整内容

生成的 src/app/ 目录结构:

1
2
3
4
5
6
src/app/
├── app.component.ts # 根组件(Standalone)
├── app.component.html # 根组件模板
├── app.component.scss # 根组件样式
├── app.config.ts # 应用配置(取代 app.module.ts)
└── app.routes.ts # 路由配置

app.config.ts 是 Standalone 模式的核心配置文件,用于注册路由、HTTP 客户端等全局服务。它取代了传统 NgModule 中的 importsproviders


2. TypeScript 严格配置

Angular CLI 生成的 tsconfig.json 默认已经包含了 strict: true。我们需要进一步收紧某些选项,并配置路径别名。

2.1 更新根 tsconfig.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "bundler",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
"useDefineForClassFields": false,
"lib": ["ES2022", "dom"],
"paths": {
"@/*": ["src/app/*"]
}
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

Angular 特有的编译器选项

选项 含义
strictInjectionParameters 强制依赖注入的构造函数参数进行类型检查。
strictInputAccessModifiers 检查模板中 @Input 属性的访问修饰符(如是否误用了 private)。
strictTemplates 对组件模板进行严格的类型检查(如检测 *ngIf 中的类型错误)。

2.2 路径别名配置

tsconfig.json 中设置了 paths: { "@/*": ["src/app/*"] }。与 React/Vue 相同,Angular 的构建工具(基于 esbuild 或 Webpack)不会自动读取 tsconfig.jsonpaths。需要在 angular.json 中为构建和开发服务器添加别名配置(仅在使用自定义 Webpack 时支持)——实际上 Angular CLI 在 ng buildng serve 阶段尚不直接支持 TypeScript 路径别名的自动解析。社区有两种解决方案:

  • 方案一(推荐):不使用 @/ 别名,采用 Angular 标准路径。Angular 的模块系统天然支持相对路径和基于 src/app/ 的绝对导入。
  • 方案二:如果团队坚持使用 @/ 别名,可以通过 angular.json 中的 build.options 配合第三方工具(如 tsconfig-paths-webpack-plugin),但会增加配置复杂度。

本课采用 Angular 标准路径src/app/ 作为根,不设置路径别名)。这在 Angular 社区是普遍做法,避免了额外的工具依赖。


3. 代码规范:ESLint + Prettier + Husky + lint-staged

Angular CLI 创建的项目默认不包含 ESLint(Angular 10 后已移除 tslint)。需要手动添加。

3.1 安装 ESLint 相关依赖

1
2
# Angular ESLint 官方 schematics
ng add @angular-eslint/schematics

此命令会自动安装 @angular-eslint/builder@angular-eslint/eslint-plugin@angular-eslint/template-parser@typescript-eslint/parser@typescript-eslint/eslint-plugineslint 等依赖,并生成 .eslintrc.json 配置文件。

3.2 ESLint 配置

生成的 .eslintrc.json 包含 Angular 和 TypeScript 的推荐规则。我们需要追加 Prettier 集成和自定义规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"root": true,
"ignorePatterns": ["projects/**/*"],
"overrides": [
{
"files": ["*.ts"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates",
"prettier"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{ "type": "attribute", "prefix": "app", "style": "camelCase" }
],
"@angular-eslint/component-selector": [
"error",
{ "type": "element", "prefix": "app", "style": "kebab-case" }
],
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"no-console": ["warn", { "allow": ["warn", "error"] }]
}
},
{
"files": ["*.html"],
"extends": ["plugin:@angular-eslint/template/recommended", "prettier"],
"rules": {}
}
]
}

与 React/Vue 配置的关键差异

  • Angular ESLint 使用 overrides 数组分别配置 TypeScript 文件和 HTML 模板文件。
  • 使用 @angular-eslint 系列插件(而非 react-hooksvue 插件)。
  • 必须包含 @angular-eslint/template/process-inline-templates 以正确解析组件内联模板。

3.3 Prettier 配置

安装 Prettier:

1
pnpm add -D prettier eslint-config-prettier

创建 .prettierrc

1
2
3
4
5
6
7
8
9
10
11
12
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"quoteProps": "as-needed",
"trailingComma": "all",
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf"
}

创建 .prettierignore

1
2
3
4
dist/
node_modules/
coverage/
.angular/

3.4 Husky + lint-staged 配置

1
2
pnpm add -D husky lint-staged
npx husky init

编辑 .husky/pre-commit

1
npx lint-staged

package.json 中添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"lint-staged": {
"*.{ts,js}": ["eslint --fix", "prettier --write"],
"*.html": ["prettier --write"],
"*.{css,scss,md,json}": ["prettier --write"]
},
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"lint": "ng lint",
"format": "prettier --write \"src/**/*.{ts,html,css,scss,md,json}\"",
"prepare": "husky"
}
}

注意:Angular 的 lint 使用 ng lint(调用 angular.json 中配置的 ESLint Builder),而非直接调用 eslint 命令。这确保了 Angular 特有的文件解析和配置集成。


4. 环境变量与 HttpClient 封装

4.1 环境变量配置

Angular 通过 src/environments/ 目录管理环境变量。CLI 生成的项目默认包含两个文件:

1
2
3
4
5
// src/environments/environment.ts(开发环境)
export const environment = {
production: false,
apiBaseUrl: 'http://localhost:3000/api',
};
1
2
3
4
5
// src/environments/environment.prod.ts(生产环境)
export const environment = {
production: true,
apiBaseUrl: 'https://api.example.com',
};

Angular CLI 在构建时通过 fileReplacementsangular.json 中配置)自动将 environment.ts 替换为 environment.prod.ts

4.2 HttpClient 封装(拦截器)

Angular 使用 HttpClient 服务进行 HTTP 请求,通过 拦截器(Interceptor) 统一注入 Token、处理错误。

创建 src/app/core/interceptors/auth.interceptor.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { HttpInterceptorFn } from '@angular/common/http';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = localStorage.getItem('token');

if (token) {
const cloned = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
return next(cloned);
}

return next(req);
};

创建 src/app/core/interceptors/error.interceptor.ts

1
2
3
4
5
6
7
8
9
10
11
12
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';

export const errorInterceptor: HttpInterceptorFn = (req, next) => {
return next(req).pipe(
catchError((error: HttpErrorResponse) => {
console.error('HTTP Error:', error.message);
// 可在这里统一处理 401 跳转登录页等
return throwError(() => new Error(error.message));
}),
);
};

src/app/app.config.ts 中注册拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { authInterceptor } from './core/interceptors/auth.interceptor';
import { errorInterceptor } from './core/interceptors/error.interceptor';

export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(
withInterceptors([authInterceptor, errorInterceptor]),
),
],
};

4.3 API 服务示例

创建 src/app/services/user.service.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from '../../environments/environment';

export interface User {
id: number;
name: string;
email: string;
role: string;
}

@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
private apiUrl = `${environment.apiBaseUrl}/users`;

getUsers(page = 1, size = 10): Observable<User[]> {
return this.http.get<User[]>(this.apiUrl, { params: { page, size } });
}

getUserById(id: number): Observable<User> {
return this.http.get<User>(`${this.apiUrl}/${id}`);
}

createUser(data: Omit<User, 'id'>): Observable<User> {
return this.http.post<User>(this.apiUrl, data);
}

updateUser(id: number, data: Partial<User>): Observable<User> {
return this.http.patch<User>(`${this.apiUrl}/${id}`, data);
}

deleteUser(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
}

与 React/Vue 封装的差异

  • Angular 使用 HttpClient 内置的 params 配置自动处理查询字符串,无需手动拼接 URLSearchParams
  • Angular 的拦截器(HttpInterceptorFn)是函数式、可组合的,通过 withInterceptors 注册。这是 Angular 15+ 推荐的新式写法(之前是类式 @Injectable 拦截器)。

5. 目录结构分层设计

5.1 推荐目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
my-angular-app/
├── public/
│ └── favicon.svg
├── src/
│ ├── app/
│ │ ├── core/ # 核心模块(拦截器、守卫、全局服务)
│ │ │ ├── interceptors/
│ │ │ │ ├── auth.interceptor.ts
│ │ │ │ └── error.interceptor.ts
│ │ │ └── guards/
│ │ │ └── auth.guard.ts
│ │ ├── shared/ # 共享模块(UI 组件、指令、管道)
│ │ │ ├── components/
│ │ │ │ ├── ui/ # 基础 UI 组件(Button、Input、Modal)
│ │ │ │ └── layout/ # 布局组件(Header、Sidebar、Footer)
│ │ │ └── pipes/
│ │ │ └── truncate.pipe.ts
│ │ ├── features/ # 特性模块(按业务领域组织)
│ │ │ ├── home/
│ │ │ │ └── home.component.ts
│ │ │ ├── users/
│ │ │ │ ├── user-list/
│ │ │ │ │ └── user-list.component.ts
│ │ │ │ ├── user-detail/
│ │ │ │ └── user.service.ts(或放在 services/ 下)
│ │ │ └── not-found/
│ │ │ └── not-found.component.ts
│ │ ├── services/ # 共享服务(可选,部分服务可放在 features/ 内)
│ │ │ └── user.service.ts
│ │ ├── app.component.ts
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ ├── app.config.ts
│ │ └── app.routes.ts
│ ├── environments/
│ │ ├── environment.ts
│ │ └── environment.prod.ts
│ ├── assets/ # 静态资源
│ ├── styles/
│ │ ├── _variables.scss # SCSS 变量
│ │ └── styles.scss # 全局样式
│ ├── index.html
│ └── main.ts
├── .eslintrc.json
├── .prettierrc
├── .prettierignore
├── .gitignore
├── angular.json
├── package.json
├── pnpm-lock.yaml
└── tsconfig.json

5.2 分层原则

  • **core/**:存放仅被 AppComponent 或根路由使用的单例服务、拦截器、守卫。它们通常是全局性的,与具体业务无关。
  • **shared/**:存放可复用的 UI 组件(ButtonComponentCardComponent)、自定义管道(truncate)、通用指令。这些组件通过 standalone: trueimports 声明其依赖,可以被任何特性组件直接导入。
  • **features/**:按业务领域划分的页面组件和服务。例如 users/ 下包含用户列表、用户详情和用户服务。特性内的组件可以自由引用 shared/ 中的组件和 core/ 中的全局服务。
  • **services/**:存放跨特性共享的服务。也可以将服务就近放置在所属的 features/ 目录下(Angular 社区两种风格均可,本课推荐按业务领域就近放置)。
  • **environments/**:环境配置文件,由 Angular CLI 在构建时替换。

5.3 命名约定

类型 规则 示例
组件文件 kebab-case + .component.ts user-list.component.ts
组件选择器 kebab-case,以 app- 为前缀 app-user-list
服务文件 kebab-case + .service.ts user.service.ts
拦截器文件 kebab-case + .interceptor.ts auth.interceptor.ts
守卫文件 kebab-case + .guard.ts auth.guard.ts
管道文件 kebab-case + .pipe.ts truncate.pipe.ts
环境文件 environment.ts / environment.prod.ts

6. 路由配置:惰性加载与 Standalone 路由

Angular 17+ 的 Standalone 路由通过 loadComponentloadChildren 实现惰性加载。

6.1 根路由配置

编辑 src/app/app.routes.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { Routes } from '@angular/router';

export const routes: Routes = [
{
path: '',
loadComponent: () =>
import('./features/home/home.component').then((m) => m.HomeComponent),
title: '首页',
},
{
path: 'users',
loadComponent: () =>
import('./features/users/user-list/user-list.component').then(
(m) => m.UserListComponent,
),
title: '用户管理',
},
{
path: 'users/:id',
loadComponent: () =>
import('./features/users/user-detail/user-detail.component').then(
(m) => m.UserDetailComponent,
),
title: '用户详情',
},
{
path: '**',
loadComponent: () =>
import('./features/not-found/not-found.component').then(
(m) => m.NotFoundComponent,
),
title: '页面未找到',
},
];

关键点

  • loadComponent 替代了传统 NgModule 模式中的 loadChildren。它直接返回一个 Promise,解析为 Standalone 组件。
  • Angular CLI 会自动将每个 import() 的目标组件拆分为独立的 JavaScript chunk,实现路由级惰性加载。
  • title 属性是 Angular 14+ 引入的,用于设置页面标题(会自动更新 document.title)。

6.2 根组件模板

编辑 src/app/app.component.html

1
2
3
4
5
6
7
8
9
10
<header>
<nav>
<a routerLink="/">首页</a>
<a routerLink="/users">用户管理</a>
</nav>
</header>

<main>
<router-outlet></router-outlet>
</main>

6.3 Standalone 组件的最小结构

创建 src/app/features/home/home.component.ts(示例):

1
2
3
4
5
6
7
8
import { Component } from '@angular/core';

@Component({
selector: 'app-home',
standalone: true,
template: `<h2>🏠 首页</h2>`,
})
export class HomeComponent {}

与 React/Vue 组件的差异

  • Angular Standalone 组件必须标记 standalone: true
  • 模板可以是外部文件(templateUrl)或内联字符串(template),样式同理。
  • 选择器(selector)对应 HTML 标签名。根组件 AppComponentselector 通常为 app-root,在 index.html 中对应 <app-root></app-root>。特性组件的选择器则在其他组件的模板中使用。

7. 全局样式:SCSS 变量与基础重置

7.1 SCSS 变量

创建 src/styles/_variables.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 颜色
$color-primary: #4f46e5;
$color-primary-hover: #4338ca;
$color-success: #10b981;
$color-warning: #f59e0b;
$color-error: #ef4444;
$color-bg: #f8fafc;
$color-text: #1e293b;
$color-text-secondary: #64748b;
$color-border: #e2e8f0;

// 间距
$space-1: 4px;
$space-2: 8px;
$space-3: 12px;
$space-4: 16px;
$space-6: 24px;
$space-8: 32px;

// 圆角
$radius-sm: 4px;
$radius-md: 8px;
$radius-lg: 12px;

// 字体
$font-family: 'Inter', system-ui, -apple-system, sans-serif;
$font-size-sm: 0.875rem;
$font-size-base: 1rem;
$font-size-lg: 1.125rem;
$font-size-xl: 1.25rem;
$font-size-2xl: 1.5rem;

7.2 全局样式

src/styles/styles.scss 中(确保 angular.jsonarchitect.build.options.styles 中包含此文件):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@use 'variables' as *;

*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}

body {
font-family: $font-family;
font-size: $font-size-base;
color: $color-text;
background-color: $color-bg;
-webkit-font-smoothing: antialiased;
}

a {
color: $color-primary;
text-decoration: none;
&:hover {
color: $color-primary-hover;
}
}

注意:Angular 使用 @use 语法引入 SCSS 文件(而非 @import,后者在 Sass 中已弃用)。需要确保 angular.jsonstylePreprocessorOptions.includePaths 包含 src/styles 路径,以便在组件 SCSS 中直接 @use 'variables' 使用全局变量。

angular.json 中添加预处理器配置:

1
2
3
4
5
6
7
8
9
"architect": {
"build": {
"options": {
"stylePreprocessorOptions": {
"includePaths": ["src/styles"]
}
}
}
}

8. 三个框架的项目初始化对比总结

维度 React (Vite) Vue 3 (Vite) Angular (CLI)
脚手架工具 pnpm create vite pnpm create vite ng new
类型检查命令 tsc --noEmit vue-tsc --noEmit ng build(内置 tsc + Angular 模板检查)
ESLint 配置 eslint:recommended + @typescript-eslint + react-hooks 同左 + plugin:vue/vue3-recommended + vue-eslint-parser @angular-eslint + template overrides
模块系统 ESM(Vite) ESM(Vite) ESM + Angular Modules / Standalone
路由模式 react-router-dom v6 vue-router v4 @angular/router(内置)
状态管理 Zustand / Redux Toolkit Pinia Services + RxJS / NgRx
HTTP 客户端 自定义封装 fetch 自定义封装 fetch HttpClient(内置拦截器)
环境变量 .envVITE_ 前缀) .envVITE_ 前缀) environments/ 文件夹 + 构建替换
路径别名 @/*src/*(Vite + tsconfig 同步配置) 同左 社区方案(@/*src/app/*),不推荐

课后练习

一、概念自测(选择题 / 填空题)

  1. (单选) Angular CLI 生成的项目中,用于定义应用全局配置(路由、HTTP 拦截器等)的文件是?
    A. app.module.ts
    B. main.ts
    C. app.config.ts
    D. angular.json

  2. (单选) Angular 17+ 的 Standalone 路由惰性加载使用哪个属性?
    A. loadChildren
    B. loadComponent
    C. component
    D. lazyComponent

  3. (填空) Angular 中,要统一为所有 HTTP 请求添加 Authorization 头,应创建一个 ______ 函数,并在 provideHttpClient(withInterceptors(...)) 中注册。

  4. (多选) 关于 Angular 项目 TypeScript 配置的描述,哪些是正确的?
    A. tsconfig.jsonstrict: true 已包含模板严格检查。
    B. angularCompilerOptions.strictTemplates 用于对组件模板进行类型检查。
    C. strictInjectionParameters 强制依赖注入的构造函数参数类型检查。
    D. Angular CLI 会自动读取 tsconfig.jsonpaths 并配置 Webpack/esbuild 别名。

二、AI 编程任务:编写面向 AI 的提示词

场景:你需要从零创建一个 Angular 17+ Standalone 项目骨架。要求如下:

  • 使用 ng new 创建项目,SCSS 样式,启用路由,Standalone 模式。
  • 配置 TypeScript 严格模式(含 angularCompilerOptions 严格检查)。
  • 安装并配置 ESLint(@angular-eslint/schematics)和 Prettier(singleQuote: true, semi: true, trailingComma: 'all'),ESLint extends 包含 @angular-eslint/recommended@typescript-eslint/recommendedprettier
  • 配置 Husky + lint-staged(匹配 *.ts*.html*.scss 等)。
  • 创建 src/environments/ 环境变量(开发与生产不同的 apiBaseUrl)。
  • 创建 auth.interceptor.tserror.interceptor.ts,并在 app.config.ts 中注册。
  • 创建 UserService(使用 HttpClient,封装 GET/POST/PATCH/DELETE 方法)。
  • 配置惰性加载路由(首页 /、用户列表 /users、用户详情 /users/:id、404 页面),每个路由使用 loadComponent 并设置 title
  • 创建完整的目录结构(core/interceptors/shared/components/ui/features/services/)。
  • 创建全局 SCSS 变量(_variables.scss)和基础样式重置。

任务要求:请写出一段完整的中文提示词,发送给 AI,使其生成符合上述要求的完整项目初始化的所有配置文件和核心代码文件。提示词中需明确指定 Angular 特有的配置项(app.config.tsHttpInterceptorFnprovideHttpClient(withInterceptors(...))loadComponentangularCompilerOptions.strictTemplates 等)。

三、Agent 模式下的提示词示例

你是一个资深前端开发 Agent。请创建一个 Angular 17+ Standalone 项目骨架。需要生成/修改以下文件:

  1. 使用 ng new 创建项目(SCSS,路由,Standalone),安装额外依赖:huskylint-stagedprettiereslint-config-prettier。使用 ng add @angular-eslint/schematics 初始化 ESLint。
  2. tsconfig.jsonstrict: trueangularCompilerOptionsstrictTemplates: truestrictInjectionParameters: truestrictInputAccessModifiers: true
  3. .eslintrc.json:overrides 中 extends 包含 plugin:@angular-eslint/recommended@typescript-eslint/recommendedprettier。rules 禁止 console.log(允许 warn/error)。
  4. .prettierrcsingleQuote: truesemi: truetrailingComma: 'all'printWidth: 100
  5. .husky/pre-commitnpx lint-staged
  6. package.jsonlint-staged 配置(*.ts 执行 eslint --fix + prettier --write*.html/*.scss/*.md/*.json 执行 prettier --write)。scripts 包含 startbuildlintformatprepare
  7. src/environments/environment.tssrc/environments/environment.prod.ts:分别设置 apiBaseUrl
  8. src/app/core/interceptors/auth.interceptor.tssrc/app/core/interceptors/error.interceptor.tsHttpInterceptorFn 函数,authInterceptor 注入 token,errorInterceptor 统一错误处理。
  9. src/app/app.config.ts:使用 provideHttpClient(withInterceptors([authInterceptor, errorInterceptor])) 注册拦截器。
  10. src/app/services/user.service.ts@Injectable({ providedIn: 'root' }),使用 inject(HttpClient),封装 GET/POST/PATCH/DELETE 方法。
  11. src/app/app.routes.ts:使用 loadComponent 惰性加载首页、用户列表、用户详情、404 页面,设置 title
  12. src/app/features/home/home.component.tssrc/app/features/users/user-list/user-list.component.tssrc/app/features/users/user-detail/user-detail.component.tssrc/app/features/not-found/not-found.component.ts:简单的 Standalone 占位组件(standalone: true,内联模板)。
  13. src/app/app.component.html:包含 <router-outlet> 和导航链接。
  14. src/styles/_variables.scsssrc/styles/styles.scss:全局 SCSS 变量和基础样式重置。angular.json 中配置 stylePreprocessorOptions.includePaths
  15. 创建完整目录结构说明(core/interceptors/shared/components/ui/features/services/)。
    所有文件添加 JSDoc 注释,确保可直接运行。完成后列出所有文件内容。

课后练习答案

一、概念自测答案

  1. C

    • 解析:Angular 17+ 使用 app.config.ts 定义应用全局配置(路由提供者、HTTP 客户端提供者等)。app.module.ts 是传统 NgModule 模式的文件,Standalone 模式中不存在。
  2. B

    • 解析:Angular 17+ 的 Standalone 路由使用 loadComponent 惰性加载单个组件。loadChildren 用于惰性加载子路由配置。
  3. **HttpInterceptorFn**(拦截器函数)

    • 解析:Angular 15+ 的函数式拦截器 HttpInterceptorFn 用于统一处理 HTTP 请求和响应,通过 withInterceptors 注册。
  4. B、C

    • 解析:A 错误,模板严格检查需要额外在 angularCompilerOptions 中配置 strictTemplates: true,它不是 strict: true 的一部分。D 错误,Angular CLI 不会自动读取 tsconfig.jsonpaths 来配置别名(需要自定义 Webpack 插件或放弃别名)。

二、AI 编程任务参考答案(提示词示例)

示例提示词
“请从零创建一个 Angular 17+ Standalone 项目骨架。要求:

  • 使用 ng new 创建项目(SCSS + 路由 + Standalone)。
  • TypeScript 严格模式(含 angularCompilerOptions 严格检查)。
  • ESLint(@angular-eslint)+ Prettier + Husky + lint-staged。
  • 环境变量(environments 文件夹)+ HttpClient 拦截器(auth + error)+ UserService。
  • 惰性加载路由(首页/用户列表/用户详情/404)+ Standalone 占位组件。
  • 全局 SCSS 变量和基础样式重置。
  • 完整目录结构(core/interceptors/、shared/components/ui/、features/、services/)。
  • 输出所有配置文件、拦截器、服务、路由、组件和样式的完整内容。”
CATALOG
  1. 1. 第123课:工程化实践——Angular 项目初始化与目录结构
    1. 1.1. 1. 项目初始化:Angular CLI 与 Standalone 模式
      1. 1.1.1. 1.1 使用 Angular CLI 创建项目
      2. 1.1.2. 1.2 初始清理
    2. 1.2. 2. TypeScript 严格配置
      1. 1.2.1. 2.1 更新根 tsconfig.json
      2. 1.2.2. 2.2 路径别名配置
    3. 1.3. 3. 代码规范:ESLint + Prettier + Husky + lint-staged
      1. 1.3.1. 3.1 安装 ESLint 相关依赖
      2. 1.3.2. 3.2 ESLint 配置
      3. 1.3.3. 3.3 Prettier 配置
      4. 1.3.4. 3.4 Husky + lint-staged 配置
    4. 1.4. 4. 环境变量与 HttpClient 封装
      1. 1.4.1. 4.1 环境变量配置
      2. 1.4.2. 4.2 HttpClient 封装(拦截器)
      3. 1.4.3. 4.3 API 服务示例
    5. 1.5. 5. 目录结构分层设计
      1. 1.5.1. 5.1 推荐目录结构
      2. 1.5.2. 5.2 分层原则
      3. 1.5.3. 5.3 命名约定
    6. 1.6. 6. 路由配置:惰性加载与 Standalone 路由
      1. 1.6.1. 6.1 根路由配置
      2. 1.6.2. 6.2 根组件模板
      3. 1.6.3. 6.3 Standalone 组件的最小结构
    7. 1.7. 7. 全局样式:SCSS 变量与基础重置
      1. 1.7.1. 7.1 SCSS 变量
      2. 1.7.2. 7.2 全局样式
    8. 1.8. 8. 三个框架的项目初始化对比总结
    9. 1.9. 课后练习
      1. 1.9.1. 一、概念自测(选择题 / 填空题)
      2. 1.9.2. 二、AI 编程任务:编写面向 AI 的提示词
      3. 1.9.3. 三、Agent 模式下的提示词示例
    10. 1.10. 课后练习答案
      1. 1.10.1. 一、概念自测答案
      2. 1.10.2. 二、AI 编程任务参考答案(提示词示例)