Angular 14 实战:从入门到精通的完整项目指南

Angular 14 实战:从入门到精通的完整项目指南

【免费下载链接】Angular-GettingStarted Sample Angular application used in the "Angular: Getting Started" course: http://bit.ly/Angular-GettingStarted 【免费下载链接】Angular-GettingStarted 项目地址: https://gitcode.com/gh_mirrors/an/Angular-GettingStarted

你是否还在为 Angular 项目搭建烦恼?是否想快速掌握组件通信、路由守卫、HTTP 服务等核心技能?本文基于官方示例项目 Angular-GettingStarted,带你从零构建一个功能完整的产品管理系统,涵盖 Angular 14 新特性与最佳实践,代码可直接复用至生产环境。

项目概述:构建企业级 Angular 应用

Angular-GettingStarted 是 Angular 官方入门教程配套项目,采用模块化架构设计,实现了产品列表展示、详情查看、条件筛选、路由懒加载等企业级应用必备功能。通过本项目,你将掌握:

  • ✅ Angular 14 组件化开发范式
  • ✅ 响应式编程与 RxJS 数据流管理
  • ✅ 路由守卫与权限控制
  • ✅ HTTP 拦截器与错误处理
  • ✅ 模块化设计与代码复用
// 核心技术栈概览 (package.json 精简版)
{
  "dependencies": {
    "@angular/core": "^14.0.0",
    "@angular/router": "^14.0.0",
    "@angular/common/http": "^14.0.0",
    "rxjs": "~7.5.0",
    "bootstrap": "^5.1.3"
  }
}

环境搭建:3 步极速启动开发环境

1. 项目克隆与依赖安装

# 克隆项目仓库
git clone https://link.gitcode.com/i/2c273b7c6489ff6b76efd51c06de2e4b.git
cd Angular-GettingStarted/APM-Final-v16

# 安装依赖 (使用国内镜像加速)
npm install --registry=https://registry.npmmirror.com

2. 启动开发服务器

# 启动带热重载的开发服务器
npm start
# 应用将运行在 http://localhost:4200

3. 构建生产版本

# 构建优化后的生产版本
npm run build --prod
# 输出文件位于 dist/apm 目录

⚠️ 注意:如遇 node-sass 安装失败,执行 npm install node-sass --sass_binary_site=https://npmmirror.com/mirrors/node-sass

项目架构:模块化设计最佳实践

目录结构解析

src/
├── app/
│   ├── products/           # 产品功能模块 (独立特性模块)
│   │   ├── product-list/   # 产品列表组件
│   │   ├── product-detail/ # 产品详情组件
│   │   ├── product.service.ts # 产品数据服务
│   │   └── product.routes.ts  # 特性模块路由
│   ├── shared/             # 共享模块 (管道、指令、通用组件)
│   ├── home/               # 首页模块
│   ├── app.routes.ts       # 根路由配置
│   └── app.module.ts       # 根模块
└── assets/                 # 静态资源

核心模块关系图

mermaid

核心功能实现:从组件到服务的全链路开发

1. 数据模型设计 (TypeScript 接口)

// src/app/products/product.ts
export interface IProduct {
  productId: number;          // 产品唯一标识
  productName: string;        // 产品名称
  productCode: string;        // 产品编码
  releaseDate: string;        // 发布日期
  price: number;              // 价格
  description: string;        // 描述
  starRating: number;         // 评分
  imageUrl: string;           // 图片URL
}

2. 产品服务:HTTP 请求与错误处理

// src/app/products/product.service.ts (核心代码)
@Injectable({ providedIn: 'root' })
export class ProductService {
  private productUrl = 'api/products/products.json';

  constructor(private http: HttpClient) { }

  getProducts(): Observable<IProduct[]> {
    return this.http.get<IProduct[]>(this.productUrl)
      .pipe(
        tap(data => console.log('产品数据:', JSON.stringify(data))),
        catchError(this.handleError) // 统一错误处理
      );
  }

  // 获取单个产品 (通过ID筛选)
  getProduct(id: number): Observable<IProduct | undefined> {
    return this.getProducts()
      .pipe(
        map(products => products.find(p => p.productId === id))
      );
  }

