Angular面試題目和答案大全

基礎概念篇

1. 什么是Angular?它與AngularJS有什么區別?

答案: Angular是由Google開發的基于TypeScript的開源Web應用框架,用于構建單頁應用程序(SPA)。

Angular vs AngularJS對比:

特性AngularJSAngular
語言JavaScriptTypeScript
架構MVC組件化架構
移動支持原生支持
SEO支持服務端渲染
性能較低高性能
學習曲線中等較陡峭
發布時間20102016

2. Angular的核心概念有哪些?

答案:

核心構建塊:

  • 模塊(Modules):應用的組織單元,使用@NgModule裝飾器
  • 組件(Components):UI的基本構建塊,控制視圖
  • 服務(Services):可復用的業務邏輯,通過依賴注入使用
  • 指令(Directives):擴展HTML元素的行為
  • 管道(Pipes):數據轉換和格式化
  • 路由(Router):頁面導航管理

核心特性:

  • 依賴注入(DI)
  • 雙向數據綁定
  • 變更檢測
  • Zone.js

3. Angular的架構模式是什么?

答案: Angular采用組件化架構模式,主要特點:

分層架構:

┌─────────────────┐
│   Presentation  │ ← 組件層(Components)
├─────────────────┤
│    Business     │ ← 服務層(Services)
├─────────────────┤
│      Data       │ ← 數據層(HTTP Client, State Management)
└─────────────────┘

設計模式:

  • MVP(Model-View-Presenter)
  • 依賴注入模式
  • 觀察者模式
  • 單例模式

4. 什么是TypeScript?為什么Angular使用TypeScript?

答案: TypeScript是Microsoft開發的JavaScript超集,添加了靜態類型定義。

Angular使用TypeScript的原因:

  • 靜態類型檢查:編譯時發現錯誤
  • 更好的IDE支持:自動完成、重構等
  • 面向對象編程:類、接口、泛型等
  • ES6+特性支持:裝飾器、模塊等
  • 更好的代碼可維護性
