六安市网站建设_网站建设公司_论坛网站_seo优化
2026/1/5 9:17:17 网站建设 项目流程

在 Angular 应用开发中,HTTP 请求是与后端交互的核心环节,而数据共享则是组件间通信的高频需求。如果直接在组件中零散编写 HTTP 请求代码,会导致代码冗余、维护困难,且组件间数据传递易形成 “数据流混乱”。本文将实战演示如何创建一个通用数据服务,既封装 HTTP 请求逻辑,又实现组件间高效的数据共享,兼顾代码复用性与可维护性。

一、核心思路

  1. HTTP 请求封装:基于 Angular 的HttpClient模块,封装 GET/POST/PUT/DELETE 等通用请求方法,统一处理请求头、错误拦截、请求 / 响应拦截。
  2. 数据共享:利用 RxJS 的BehaviorSubject/ReplaySubject实现组件间数据订阅与推送,确保数据变更能实时同步到所有订阅组件。
  3. 单一职责:服务专注于数据处理,组件仅负责视图渲染与用户交互,符合 “关注点分离” 原则。

二、前置准备

  1. 确保已创建 Angular 项目(ng new data-service-demo)。
  2. 导入HttpClientModule到根模块(AppModule):
// app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule } from '@angular/common/http'; // 导入HTTP模块 import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule], // 注册模块 providers: [], bootstrap: [AppComponent] }) export class AppModule { }

三、创建通用数据服务

使用 Angular CLI 创建服务:ng generate service services/data(自动注册到根注入器)。

3.1 基础结构与依赖注入

// src/app/services/data.service.ts import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; import { BehaviorSubject, Observable, throwError } from 'rxjs'; import { catchError, tap } from 'rxjs/operators'; // 定义通用响应类型(适配后端返回格式) export interface ApiResponse<T> { code: number; data: T; message: string; } @Injectable({ providedIn: 'root' // 根级别注入,全局单例 }) export class DataService { // 1. 数据共享:BehaviorSubject(初始值+订阅时推送最新值) private userData$ = new BehaviorSubject<any>(null); public userDataObs$ = this.userData$.asObservable(); // 对外暴露只读Observable // 2. HTTP基础配置 private baseUrl = 'https://api.example.com/v1'; // 后端接口基础地址 private httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('token') || '' // 统一携带token }) }; constructor(private http: HttpClient) { } }

3.2 封装通用 HTTP 请求方法

封装 GET/POST/PUT/DELETE,统一处理错误与响应:

// 错误处理私有方法 private handleError(error: HttpErrorResponse): Observable<never> { let errorMessage = '未知错误'; if (error.error instanceof ErrorEvent) { // 客户端错误(如网络、语法) errorMessage = `客户端错误:${error.error.message}`; } else { // 服务端错误(状态码、响应体) errorMessage = `服务端错误:${error.status} - ${error.message}`; // 可根据状态码做特殊处理(如401未授权、403禁止访问) if (error.status === 401) { // 示例:跳转到登录页 // this.router.navigate(['/login']); errorMessage = '登录状态失效,请重新登录'; } } console.error(errorMessage); return throwError(() => new Error(errorMessage)); } // 封装GET请求 get<T>(endpoint: string, params?: any): Observable<ApiResponse<T>> { return this.http.get<ApiResponse<T>>(`${this.baseUrl}/${endpoint}`, { ...this.httpOptions, params }).pipe( catchError(this.handleError) ); } // 封装POST请求 post<T>(endpoint: string, data: any): Observable<ApiResponse<T>> { return this.http.post<ApiResponse<T>>(`${this.baseUrl}/${endpoint}`, data, this.httpOptions).pipe( catchError(this.handleError) ); } // 封装PUT请求 put<T>(endpoint: string, data: any): Observable<ApiResponse<T>> { return this.http.put<ApiResponse<T>>(`${this.baseUrl}/${endpoint}`, data, this.httpOptions).pipe( catchError(this.handleError) ); } // 封装DELETE请求 delete<T>(endpoint: string): Observable<ApiResponse<T>> { return this.http.delete<ApiResponse<T>>(`${this.baseUrl}/${endpoint}`, this.httpOptions).pipe( catchError(this.handleError) ); }

3.3 封装业务数据方法与数据共享

基于通用 HTTP 方法,封装具体业务逻辑,并通过BehaviorSubject实现数据共享:

// 示例:获取用户列表并共享数据 getUserList(): Observable<ApiResponse<any[]>> { return this.get<any[]>('users').pipe( tap((response) => { // 请求成功后,更新共享数据 this.userData$.next(response.data); }) ); } // 示例:新增用户 addUser(user: any): Observable<ApiResponse<any>> { return this.post<any>('users', user).pipe( tap((response) => { // 新增成功后,更新共享数据(重新获取列表/追加新数据) this.getUserList().subscribe(); // 重新拉取最新列表 }) ); } // 手动更新共享数据(供组件调用) updateSharedUserData(data: any): void { this.userData$.next(data); } // 获取共享数据的最新值(非订阅方式) getCurrentUserData(): any { return this.userData$.value; }

四、组件中使用数据服务

4.1 组件 1:发起请求并订阅共享数据

// src/app/components/user-list/user-list.component.ts import { Component, OnInit } from '@angular/core'; import { DataService } from '../../services/data.service'; @Component({ selector: 'app-user-list', template: ` <div *ngIf="errorMsg" class="error">{{ errorMsg }}</div> <ul> <li *ngFor="let user of userList">{{ user.name }} - {{ user.email }}</li> </ul> <button (click)="refreshUserList()">刷新列表</button> ` }) export class UserListComponent implements OnInit { userList: any[] = []; errorMsg = ''; constructor(private dataService: DataService) { } ngOnInit(): void { // 1. 订阅共享数据(数据变更时自动更新) this.dataService.userDataObs$.subscribe({ next: (data) => { if (data) this.userList = data; }, error: (err) => this.errorMsg = err.message }); // 2. 首次加载数据 this.refreshUserList(); } refreshUserList(): void { this.dataService.getUserList().subscribe({ error: (err) => this.errorMsg = err.message }); } }

4.2 组件 2:修改数据并同步共享状态

// src/app/components/add-user/add-user.component.ts import { Component } from '@angular/core'; import { DataService } from '../../services/data.service'; @Component({ selector: 'app-add-user', template: ` <div> <input type="text" [(ngModel)]="newUser.name" placeholder="姓名"> <input type="email" [(ngModel)]="newUser.email" placeholder="邮箱"> <button (click)="addNewUser()">新增用户</button> <div *ngIf="msg" class="msg">{{ msg }}</div> </div> ` }) export class AddUserComponent { newUser = { name: '', email: '' }; msg = ''; constructor(private dataService: DataService) { } addNewUser(): void { if (!this.newUser.name || !this.newUser.email) { this.msg = '请填写完整信息'; return; } this.dataService.addUser(this.newUser).subscribe({ next: () => { this.msg = '新增成功'; this.newUser = { name: '', email: '' }; }, error: (err) => { this.msg = err.message; } }); } }

五、进阶优化

5.1 请求防抖 / 节流

对于高频请求(如搜索框输入),可通过 RxJS 的debounceTime/throttleTime优化:

searchUsers(keyword: string): Observable<ApiResponse<any[]>> { return of(keyword).pipe( debounceTime(300), // 300ms内仅执行最后一次 switchMap((kw) => this.get<any[]>('users', { keyword: kw })) ); }

5.2 请求缓存

避免重复请求相同数据,可结合localStorageMap实现缓存:

private cache = new Map<string, any>(); getWithCache<T>(endpoint: string, params?: any): Observable<ApiResponse<T>> { const cacheKey = `${endpoint}_${JSON.stringify(params)}`; if (this.cache.has(cacheKey)) { return of(this.cache.get(cacheKey)); } return this.get<T>(endpoint, params).pipe( tap((data) => this.cache.set(cacheKey, data)) ); }

5.3 全局加载状态

通过BehaviorSubject维护加载状态,供组件渲染 loading 效果:

private loading$ = new BehaviorSubject<boolean>(false); public loadingObs$ = this.loading$.asObservable(); // 改造GET方法,添加加载状态 get<T>(endpoint: string, params?: any): Observable<ApiResponse<T>> { this.loading$.next(true); return this.http.get<ApiResponse<T>>(`${this.baseUrl}/${endpoint}`, { ...this.httpOptions, params }).pipe( catchError(this.handleError), finalize(() => this.loading$.next(false)) // 请求完成(成功/失败)后关闭加载 ); }

六、总结

通过封装通用数据服务,我们实现了:

  1. 代码复用:HTTP 请求逻辑集中管理,避免组件冗余代码;
  2. 数据统一:通过 RxJS Subject 实现组件间数据实时共享,无需通过@Input/@Output或路由传参;
  3. 易维护性:接口地址、请求头、错误处理等统一修改,降低维护成本;
  4. 可扩展性:可快速添加缓存、防抖、加载状态等通用能力。

在实际项目中,可根据业务复杂度进一步拆分服务(如按模块拆分UserServiceOrderService),但核心思路始终是 “封装通用逻辑,暴露业务接口,统一数据流转”。这种模式不仅适用于 Angular,也可迁移到 React/Vue 等框架的服务 / Store 设计中。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询