  private handleError(err: HttpErrorResponse): Observable<never> {
    let errorMessage = '';
    if (err.error instanceof ErrorEvent) {
      errorMessage = `客户端错误: ${err.error.message}`;
    } else {
      errorMessage = `服务端错误: ${err.status}, 消息: ${err.message}`;
    }
    console.error(errorMessage);
    return throwError(() => errorMessage);
  }
}

3. 产品列表组件:数据展示与筛选

// src/app/products/product-list.component.ts (核心代码)
@Component({
  templateUrl: './product-list.component.html',
  styleUrls: ['./product-list.component.css']
})
export class ProductListComponent implements OnInit, OnDestroy {
  pageTitle = '产品列表';
  filteredProducts: IProduct[] = [];
  products: IProduct[] = [];
  errorMessage = '';
  sub!: Subscription;

  // 筛选器实现双向绑定
  private _listFilter = '';
  get listFilter(): string { return this._listFilter; }
  set listFilter(value: string) {
    this._listFilter = value;
    this.filteredProducts = this.performFilter(value);
  }

  constructor(private productService: ProductService) {}

  ngOnInit(): void {
    // 订阅产品数据
    this.sub = this.productService.getProducts().subscribe({
      next: products => {
        this.products = products;
        this.filteredProducts = this.products;
      },
      error: err => this.errorMessage = err
    });
  }

  // 筛选逻辑实现
  performFilter(filterBy: string): IProduct[] {
    filterBy = filterBy.toLocaleLowerCase();
    return this.products.filter(product =>
      product.productName.toLocaleLowerCase().includes(filterBy)
    );
  }

  // 清理订阅防止内存泄漏
  ngOnDestroy(): void {
    this.sub.unsubscribe();
  }
}

列表组件模板 (HTML):

<!-- 产品列表筛选器 -->
<div class="mb-3">
  <input type="text" 
         [(ngModel)]="listFilter" 
         placeholder="输入关键词筛选产品">
</div>

<!-- 产品表格 -->
<table class="table table-striped">
  <thead>
    <tr>
      <th>图片</th>
      <th>名称</th>
      <th>价格</th>
      <th>评分</th>
      <th>操作</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let product of filteredProducts">
      <td>
        <img [src]="product.imageUrl" 
             [alt]="product.productName"
             [style.width.px]="imageWidth" 
             [style.margin.px]="imageMargin">
      </td>
      <td>{{ product.productName }}</td>
      <td>{{ product.price | currency:'CNY' }}</td>
      <td>
        <pm-star [rating]="product.starRating" 
                 (ratingClicked)="onRatingClicked($event)">
        </pm-star>
      </td>
      <td>
        <a [routerLink]="['/products', product.productId]">查看详情</a>
      </td>
    </tr>
  </tbody>
</table>

4. 路由配置:懒加载与守卫

// src/app/app.routes.ts (APM-Final-v16 版本)
export const routes: Routes = [
  { path: 'welcome', component: WelcomeComponent },
  { path: '', redirectTo: 'welcome', pathMatch: 'full' },
  
  // 产品模块懒加载配置
  {
    path: 'products',
    loadChildren: () => import('./products/product.routes')
      .then(r => r.PRODUCT_ROUTES)
  },
  
  // 关于页面组件懒加载
  { 
    path: 'about', 
    loadComponent: () => import('./about/about.component')
      .then(c => c.AboutComponent)
  },
  
  // 404 路由
  { path: '**', redirectTo: 'welcome', pathMatch: 'full' }
];

产品模块路由:

// src/app/products/product.routes.ts
import { Routes } from '@angular/router';
import { ProductListComponent } from './product-list.component';
import { ProductDetailComponent } from './product-detail.component';
import { ProductDetailGuard } from './product-detail.guard';

export const PRODUCT_ROUTES: Routes = [
  { path: '', component: ProductListComponent },
  {
    path: ':id',
    component: ProductDetailComponent,
    canActivate: [ProductDetailGuard] // 路由守卫
  }
];

5. 路由守卫:权限控制实现

// src/app/products/product-detail.guard.ts
@Injectable({ providedIn: 'root' })
export class ProductDetailGuard implements CanActivate {
  constructor(private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot): boolean {
    const id = Number(route.paramMap.get('id'));
    // 验证ID是否有效
    if (isNaN(id) || id < 1) {
      alert('无效的产品ID');
      this.router.navigate(['/products']);
      return false;
    }
    return true;
  }
}