// TypeScript示例
interface User {id: number;name: string;email: string;
}class UserService {getUser(id: number): User {// 實現邏輯}
}

組件篇

5. Angular組件的生命周期鉤子有哪些?

答案:

生命周期順序:

  1. ngOnChanges:輸入屬性變化時調用
  2. ngOnInit:初始化時調用(一次)
  3. ngDoCheck:每次變更檢測時調用
  4. ngAfterContentInit:內容投影初始化后調用(一次)
  5. ngAfterContentChecked:每次檢查投影內容后調用
  6. ngAfterViewInit:視圖初始化后調用(一次)
  7. ngAfterViewChecked:每次檢查視圖后調用
  8. ngOnDestroy:銷毀前調用
export class MyComponent implements OnInit, OnDestroy {ngOnInit(): void {console.log('Component initialized');}ngOnDestroy(): void {console.log('Component destroyed');}
}

6. 組件通信的方式有哪些?

答案:

1. 父子組件通信:

// 父傳子:@Input
@Component({template: `<child-component [data]="parentData"></child-component>`
})
export class ParentComponent {parentData = 'Hello Child';
}@Component({selector: 'child-component'
})
export class ChildComponent {@Input() data: string;
}// 子傳父:@Output
@Component({selector: 'child-component',template: `<button (click)="sendData()">Send</button>`
})
export class ChildComponent {@Output() dataEvent = new EventEmitter<string>();sendData() {this.dataEvent.emit('Hello Parent');}
}

2. 服務通信:

@Injectable()
export class DataService {private dataSubject = new BehaviorSubject<string>('');data$ = this.dataSubject.asObservable();updateData(data: string) {this.dataSubject.next(data);}
}

3. ViewChild/ViewChildren:

@Component({template: `<child-component #child></child-component>`
})
export class ParentComponent {@ViewChild('child') childComponent: ChildComponent;callChildMethod() {this.childComponent.someMethod();}
}

7. 什么是內容投影(Content Projection)?

答案: 內容投影是Angular中將外部內容插入到組件模板指定位置的機制,類似于Vue的插槽。

單插槽投影:

// 子組件
@Component({selector: 'card',template: `<div class="card"><ng-content></ng-content></div>`
})
export class CardComponent {}// 使用
<card><h1>卡片標題</h1><p>卡片內容</p>
</card>

多插槽投影:

// 子組件
@Component({selector: 'card',template: `<div class="card"><header><ng-content select="[slot=header]"></ng-content></header><main><ng-content select="[slot=content]"></ng-content></main></div>`
})
export class CardComponent {}// 使用
<card><h1 slot="header">標題</h1><p slot="content">內容</p>
</card>

8. Angular中的變更檢測機制是如何工作的?

答案: Angular使用Zone.js來監聽異步操作,并觸發變更檢測。

變更檢測過程:

  1. 觸發源:用戶事件、HTTP請求、定時器等
  2. Zone.js攔截:攔截異步操作
  3. 觸發檢測:從根組件開始檢測
  4. 單向檢測:從上到下檢測組件樹
  5. 更新DOM:更新變化的部分

優化策略:

// OnPush策略
@Component({changeDetection: ChangeDetectionStrategy.OnPush
})
export class OptimizedComponent {@Input() data: any;constructor(private cdr: ChangeDetectorRef) {}// 手動觸發檢測updateData() {this.cdr.markForCheck();}
}

脫離Zone:

export class MyComponent {constructor(private ngZone: NgZone) {}heavyTask() {this.ngZone.runOutsideAngular(() => {// 不觸發變更檢測的操作setInterval(() => {// 重任務}, 100);});}
}

指令篇

9. Angular中的指令類型有哪些?

答案:

指令分類:

1. 組件指令(Component Directives)

  • 最復雜的指令類型
  • 有自己的模板
  • 繼承自Directive

2. 屬性指令(Attribute Directives)

  • 改變元素外觀或行為
  • 不改變DOM結構
@Directive({selector: '[appHighlight]'
})
export class HighlightDirective {@Input() appHighlight: string;@HostListener('mouseenter') onMouseEnter() {this.highlight(this.appHighlight || 'yellow');}@HostListener('mouseleave') onMouseLeave() {this.highlight('');}private highlight(color: string) {this.el.nativeElement.style.backgroundColor = color;}constructor(private el: ElementRef) {}
}

3. 結構指令(Structural Directives)

  • 改變DOM結構
  • 使用*語法
@Directive({selector: '[appUnless]'
})
export class UnlessDirective {private hasView = false;@Input() set appUnless(condition: boolean) {if (!condition && !this.hasView) {this.viewContainer.createEmbeddedView(this.templateRef);this.hasView = true;} else if (condition && this.hasView) {this.viewContainer.clear();this.hasView = false;}}constructor(private templateRef: TemplateRef<any>,private viewContainer: ViewContainerRef) {}
}

10. 常用的內置指令有哪些?

答案:

結構指令:

<!-- *ngIf -->
<div *ngIf="isVisible; else elseTemplate">顯示內容</div>
<ng-template #elseTemplate>隱藏時顯示</ng-template><!-- *ngFor -->
<li *ngFor="let item of items; index as i; trackBy: trackByFn">{{i}}: {{item.name}}
</li><!-- *ngSwitch -->
<div [ngSwitch]="value"><p *ngSwitchCase="'A'">A</p><p *ngSwitchCase="'B'">B</p><p *ngSwitchDefault>Default</p>
</div>

屬性指令:

<!-- ngClass -->
<div [ngClass]="{'active': isActive, 'disabled': isDisabled}"><!-- ngStyle -->
<div [ngStyle]="{'color': textColor, 'font-size': fontSize + 'px'}"><!-- ngModel -->
<input [(ngModel)]="inputValue">

服務與依賴注入篇

11. 什么是依賴注入?Angular是如何實現的?

答案: 依賴注入(DI)是一種設計模式,將依賴對象的創建和管理交給外部容器,而不是在類內部創建。

Angular DI特點:

  • 分層注入器:模塊級、組件級注入器
  • 提供商(Providers):配置如何創建依賴
  • 注入令牌(Tokens):標識依賴項
// 服務定義
@Injectable({providedIn: 'root' // 單例,應用級注入
})
export class DataService {constructor(private http: HttpClient) {}
}// 組件中注入
@Component({})
export class MyComponent {constructor(private dataService: DataService) {}
}// 自定義提供商
@NgModule({providers: [{ provide: API_URL, useValue: 'https://api.example.com' },{ provide: DataService, useClass: MockDataService },{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }]
})
export class AppModule {}

12. Provider的不同類型有哪些?

答案:

Provider類型:

1. useClass:

{ provide: DataService, useClass: DataService }
// 簡寫
DataService

2. useValue:

{ provide: API_CONFIG, useValue: { url: 'https://api.com', timeout: 5000 } }

3. useFactory:

{provide: DataService,useFactory: (http: HttpClient) => new DataService(http),deps: [HttpClient]
}

4. useExisting:

{ provide: NewService, useExisting: OldService }

5. multi: true:

{provide: HTTP_INTERCEPTORS,useClass: LoggingInterceptor,multi: true
}

13. 什么是注入令牌(Injection Token)?

答案: 注入令牌是用于標識依賴項的唯一標識符,特別適用于注入原始值或配置對象。

// 創建令牌
export const API_CONFIG = new InjectionToken<ApiConfig>('api.config');interface ApiConfig {url: string;timeout: number;
}// 提供值
@NgModule({providers: [{provide: API_CONFIG,useValue: {url: 'https://api.example.com',timeout: 5000}}]
})
export class AppModule {}// 注入使用
@Injectable()
export class ApiService {constructor(@Inject(API_CONFIG) private config: ApiConfig) {}getData() {console.log(this.config.url);}
}

模塊篇

14. Angular模塊系統是如何工作的?

答案: Angular模塊(NgModule)是組織應用的方式,將相關的組件、指令、服務等打包在一起。

NgModule元數據:

@NgModule({declarations: [    // 聲明:組件、指令、管道AppComponent,HeaderComponent],imports: [        // 導入:其他模塊CommonModule,FormsModule,HttpClientModule],providers: [      // 提供商:服務DataService,{ provide: API_URL, useValue: 'https://api.com' }],exports: [        // 導出:可被其他模塊使用HeaderComponent],bootstrap: [      // 啟動組件(僅根模塊)AppComponent]
})
export class AppModule {}

模塊類型:

  • 根模塊(Root Module):AppModule,啟動應用
  • 特性模塊(Feature Module):業務功能模塊
  • 共享模塊(Shared Module):通用組件和服務
  • 核心模塊(Core Module):單例服務

15. 懶加載模塊是如何實現的?

答案: 懶加載允許按需加載模塊,減少初始包大小,提高應用啟動速度。

路由配置:

const routes: Routes = [{path: 'admin',loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)},{path: 'user',loadChildren: () => import('./user/user.module').then(m => m.UserModule)}
];@NgModule({imports: [RouterModule.forRoot(routes)],exports: [RouterModule]
})
export class AppRoutingModule {}

特性模塊:

@NgModule({declarations: [AdminComponent,UserListComponent],imports: [CommonModule,AdminRoutingModule]
})
export class AdminModule {}

預加載策略:

RouterModule.forRoot(routes, {preloadingStrategy: PreloadAllModules  // 預加載所有懶加載模塊
})

路由篇

16. Angular路由的核心概念有哪些?

答案:

核心概念:

  • Routes:路由配置數組
  • Router:導航服務
  • ActivatedRoute:當前激活路由信息
  • RouterOutlet:路由組件顯示位置
  • RouterLink:聲明式導航

基本配置:

const routes: Routes = [{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },{ path: 'dashboard', component: DashboardComponent },{ path: 'users/:id', component: UserDetailComponent },{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) },{ path: '**', component: NotFoundComponent }
];

路由參數:

// 路徑參數
export class UserDetailComponent {userId: string;constructor(private route: ActivatedRoute) {this.userId = this.route.snapshot.paramMap.get('id');// 響應式獲取this.route.paramMap.subscribe(params => {this.userId = params.get('id');});}
}// 查詢參數
this.router.navigate(['/users'], { queryParams: { page: 1, size: 10 } });

17. 路由守衛有哪些類型?

答案:

守衛類型:

1. CanActivate - 控制是否可以激活路由:

@Injectable()
export class AuthGuard implements CanActivate {constructor(private auth: AuthService, private router: Router) {}canActivate(): boolean {if (this.auth.isLoggedIn()) {return true;}this.router.navigate(['/login']);return false;}
}

2. CanActivateChild - 控制是否可以激活子路由:

@Injectable()
export class AdminGuard implements CanActivateChild {canActivateChild(): boolean {return this.checkAdminPermission();}
}

3. CanDeactivate - 控制是否可以離開路由:

@Injectable()
export class CanDeactivateGuard implements CanDeactivate<FormComponent> {canDeactivate(component: FormComponent): boolean {if (component.hasUnsavedChanges()) {return confirm('有未保存的更改,確定要離開嗎?');}return true;}
}

4. Resolve - 預加載數據:

@Injectable()
export class UserResolve implements Resolve<User> {constructor(private userService: UserService) {}resolve(route: ActivatedRouteSnapshot): Observable<User> {return this.userService.getUser(route.paramMap.get('id'));}
}

路由配置:

{path: 'user/:id',component: UserComponent,canActivate: [AuthGuard],canDeactivate: [CanDeactivateGuard],resolve: { user: UserResolve }
}

表單篇

18. Angular中的表單類型有哪些?

答案:

兩種表單類型:

1. 模板驅動表單(Template-driven Forms):

// 組件
export class TemplateFormComponent {user = { name: '', email: '' };onSubmit(form: NgForm) {if (form.valid) {console.log(this.user);}}
}// 模板
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)"><input name="name" [(ngModel)]="user.name" required #name="ngModel"><div *ngIf="name.invalid && name.touched">姓名必填</div><input name="email" [(ngModel)]="user.email" required email #email="ngModel"><div *ngIf="email.invalid && email.touched">郵箱格式錯誤</div><button [disabled]="userForm.invalid">提交</button>
</form>

2. 響應式表單(Reactive Forms):

export class ReactiveFormComponent {userForm: FormGroup;constructor(private fb: FormBuilder) {this.userForm = this.fb.group({name: ['', [Validators.required, Validators.minLength(2)]],email: ['', [Validators.required, Validators.email]],address: this.fb.group({street: [''],city: ['']})});}onSubmit() {if (this.userForm.valid) {console.log(this.userForm.value);}}get name() { return this.userForm.get('name'); }get email() { return this.userForm.get('email'); }
}// 模板
<form [formGroup]="userForm" (ngSubmit)="onSubmit()"><input formControlName="name"><div *ngIf="name?.invalid && name?.touched"><div *ngIf="name?.errors?.['required']">姓名必填</div><div *ngIf="name?.errors?.['minlength']">姓名至少2個字符</div></div><input formControlName="email"><div *ngIf="email?.invalid && email?.touched">郵箱格式錯誤</div><div formGroupName="address"><input formControlName="street" placeholder="街道"><input formControlName="city" placeholder="城市"></div><button [disabled]="userForm.invalid">提交</button>
</form>

19. 如何創建自定義驗證器?

答案:

同步驗證器:

// 自定義驗證器函數
export function forbiddenNameValidator(forbiddenName: string): ValidatorFn {return (control: AbstractControl): ValidationErrors | null => {const forbidden = forbiddenName && control.value?.toLowerCase().includes(forbiddenName.toLowerCase());return forbidden ? { forbiddenName: { value: control.value } } : null;};
}// 使用
this.userForm = this.fb.group({name: ['', [Validators.required, forbiddenNameValidator('admin')]]
});

異步驗證器:

export function uniqueEmailValidator(userService: UserService): AsyncValidatorFn {return (control: AbstractControl): Observable<ValidationErrors | null> => {if (!control.value) {return of(null);}return userService.checkEmailExists(control.value).pipe(map(exists => exists ? { emailExists: true } : null),catchError(() => of(null)));};
}// 使用
this.userForm = this.fb.group({email: ['', [Validators.required, Validators.email], [uniqueEmailValidator(this.userService)]]
});

跨字段驗證器:

export const passwordMatchValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {const password = control.get('password');const confirmPassword = control.get('confirmPassword');return password && confirmPassword && password.value !== confirmPassword.value ? { passwordMismatch: true } : null;
};// 使用
this.userForm = this.fb.group({password: ['', Validators.required],confirmPassword: ['', Validators.required]
}, { validators: passwordMatchValidator });

HTTP篇

20. Angular中如何進行HTTP通信?

答案:

HttpClient基本使用:

@Injectable({providedIn: 'root'
})
export class ApiService {private baseUrl = 'https://api.example.com';constructor(private http: HttpClient) {}// GET請求getUsers(): Observable<User[]> {return this.http.get<User[]>(`${this.baseUrl}/users`);}// POST請求createUser(user: User): Observable<User> {return this.http.post<User>(`${this.baseUrl}/users`, user, {headers: new HttpHeaders({'Content-Type': 'application/json'})});}// PUT請求updateUser(id: number, user: User): Observable<User> {return this.http.put<User>(`${this.baseUrl}/users/${id}`, user);}// DELETE請求deleteUser(id: number): Observable<void> {return this.http.delete<void>(`${this.baseUrl}/users/${id}`);}// 帶參數的GET請求getUsersWithParams(page: number, size: number): Observable<User[]> {const params = new HttpParams().set('page', page.toString()).set('size', size.toString());return this.http.get<User[]>(`${this.baseUrl}/users`, { params });}
}

錯誤處理:

export class ApiService {getUsers(): Observable<User[]> {return this.http.get<User[]>(`${this.baseUrl}/users`).pipe(retry(3), // 重試3次catchError(this.handleError));}private handleError(error: HttpErrorResponse): Observable<never> {let errorMessage = '';if (error.error instanceof ErrorEvent) {// 客戶端錯誤errorMessage = `Error: ${error.error.message}`;} else {// 服務端錯誤errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;}console.error(errorMessage);return throwError(() => new Error(errorMessage));}
}

21. 什么是HTTP攔截器?如何使用?

答案: HTTP攔截器可以攔截HTTP請求和響應,用于添加通用邏輯如身份驗證、日志記錄、錯誤處理等。

身份驗證攔截器:

@Injectable()
export class AuthInterceptor implements HttpInterceptor {constructor(private auth: AuthService) {}intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {// 添加授權頭const authToken = this.auth.getToken();if (authToken) {const authReq = req.clone({headers: req.headers.set('Authorization', `Bearer ${authToken}`)});return next.handle(authReq);}return next.handle(req);}
}

日志攔截器:

@Injectable()
export class LoggingInterceptor implements HttpInterceptor {intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {console.log('Request:', req.url);return next.handle(req).pipe(tap(event => {if (event instanceof HttpResponse) {console.log('Response:', event.status, event.body);}}));}
}

注冊攔截器:

@NgModule({providers: [{provide: HTTP_INTERCEPTORS,useClass: AuthInterceptor,multi: true},{provide: HTTP_INTERCEPTORS,useClass: LoggingInterceptor,multi: true}]
})
export class AppModule {}

RxJS篇

22. 什么是Observable?與Promise的區別是什么?

答案: Observable是RxJS中表示異步數據流的核心概念。

Observable vs Promise:

特性ObservablePromise
執行時機懶執行(訂閱時執行)立即執行
值的數量多個值單個值
取消支持支持取消不支持取消
操作符豐富的操作符基本的then/catch
錯誤處理多種錯誤處理方式catch

Observable示例:

// 創建Observable
const numbers$ = new Observable<number>(subscriber => {subscriber.next(1);subscriber.next(2);subscriber.next(3);subscriber.complete();
});// 訂閱
const subscription = numbers$.subscribe({next: value => console.log(value),error: err => console.error(err),complete: () => console.log('Complete')
});// 取消訂閱
subscription.unsubscribe();

23. 常用的RxJS操作符有哪些?

答案:

創建操作符:

// of - 創建發出指定值的Observable
const numbers$ = of(1, 2, 3, 4, 5);// from - 從數組、Promise等創建Observable
const fromArray$ = from([1, 2, 3]);
const fromPromise$ = from(fetch('/api/data'));// interval - 定時發出數值
const timer$ = interval(1000); // 每秒發出遞增數字// fromEvent - 從DOM事件創建Observable
const clicks$ = fromEvent(document, 'click');

轉換操作符:

// map - 轉換值
const doubled$ = numbers$.pipe(map(x => x * 2)
);// mergeMap - 扁平化內部Observable
const searchResults$ = searchTerms$.pipe(mergeMap(term => this.searchService.search(term))
);// switchMap - 切換到新的Observable,取消之前的
const latestSearch$ = searchTerms$.pipe(switchMap(term => this.searchService.search(term))
);// concatMap - 按順序連接Observable
const orderedResults$ = requests$.pipe(concatMap(request => this.processRequest(request))
);

過濾操作符:

// filter - 過濾值
const evenNumbers$ = numbers$.pipe(filter(x => x % 2 === 0)
);// distinctUntilChanged - 去重相鄰重復值
const uniqueValues$ = values$.pipe(distinctUntilChanged()
);// debounceTime - 防抖
const searchInput$ = fromEvent(input, 'input').pipe(debounceTime(300),map(event => event.target.value)
);// throttleTime - 節流
const throttledClicks$ = clicks$.pipe(throttleTime(1000)
);

組合操作符:

// combineLatest - 組合最新值
const combined$ = combineLatest([firstName$,lastName$
]).pipe(map(([first, last]) => `${first} ${last}`)
);// merge - 合并多個Observable
const allEvents$ = merge(clicks$, keyPresses$, scrolls$);// zip - 配對值
const paired$ = zip(numbers$, letters$).pipe(map(([num, letter]) => `${num}${letter}`)
);

24. 如何處理內存泄漏和取消訂閱?

答案:

手動取消訂閱:

export class MyComponent implements OnInit, OnDestroy {private destroy$ = new Subject<void>();ngOnInit() {// 方法1:使用takeUntilthis.dataService.getData().pipe(takeUntil(this.destroy$)).subscribe(data => {console.log(data);});// 方法2:保存subscriptionthis.subscription = this.dataService.getData().subscribe(data => {console.log(data);});}ngOnDestroy() {// 方法1:發出銷毀信號this.destroy$.next();this.destroy$.complete();// 方法2:手動取消訂閱if (this.subscription) {this.subscription.unsubscribe();}}
}

使用async管道(推薦):

export class MyComponent {data$ = this.dataService.getData(); // 不需要手動取消訂閱constructor(private dataService: DataService) {}
}// 模板中使用async管道
<div *ngIf="data$ | async as data">{{data.name}}
</div>

自定義操作符:

// 自動取消訂閱的操作符
export function untilDestroyed(component: any) {return <T>(source: Observable<T>) => {const destroy$ = new Subject();const originalDestroy = component.ngOnDestroy;component.ngOnDestroy = function() {destroy$.next();destroy$.complete();if (originalDestroy) {originalDestroy.apply(this, arguments);}};return source.pipe(takeUntil(destroy$));};
}// 使用
this.dataService.getData().pipe(untilDestroyed(this)
).subscribe(data => console.log(data));

測試篇

25. Angular中的測試類型有哪些?

答案:

測試類型:

1. 單元測試(Unit Tests):

describe('UserService', () => {let service: UserService;let httpMock: HttpTestingController;beforeEach(() => {TestBed.configureTestingModule({imports: [HttpClientTestingModule],providers: [UserService]});service = TestBed.inject(UserService);httpMock = TestBed.inject(HttpTestingController);});it('should fetch users', () => {const mockUsers = [{ id: 1, name: 'John' }];service.getUsers().subscribe(users => {expect(users).toEqual(mockUsers);});const req = httpMock.expectOne('/api/users');expect(req.request.method).toBe('GET');req.flush(mockUsers);});afterEach(() => {httpMock.verify();});
});

2. 組件測試:

describe('UserComponent', () => {let component: UserComponent;let fixture: ComponentFixture<UserComponent>;let userService: jasmine.SpyObj<UserService>;beforeEach(() => {const spy = jasmine.createSpyObj('UserService', ['getUsers']);TestBed.configureTestingModule({declarations: [UserComponent],providers: [{ provide: UserService, useValue: spy }]});fixture = TestBed.createComponent(UserComponent);component = fixture.componentInstance;userService = TestBed.inject(UserService) as jasmine.SpyObj<UserService>;});it('should display users', () => {const mockUsers = [{ id: 1, name: 'John' }];userService.getUsers.and.returnValue(of(mockUsers));fixture.detectChanges();const compiled = fixture.nativeElement;expect(compiled.querySelector('.user-name').textContent).toContain('John');});
});

3. 集成測試:

describe('App Integration', () => {it('should navigate to user detail', fakeAsync(() => {const fixture = TestBed.createComponent(AppComponent);const router = TestBed.inject(Router);const location = TestBed.inject(Location);fixture.detectChanges();router.navigate(['/users/1']);tick();expect(location.path()).toBe('/users/1');}));
});

26. 如何測試異步操作?

答案:

使用fakeAsync和tick:

it('should handle async operation', fakeAsync(() => {let result: string;setTimeout(() => {result = 'async result';}, 1000);tick(1000); // 模擬時間流逝expect(result).toBe('async result');
}));

測試Observable:

it('should handle observable', () => {const testData = { id: 1, name: 'Test' };service.getData().subscribe(data => {expect(data).toEqual(testData);});const req = httpMock.expectOne('/api/data');req.flush(testData);
});

使用done回調:

it('should handle promise', (done) => {service.getDataPromise().then(data => {expect(data).toBeTruthy();done();});
});

性能優化篇

27. Angular性能優化的策略有哪些?

答案:

變更檢測優化:

// 1. OnPush變更檢測策略
@Component({selector: 'app-user',changeDetection: ChangeDetectionStrategy.OnPush,template: `<div>{{user.name}}</div>`
})
export class UserComponent {@Input() user: User;
}// 2. 使用Immutable數據
updateUser(newName: string) {this.user = { ...this.user, name: newName }; // 創建新對象
}// 3. TrackBy函數優化*ngFor
trackByUserId(index: number, user: User): number {return user.id;
}// 模板中使用
<div *ngFor="let user of users; trackBy: trackByUserId">{{user.name}}
</div>

懶加載和預加載:

// 路由懶加載
const routes: Routes = [{path: 'feature',loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule)}
];// 預加載策略
RouterModule.forRoot(routes, {preloadingStrategy: PreloadAllModules
})

代碼分割和Tree Shaking:

// 動態導入
async loadUtility() {const { heavyUtility } = await import('./heavy-utility');return heavyUtility();
}// 使用具體導入
import { map, filter } from 'rxjs/operators'; // ? 好
import * as rxjs from 'rxjs'; // ? 不好

服務端渲染(SSR):

ng add @nguniversal/express-engine
npm run build:ssr
npm run serve:ssr

28. 如何優化大型列表的渲染性能?

答案:

虛擬滾動:

// 安裝CDK
npm install @angular/cdk// 使用虛擬滾動
@Component({template: `<cdk-virtual-scroll-viewport itemSize="50" class="viewport"><div *cdkVirtualFor="let item of items; trackBy: trackByFn">{{item.name}}</div></cdk-virtual-scroll-viewport>`,styles: [`.viewport {height: 400px;width: 100%;}`]
})
export class VirtualScrollComponent {items = Array.from({length: 10000}, (_, i) => ({id: i, name: `Item ${i}`}));trackByFn(index: number, item: any) {return item.id;}
}

分頁和無限滾動:

@Component({template: `<div *ngFor="let item of visibleItems">{{item.name}}</div><div #sentinel></div>`
})
export class InfiniteScrollComponent implements OnInit, AfterViewInit {@ViewChild('sentinel') sentinel: ElementRef;allItems: Item[] = [];visibleItems: Item[] = [];private page = 0;private pageSize = 20;ngAfterViewInit() {const observer = new IntersectionObserver(entries => {if (entries[0].isIntersecting) {this.loadMore();}});observer.observe(this.sentinel.nativeElement);}loadMore() {const start = this.page * this.pageSize;const end = start + this.pageSize;this.visibleItems = [...this.visibleItems, ...this.allItems.slice(start, end)];this.page++;}
}

狀態管理篇

29. Angular中有哪些狀態管理方案?

答案:

1. 服務 + BehaviorSubject:

@Injectable({providedIn: 'root'
})
export class StateService {private userSubject = new BehaviorSubject<User | null>(null);user$ = this.userSubject.asObservable();updateUser(user: User) {this.userSubject.next(user);}getUser(): User | null {return this.userSubject.value;}
}

2. NgRx:

// Actions
export const loadUsers = createAction('[User] Load Users');
export const loadUsersSuccess = createAction('[User] Load Users Success',props<{ users: User[] }>()
);// Reducer
const userReducer = createReducer(initialState,on(loadUsers, state => ({ ...state, loading: true })),on(loadUsersSuccess, (state, { users }) => ({...state,users,loading: false}))
);// Effects
@Injectable()
export class UserEffects {loadUsers$ = createEffect(() =>this.actions$.pipe(ofType(loadUsers),switchMap(() =>this.userService.getUsers().pipe(map(users => loadUsersSuccess({ users }))))));constructor(private actions$: Actions,private userService: UserService) {}
}// Selectors
export const selectUsers = createSelector(selectUserState,state => state.users
);// 組件中使用
@Component({})
export class UserListComponent {users$ = this.store.select(selectUsers);constructor(private store: Store) {}loadUsers() {this.store.dispatch(loadUsers());}
}

3. Akita:

// Store
export interface UserState extends EntityState<User> {loading: boolean;
}@Injectable({ providedIn: 'root' })
@StoreConfig({ name: 'user' })
export class UserStore extends EntityStore<UserState> {constructor() {super({ loading: false });}
}// Query
@Injectable({ providedIn: 'root' })
export class UserQuery extends QueryEntity<UserState> {loading$ = this.select('loading');constructor(protected store: UserStore) {super(store);}
}// Service
@Injectable({ providedIn: 'root' })
export class UserService {constructor(private userStore: UserStore,private http: HttpClient) {}getUsers() {this.userStore.setLoading(true);return this.http.get<User[]>('/api/users').pipe(tap(users => {this.userStore.set(users);this.userStore.setLoading(false);}));}
}

30. 如何在Angular中實現緩存?

答案:

HTTP緩存:

@Injectable()
export class CacheInterceptor implements HttpInterceptor {private cache = new Map<string, HttpResponse<any>>();intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {if (req.method === 'GET') {const cachedResponse = this.cache.get(req.url);if (cachedResponse) {return of(cachedResponse);}}return next.handle(req).pipe(tap(event => {if (event instanceof HttpResponse && req.method === 'GET') {this.cache.set(req.url, event);}}));}
}

內存緩存服務:

@Injectable({providedIn: 'root'
})
export class CacheService {private cache = new Map<string, { data: any; timestamp: number; ttl: number }>();set(key: string, data: any, ttl: number = 300000): void { // 5分鐘TTLthis.cache.set(key, {data,timestamp: Date.now(),ttl});}get(key: string): any | null {const cached = this.cache.get(key);if (!cached) {return null;}if (Date.now() - cached.timestamp > cached.ttl) {this.cache.delete(key);return null;}return cached.data;}clear(): void {this.cache.clear();}
}// 在服務中使用緩存
@Injectable()
export class DataService {constructor(private http: HttpClient,private cache: CacheService) {}getData(id: string): Observable<any> {const cacheKey = `data_${id}`;const cached = this.cache.get(cacheKey);if (cached) {return of(cached);}return this.http.get(`/api/data/${id}`).pipe(tap(data => this.cache.set(cacheKey, data)));}
}

安全篇

31. Angular中的安全防護措施有哪些?

答案:

XSS防護:

// Angular自動轉義HTML
@Component({template: `<div>{{userInput}}</div> <!-- 自動轉義 --><div [innerHTML]="trustedHtml"></div> <!-- 需要信任的HTML -->`
})
export class SafeComponent {userInput = '<script>alert("xss")</script>'; // 會被轉義constructor(private sanitizer: DomSanitizer) {}get trustedHtml() {return this.sanitizer.bypassSecurityTrustHtml(this.someHtml);}
}

CSRF防護:

// HttpClientXsrfModule配置
@NgModule({imports: [HttpClientModule,HttpClientXsrfModule.withOptions({cookieName: 'XSRF-TOKEN',headerName: 'X-XSRF-TOKEN'})]
})
export class AppModule {}

內容安全策略(CSP):

<!-- index.html -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-eval';">

JWT Token管理:

@Injectable()
export class AuthService {private tokenKey = 'auth_token';setToken(token: string): void {localStorage.setItem(this.tokenKey, token);}getToken(): string | null {return localStorage.getItem(this.tokenKey);}removeToken(): void {localStorage.removeItem(this.tokenKey);}isTokenExpired(): boolean {const token = this.getToken();if (!token) return true;const payload = JSON.parse(atob(token.split('.')[1]));return Date.now() >= payload.exp * 1000;}
}

32. 如何實現角色基礎的訪問控制?

答案:

權限守衛:

export enum Role {Admin = 'admin',User = 'user',Guest = 'guest'
}@Injectable()
export class RoleGuard implements CanActivate {constructor(private auth: AuthService,private router: Router) {}canActivate(route: ActivatedRouteSnapshot): boolean {const requiredRoles = route.data['roles'] as Role[];const userRole = this.auth.getUserRole();if (requiredRoles && !requiredRoles.includes(userRole)) {this.router.navigate(['/unauthorized']);return false;}return true;}
}// 路由配置
{path: 'admin',component: AdminComponent,canActivate: [RoleGuard],data: { roles: [Role.Admin] }
}

權限指令:

@Directive({selector: '[hasRole]'
})
export class HasRoleDirective implements OnInit {@Input() hasRole: Role[];constructor(private templateRef: TemplateRef<any>,private viewContainer: ViewContainerRef,private auth: AuthService) {}ngOnInit() {const userRole = this.auth.getUserRole();if (this.hasRole.includes(userRole)) {this.viewContainer.createEmbeddedView(this.templateRef);} else {this.viewContainer.clear();}}
}// 使用
<button *hasRole="[Role.Admin]">刪除用戶</button>

高級特性篇

33. Angular中的動態組件如何實現?

答案:

使用ComponentFactoryResolver(Angular 13之前):

@Component({template: `<div #container></div>`
})
export class DynamicHostComponent {@ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;constructor(private componentFactoryResolver: ComponentFactoryResolver) {}loadComponent(componentType: any) {const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentType);this.container.clear();const componentRef = this.container.createComponent(componentFactory);// 設置輸入屬性componentRef.instance.data = 'some data';return componentRef;}
}

使用ViewContainerRef.createComponent(Angular 13+):

@Component({template: `<div #container></div>`
})
export class DynamicHostComponent {@ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;loadComponent(componentType: any) {this.container.clear();const componentRef = this.container.createComponent(componentType);componentRef.setInput('data', 'some data');return componentRef;}
}

動態表單示例:

interface FieldConfig {type: 'input' | 'select' | 'checkbox';name: string;label: string;options?: string[];
}@Component({template: `<form [formGroup]="form"><div #fieldContainer></div></form>`
})
export class DynamicFormComponent implements OnInit {@ViewChild('fieldContainer', { read: ViewContainerRef }) container: ViewContainerRef;@Input() fields: FieldConfig[] = [];form: FormGroup;constructor(private fb: FormBuilder) {}ngOnInit() {this.buildForm();this.loadFields();}private buildForm() {const group: any = {};this.fields.forEach(field => {group[field.name] = [''];});this.form = this.fb.group(group);}private loadFields() {this.fields.forEach(field => {const component = this.getComponentType(field.type);const componentRef = this.container.createComponent(component);componentRef.setInput('config', field);componentRef.setInput('formControl', this.form.get(field.name));});}private getComponentType(type: string) {switch (type) {case 'input': return InputFieldComponent;case 'select': return SelectFieldComponent;case 'checkbox': return CheckboxFieldComponent;default: return InputFieldComponent;}}
}

34. 什么是Angular Elements?

答案: Angular Elements允許將Angular組件轉換為自定義元素(Custom Elements),可以在任何HTML頁面中使用。

創建Angular Element:

// 安裝
npm install @angular/elements// 創建自定義元素
import { createCustomElement } from '@angular/elements';@Component({selector: 'app-hello',template: `<h1>Hello {{name}}!</h1>`,encapsulation: ViewEncapsulation.ShadowDom
})
export class HelloComponent {@Input() name = 'World';
}@NgModule({declarations: [HelloComponent],imports: [BrowserModule],entryComponents: [HelloComponent] // Angular 9之前需要
})
export class AppModule {constructor(private injector: Injector) {}ngDoBootstrap() {const helloElement = createCustomElement(HelloComponent, { injector: this.injector });customElements.define('hello-element', helloElement);}
}

使用自定義元素:

<!-- 在任何HTML頁面中使用 -->
<hello-element name="Angular"></hello-element><script>// 動態設置屬性const element = document.querySelector('hello-element');element.name = 'Dynamic Name';
</script>

35. Angular中的微前端架構如何實現?

答案:

使用Module Federation:

// webpack.config.js (子應用)
const ModuleFederationPlugin = require('@module-federation/webpack');module.exports = {plugins: [new ModuleFederationPlugin({name: 'mfe1',filename: 'remoteEntry.js',exposes: {'./Module': './src/app/feature/feature.module.ts'},shared: {'@angular/core': { singleton: true },'@angular/common': { singleton: true },'@angular/router': { singleton: true }}})]
};// webpack.config.js (主應用)
new ModuleFederationPlugin({name: 'shell',remotes: {mfe1: 'mfe1@http://localhost:4201/remoteEntry.js'},shared: {'@angular/core': { singleton: true },'@angular/common': { singleton: true },'@angular/router': { singleton: true }}
})

路由配置:

// 主應用路由
const routes: Routes = [{path: 'mfe1',loadChildren: () => import('mfe1/Module').then(m => m.FeatureModule)}
];

Single-SPA方案:

// 子應用入口
import { NgZone } from '@angular/core';
import { Router } from '@angular/router';
import { singleSpaAngular, getSingleSpaExtraProviders } from 'single-spa-angular';const lifecycles = singleSpaAngular({bootstrapFunction: singleSpaProps => {return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(AppModule);},template: '<app-root />',Router,NgZone
});export const bootstrap = lifecycles.bootstrap;
export const mount = lifecycles.mount;
export const unmount = lifecycles.unmount;

最佳實踐篇

36. Angular項目的文件結構最佳實踐

答案:

推薦的項目結構:

src/
├── app/
│   ├── core/                    # 核心模塊(單例服務)
│   │   ├── guards/
│   │   ├── interceptors/
│   │   ├── services/
│   │   └── core.module.ts
│   ├── shared/                  # 共享模塊
│   │   ├── components/
│   │   ├── directives/
│   │   ├── pipes/
│   │   └── shared.module.ts
│   ├── features/                # 特性模塊
│   │   ├── user/
│   │   │   ├── components/
│   │   │   ├── services/
│   │   │   ├── user-routing.module.ts
│   │   │   └── user.module.ts
│   ├── layouts/                 # 布局組件
│   ├── app-routing.module.ts
│   ├── app.component.ts
│   └── app.module.ts
├── assets/                      # 靜態資源
│   ├── images/
│   ├── icons/
│   └── i18n/
├── environments/                # 環境配置
└── styles/                      # 全局樣式├── _variables.scss├── _mixins.scss└── styles.scss

模塊組織原則:

  • Core Module:放置單例服務,只被AppModule導入
  • Shared Module:放置通用組件、指令、管道
  • Feature Module:按業務功能組織,可懶加載
  • Layout Module:布局相關組件

37. Angular編碼規范和最佳實踐

答案:

命名規范:

// 1. 使用kebab-case命名文件
user-detail.component.ts
user.service.ts
auth.guard.ts// 2. 類名使用PascalCase
export class UserDetailComponent {}
export class UserService {}
export class AuthGuard {}// 3. 變量和方法使用camelCase
userName: string;
getUserData(): void {}// 4. 常量使用SCREAMING_SNAKE_CASE
export const API_BASE_URL = 'https://api.example.com';

組件設計原則:

// 1. 單一職責原則
@Component({selector: 'app-user-list',  // 只負責用戶列表顯示template: `<div *ngFor="let user of users"><app-user-item [user]="user" (delete)="onDelete($event)"></app-user-item></div>`
})
export class UserListComponent {@Input() users: User[];@Output() delete = new EventEmitter<number>();onDelete(userId: number) {this.delete.emit(userId);}
}// 2. 使用OnPush策略提高性能
@Component({changeDetection: ChangeDetectionStrategy.OnPush
})
export class OptimizedComponent {}// 3. 實現生命周期接口
export class MyComponent implements OnInit, OnDestroy {ngOnInit(): void {}ngOnDestroy(): void {}
}// 4. 使用trackBy優化*ngFor
trackByUserId = (index: number, user: User) => user.id;

服務設計原則:

// 1. 使用依賴注入
@Injectable({providedIn: 'root'  // 單例服務
})
export class UserService {constructor(private http: HttpClient) {}
}// 2. 返回Observable而不是Promise
getUserById(id: number): Observable<User> {return this.http.get<User>(`/api/users/${id}`);
}// 3. 錯誤處理
getUserById(id: number): Observable<User> {return this.http.get<User>(`/api/users/${id}`).pipe(retry(3),catchError(this.handleError));
}private handleError(error: HttpErrorResponse): Observable<never> {console.error('An error occurred:', error.error);return throwError(() => new Error('Something went wrong'));
}

模板最佳實踐:

<!-- 1. 使用async管道 -->
<div *ngIf="user$ | async as user">{{user.name}}
</div><!-- 2. 避免在模板中調用方法 -->
<!-- 不好 -->
<div>{{getFullName()}}</div><!-- 好 -->
<div>{{fullName}}</div><!-- 3. 使用trackBy提高性能 -->
<div *ngFor="let item of items; trackBy: trackByFn">{{item.name}}
</div><!-- 4. 合理使用*ngIf和hidden -->
<!-- 頻繁切換使用hidden -->
<div [hidden]="!isVisible">Content</div><!-- 不頻繁切換使用*ngIf -->
<div *ngIf="isVisible">Content</div>

38. 如何進行Angular應用的SEO優化?

答案:

服務端渲染(SSR):

# 添加Angular Universal
ng add @nguniversal/express-engine# 構建和運行SSR
npm run build:ssr
npm run serve:ssr

預渲染(Prerendering):

# 添加預渲染功能
ng add @nguniversal/express-engine# 構建預渲染版本
npm run prerender

Meta標簽管理:

import { Meta, Title } from '@angular/platform-browser';@Component({})
export class ProductDetailComponent implements OnInit {constructor(private meta: Meta,private title: Title,private route: ActivatedRoute) {}ngOnInit() {this.route.data.subscribe(data => {const product = data.product;// 設置頁面標題this.title.setTitle(`${product.name} - 商品詳情`);// 設置Meta標簽this.meta.updateTag({ name: 'description', content: product.description });this.meta.updateTag({ name: 'keywords', content: product.keywords });// Open Graph標簽this.meta.updateTag({ property: 'og:title', content: product.name });this.meta.updateTag({ property: 'og:description', content: product.description });this.meta.updateTag({ property: 'og:image', content: product.image });// Twitter Card標簽this.meta.updateTag({ name: 'twitter:card', content: 'summary_large_image' });this.meta.updateTag({ name: 'twitter:title', content: product.name });});}
}

結構化數據:

@Injectable()
export class SeoService {constructor(@Inject(DOCUMENT) private document: Document) {}addStructuredData(data: any) {const script = this.document.createElement('script');script.type = 'application/ld+json';script.text = JSON.stringify(data);this.document.head.appendChild(script);}addProductStructuredData(product: Product) {const structuredData = {'@context': 'https://schema.org/','@type': 'Product','name': product.name,'description': product.description,'image': product.image,'offers': {'@type': 'Offer','price': product.price,'priceCurrency': 'CNY'}};this.addStructuredData(structuredData);}
}

39. Angular國際化(i18n)如何實現?

答案:

配置國際化:

# 添加國際化支持
ng add @angular/localize# 提取文本
ng extract-i18n# 構建不同語言版本
ng build --localize

標記需要翻譯的文本:

<!-- 使用i18n屬性 -->
<h1 i18n="@@welcome">歡迎</h1><!-- 帶描述和含義 -->
<p i18n="user.greeting|問候用戶">你好,{{userName}}</p><!-- 復數形式 -->
<span i18n>{count, plural, =0 {沒有消息} =1 {1條消息} other {{{count}}條消息}}</span><!-- ICU表達式 -->
<span i18n>{gender, select, male {他} female {她} other {它}}</span>

在組件中使用:

import { LOCALE_ID, Inject } from '@angular/core';@Component({})
export class MyComponent {constructor(@Inject(LOCALE_ID) public locale: string) {}// 使用$localize標記字符串message = $localize`歡迎使用我們的應用`;// 帶插值的本地化greetUser(name: string) {return $localize`你好,${name}`;}
}

日期和數字管道:

<!-- 日期本地化 -->
<p>{{ today | date:'medium':locale }}</p><!-- 數字本地化 -->
<p>{{ price | currency:'CNY':'symbol':'1.2-2':locale }}</p><!-- 百分比 -->
<p>{{ ratio | percent:'1.1-1':locale }}</p>

動態切換語言:

@Injectable()
export class LocaleService {private currentLocale = 'zh-CN';setLocale(locale: string) {this.currentLocale = locale;// 重新加載應用或動態加載翻譯文件window.location.reload();}getCurrentLocale(): string {return this.currentLocale;}
}

40. Angular應用的部署策略有哪些?

答案:

構建優化:

# 生產環境構建
ng build --prod# 啟用AOT編譯
ng build --aot# 分析包大小
ng build --prod --source-map
npm install -g webpack-bundle-analyzer
webpack-bundle-analyzer dist/*/main.js

部署配置文件:

// angular.json
{"projects": {"my-app": {"architect": {"build": {"configurations": {"production": {"fileReplacements": [{"replace": "src/environments/environment.ts","with": "src/environments/environment.prod.ts"}],"optimization": true,"outputHashing": "all","sourceMap": false,"extractCss": true,"namedChunks": false,"extractLicenses": true,"vendorChunk": false,"buildOptimizer": true,"budgets": [{"type": "initial","maximumWarning": "2mb","maximumError": "5mb"}]}}}}}}
}

Nginx配置示例:

server {listen 80;server_name your-domain.com;root /var/www/your-app/dist;index index.html;# 處理Angular路由location / {try_files $uri $uri/ /index.html;}# 靜態資源緩存location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {expires 1y;add_header Cache-Control "public, immutable";}# 壓縮gzip on;gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}

Docker部署:

# 多階段構建
FROM node:16-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build --prodFROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

CI/CD流水線(GitHub Actions示例):

name: Deploy Angular Appon:push:branches: [main]jobs:build-and-deploy:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2- name: Setup Node.jsuses: actions/setup-node@v2with:node-version: '16'cache: 'npm'- name: Install dependenciesrun: npm ci- name: Run testsrun: npm run test -- --watch=false --browsers=ChromeHeadless- name: Buildrun: npm run build --prod- name: Deploy to S3run: aws s3 sync dist/ s3://your-bucket-name --deleteenv:AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

面試技巧和項目經驗篇

41. 常見的Angular面試陷阱題

問題1:為什么需要使用OnPush變更檢測策略? 答案: 默認的變更檢測策略會檢查所有組件,OnPush策略只在以下情況觸發檢測:

  • @Input屬性引用發生變化
  • 事件觸發(DOM事件、@Output事件)
  • 手動調用ChangeDetectorRef.markForCheck()
  • Observable發出新值(使用async管道)

問題2:Angular中的單例服務是如何保證的? 答案: 通過providedIn: 'root'或在根模塊的providers中注冊,Angular的依賴注入系統會為整個應用創建唯一實例。子模塊中重復提供會創建新實例。

問題3:ngFor和ngIf能同時使用嗎? 答案: 不能在同一個元素上同時使用。解決方案:

<!-- 使用ng-container -->
<ng-container *ngFor="let item of items"><div *ngIf="item.visible">{{item.name}}</div>
</ng-container><!-- 或者使用管道過濾 -->
<div *ngFor="let item of items | filter:isVisible">{{item.name}}</div>

42. 項目經驗相關問題的回答框架

問題:描述一個你在Angular項目中遇到的性能問題及解決方案

回答框架:

  1. 背景描述:項目規模、遇到的具體性能問題
  2. 問題分析:使用的分析工具(Chrome DevTools、Angular DevTools)
  3. 解決方案:具體采用的優化策略
  4. 效果評估:優化前后的性能對比數據
  5. 經驗總結:從中學到的經驗和最佳實踐

示例回答: "在一個電商項目中,商品列表頁面在顯示1000+商品時出現卡頓。通過Chrome DevTools分析發現變更檢測耗時過長。我采用了以下優化方案:

  1. 使用OnPush變更檢測策略
  2. 實現虛擬滾動(CDK Virtual Scrolling)
  3. 添加trackBy函數優化*ngFor
  4. 使用異步管道避免手動訂閱 最終頁面渲染時間從3秒降至500ms,用戶體驗顯著提升。"

問題:如何設計一個可復用的Angular組件庫?

回答要點:

  • API設計:簡潔明了的輸入輸出接口
  • 主題定制:支持CSS變量和主題切換
  • 無障礙訪問:遵循ARIA標準
  • 文檔和示例:詳細的使用文檔和在線示例
  • 測試覆蓋:單元測試和端到端測試
  • 版本管理:語義化版本控制
  • TypeScript支持:完整的類型定義

43. Angular新特性和發展趨勢

Angular 15+ 新特性:

  • Standalone Components:獨立組件,減少模塊樣板代碼
  • Optional Injectors:可選的依賴注入
  • Directive Composition API:指令組合
  • Image Optimization:圖片優化指令
  • Angular CLI Auto-completion:CLI自動補全

Standalone Components示例:

@Component({selector: 'app-standalone',standalone: true,imports: [CommonModule, FormsModule],template: `<div>Standalone Component</div>`
})
export class StandaloneComponent {}// 引導應用
bootstrapApplication(StandaloneComponent, {providers: [importProvidersFrom(BrowserModule),provideRouter(routes)]
});

未來發展趨勢:

  • 更好的性能:Ivy渲染引擎持續優化
  • 更小的包體積:Tree-shaking和代碼分割
  • 更好的開發體驗:更強的TypeScript集成
  • Web Components:更好的自定義元素支持
  • 微前端:Module Federation集成

總結

以上43個問題涵蓋了Angular的核心概念、高級特性、最佳實踐等各個方面。在準備面試時,建議:

  1. 深入理解原理:不僅要知道怎么用,更要知道為什么這樣設計
  2. 結合實際項目:用具體的項目經驗來回答問題
  3. 關注新特性:了解Angular的最新發展和趨勢
  4. 練習代碼實現:能夠手寫核心功能的簡單實現
  5. 準備問題反問:準備一些有深度的問題向面試官提問

記住,面試不僅是展示技術能力,也是展示解決問題的思路和學習能力的機會。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/91063.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/91063.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/91063.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CSS 語音參考

CSS 語音參考 概述 CSS&#xff08;層疊樣式表&#xff09;是用于描述HTML或XML文檔樣式的樣式表語言。它為網頁元素提供了一種統一的方式來定義字體、顏色、布局和其他視覺屬性。CSS語音參考旨在為開發者提供一個詳盡的指南&#xff0c;以便他們能夠更有效地使用CSS來增強網頁…

C# WPF 實現讀取文件夾中的PDF并顯示其頁數

文章目錄技術選型第一步&#xff1a;創建項目并安裝依賴庫第二步&#xff1a;定義數據模型 (Model)第三步&#xff1a;創建視圖模型 (ViewModel)第四步&#xff1a;設計用戶界面 (View)總結與解釋后記關于轉換器的錯誤工作中需要整理一些PDF格式文件&#xff0c;程序員的存在就…

設計模式(五)創建型:原型模式詳解

設計模式&#xff08;五&#xff09;創建型&#xff1a;原型模式詳解原型模式&#xff08;Prototype Pattern&#xff09;是 GoF 23 種設計模式中的創建型模式之一&#xff0c;其核心價值在于通過復制現有對象來創建新對象&#xff0c;而不是通過 new 關鍵字調用構造函數。它特…

K8S 八 數據存儲-高級存儲PV PVC 生命周期;配置存儲ConfigMap Secret

目錄數據存儲 Volume8.1 基本存儲8.1.1 EmptyDir8.1.2 HostPath 掛載目錄8.1.3 NFSnfs的服務8.2 高級存儲8.2.1 PV和PVC8.2.2 PV 持久化卷申請8.2.3 PVC 資源申請PVC的配置參數8.2.4 生命周期配置存儲8.3.1 ConfigMap8.3.2 Secret數據存儲 Volume Kubernetes的Volume支持多種類…

Baumer工業相機堡盟工業相機如何通過YoloV8深度學習模型實現輪船檢測識別(C#代碼UI界面版)

Baumer工業相機堡盟工業相機如何通過YoloV8深度學習模型實現輪船檢測識別&#xff08;C#代碼UI界面版&#xff09;工業相機使用YoloV8模型實現輪船檢測識別工業相機通過YoloV8模型實現輪船檢測識別的技術背景在相機SDK中獲取圖像轉換圖像的代碼分析工業相機圖像轉換Bitmap圖像格…

自習室預約小程序的設計與實現

自習室預約小程序的設計與實現現代學習環境對高效、便捷的預約系統需求日益增長。自習室預約小程序結合前沿技術棧&#xff0c;提供流暢的用戶體驗和強大的后臺管理功能&#xff0c;滿足學生、職場人士等群體的自習需求。技術架構與核心功能Vue.js 構建動態前端界面 采用 Vue.j…

Docker 實戰大綱

文章目錄Docker 實戰 – Mysql &#xff08;敬請期待……&#xff09;

從一個“詭異“的C++程序理解狀態機、防抖與系統交互

引言 在編程世界中&#xff0c;有時一個看似簡單的代碼片段可能隱藏著令人驚訝的復雜性。本文將從一個"故意設計"的C程序出發&#xff0c;深入探討其背后涉及的狀態機模式、防抖機制以及操作系統與控制臺的交互原理。通過這個案例&#xff0c;我們不僅能理解這些核心…

NAS-Bench-101: Towards Reproducible Neural Architecture Search

概述這篇題為"NAS-Bench-101: Towards Reproducible Neural Architecture Search"的論文由Chris Ying等人合作完成&#xff0c;旨在解決神經網絡架構搜索(NAS)領域面臨的重大挑戰&#xff1a;計算資源需求高和實驗難以復現的問題。論文提出了NAS-Bench-101&#xff0…

SpringBoot整合Fastexcel/EasyExcel導出Excel導出多個圖片

整個工具的代碼都在Gitee或者Github地址內 gitee&#xff1a;solomon-parent: 這個項目主要是總結了工作上遇到的問題以及學習一些框架用于整合例如:rabbitMq、reids、Mqtt、S3協議的文件服務器、mongodb、xxl-job、powerjob還有用Docker compose部署各類中間組件。如果大家有…

網絡原理--HTTPHTTPS

目錄 一、HTTP 1.1 HTTP是什么 1.2 HTTP協議的工作過程 1.3 HTTP協議格式 1.3.1 抓包工具的使用 1.3.2 抓包結果 1.4 HTTP請求 1.4.1 URL 1.4.2 認識“方法” (method) 1.4.3 認識請求“報頭”(header) 1.4.4 認識請求“正文”(body) 1.5 HTTP 響應詳解 1.5.1 HTTP…

『 C++ 入門到放棄 』- 哈希表

一、哈希的概念 哈希&#xff0c;也稱「 散列 」是一種用來進行高效查找的數據結構&#xff0c;查找的時間復雜度平均為O(1)&#xff0c;其本質就是依賴哈希函數這個算法來將 key 和該 key 存儲位置建立一個映射關系。 而因為是有著映射關系&#xff0c;所以哈希的事件復雜度為…

零售收銀系統開源代碼全解析:連鎖門店一體化解決方案(含POS+進銷存+商城)

過去10年&#xff0c;收銀系統技術經歷了從單機版到云服務、從單純結算到全渠道整合的快速演進。面對連鎖多門店、AI稱重、智能分賬、跨店庫存同步等新需求&#xff0c;很多企業的現有傳統saas系統已顯乏力。本文將梳理收銀系統關鍵技術指標&#xff0c;助您在系統升級時做出明…

能源高效利用如何實現?樓宇自控系統智能化監管建筑設備

隨著全球能源危機日益嚴峻和“雙碳”目標的持續推進&#xff0c;建筑領域作為能耗大戶&#xff08;約占社會總能耗的40%&#xff09;&#xff0c;其節能潛力備受關注。樓宇自控系統&#xff08;Building Automation System&#xff0c;簡稱BAS&#xff09;作為建筑智能化的核心…

校園二手交易小程序的設計與實現

文章目錄前言詳細視頻演示具體實現截圖后端框架SpringBoot微信小程序持久層框架MyBaits成功系統案例&#xff1a;參考代碼數據庫源碼獲取前言 博主介紹:CSDN特邀作者、985高校計算機專業畢業、現任某互聯網大廠高級全棧開發工程師、Gitee/掘金/華為云/阿里云/GitHub等平臺持續…

Redis(二):Redis高級特性和應用(慢查詢、Pipeline、事務)

Redis的慢查詢 許多存儲系統&#xff08;例如 MySQL)提供慢查詢日志幫助開發和運維人員定位系統存在的慢操作。所謂慢查詢日志就是系統在命令執行前后計算每條命令的執行時間&#xff0c;當超過預設閥值,就將這條命令的相關信息&#xff08;例如:發生時間&#xff0c;耗時&…

如何為你的WordPress網站選擇合適的安全插件

在管理WordPress網站時&#xff0c;安全因素至關重要。由于WordPress的廣泛使用&#xff0c;它也成為了黑客攻擊的首要目標。為了避免潛在的安全風險&#xff0c;選擇合適的安全插件至關重要。而Wordfence和iThemes&#xff0c;作為兩款頗具人氣的WordPress安全插件&#xff0c…

我們使用Rust開發的AI知識庫應用

這段時間陸陸續續的開發了2個AI知識庫應用&#xff0c;一個面向企業&#xff0c;一個面向C端用戶。 飛樹智庫&#xff1a;一個安全高效的面向 企業的知識庫平臺&#xff08;https://fskb.coderbox.cn/&#xff09;。 小飛樹&#xff1a;一個專注于個人知識管理的AI應用&#…

自動化測試實戰篇

目錄 1. 自動化實施步驟 1.1 編寫web測試用例 1.2 自動化測試腳本開發 1.3 將自動化測試補充至測試報告 1. 自動化實施步驟 1.1 編寫web測試用例 1.2 自動化測試腳本開發 TestDevelopment: 測試用例 - Gitee.comhttps://gitee.com/Axurea/test-development/tree/master/2…

idea 服務器Debug端口啟動設置

一&#xff1a;在阿里云服務器安全組已經設置了端口授權對象&#xff1a;正確命令&#xff1a;nohup java -Xdebug -Xrunjdwp:transportdt_socket,servery,suspendn,address9998 -jar -Duser.timezoneGMT08 -Xms256m -Xmx256m /opt/projects/*/*/*-starter-1.0-SNAPSHOT.jar -…