高级特性:Angular 14 新功能应用

1. 独立组件与懒加载

Angular 14 引入的独立组件特性允许组件不依赖模块直接使用,结合 loadComponent 实现更高效的懒加载:

// 独立组件路由配置 (app.routes.ts)
{ 
  path: 'about', 
  loadComponent: () => import('./about/about.component')
    .then(c => c.AboutComponent)
}

2. 响应式表单与数据验证

虽然项目中未直接实现表单功能,但可基于现有架构扩展产品添加/编辑功能:

// 产品表单组件示例 (扩展实现)
@Component({
  selector: 'pm-product-form',
  template: `
    <form [formGroup]="productForm" (ngSubmit)="onSubmit()">
      <div class="mb-3">
        <label for="productName">产品名称</label>
        <input id="productName" formControlName="productName" class="form-control">
        <div *ngIf="productName.invalid && productName.touched" class="text-danger">
          产品名称为必填项
        </div>
      </div>
      <!-- 其他表单控件 -->
      <button type="submit" [disabled]="productForm.invalid" class="btn btn-primary">
        保存
      </button>
    </form>
  `
})
export class ProductFormComponent {
  productForm = new FormGroup({
    productName: new FormControl('', [Validators.required, Validators.minLength(3)]),
    price: new FormControl(0, [Validators.required, Validators.min(0)])
  });

  get productName() { return this.productForm.get('productName')!; }

  onSubmit() {
    console.log('表单数据:', this.productForm.value);
    // 提交逻辑
  }
}

3. 依赖注入优化

Angular 14 简化了服务注册方式,通过 providedIn: 'root' 实现全局单例:

// 服务自动注册 (无需在模块中声明)
@Injectable({ providedIn: 'root' })
export class ProductService { ... }

实战部署:生产环境构建与优化

1. 构建命令与参数

# 生产环境构建 (默认配置)
npm run build --prod

# 自定义构建配置
ng build --configuration production --output-path=dist/prod --base-href=/angular-product-manager/

2. 构建优化策略

优化项配置方式效果
代码压缩optimization: true减小JS/CSS体积约40%
树摇优化默认启用移除未使用代码
懒加载路由路由配置中使用 loadChildren首屏加载时间减少60%
缓存策略添加 --output-hashing=all静态资源长效缓存

3. Nginx 部署配置

# /etc/nginx/sites-available/angular-app
server {
    listen 80;
    server_name angular-app.example.com;
    root /var/www/angular-app/dist/apm;

    # 支持HTML5路由模式
    location / {
        try_files $uri $uri/ /index.html;
    }

    # 缓存静态资源
    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000";
    }
}

问题排查:常见错误与解决方案

错误场景可能原因解决方案
HTTP 404 错误API路径配置错误检查 productUrl 是否指向正确的JSON文件
路由跳转失败路由参数类型错误使用 Number() 确保ID为数字类型
内存泄漏未取消订阅实现 OnDestroy 接口并调用 unsubscribe()
组件不渲染模块未声明组件在对应模块的 declarations 数组中添加组件
样式不生效样式封装问题使用 /deep/ 穿透或 encapsulation: ViewEncapsulation.None

总结与进阶路线

通过本项目实战,你已掌握 Angular 开发核心技能。建议后续学习路径:

  1. 状态管理:集成 NgRx 处理复杂数据流
  2. 单元测试:使用 Jasmine + Karma 编写组件测试
  3. 国际化:实现多语言支持 (ngx-translate)
  4. PWA 改造:添加 Service Worker 支持离线访问
  5. 微前端:使用 Module Federation 实现应用集成

mermaid

项目完整代码:https://link.gitcode.com/i/2c273b7c6489ff6b76efd51c06de2e4b
官方文档:Angular 中文官网

收藏与分享

如果本文对你有帮助,请点赞 👍 + 收藏 ⭐ 支持作者。关注专栏,获取更多 Angular 实战教程!

下期预告:《Angular 性能优化 20 个实战技巧》,深入讲解 Change Detection、渲染优化、网络请求优化等核心内容。

【免费下载链接】Angular-GettingStarted Sample Angular application used in the "Angular: Getting Started" course: http://bit.ly/Angular-GettingStarted 【免费下载链接】Angular-GettingStarted 项目地址: https://gitcode.com/gh_mirrors/an/Angular-GettingStarted

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值