Angular 面试题及详细答案

Angular 面试题及详细答案

Angular 作为 Google 维护的企业级前端框架,其面试考察通常覆盖核心概念、生命周期、依赖注入、状态管理、性能优化等维度。以下整理了高频面试题及深度解析,帮助理解原理而非单纯记忆。

一、基础概念与核心特性

1. Angular 和 AngularJS 的主要区别是什么?

Angular(通常指 Angular 2+,简称 Angular)是 AngularJS(Angular 1.x)的完全重写版本,核心差异如下:

对比维度 AngularJS (1.x) Angular (2+)
语言支持 仅支持 JavaScript 推荐 TypeScript(强类型),也支持 JS
架构模式 MVC(Model-View-Controller) 组件化架构(Component-Based)
数据绑定 双向绑定(基于脏检查) 双向绑定(基于 Zone.js)+ 单向绑定([]
依赖注入 模块级注入,配置复杂 层级注入(模块/组件/指令),API 更清晰
性能 脏检查机制,大应用性能差 增量变更检测,性能更优
移动支持 无原生支持,需第三方框架(如 Ionic 1) 原生支持 PWA、移动端适配,可配合 Ionic 4+
生态 老旧,社区活跃度低 活跃,官方维护路由、表单等模块

2. Angular 的核心模块(Modules)有哪些?各自的作用是什么?

Angular 中模块(NgModule)是组织代码的基本单元 ,每个 Angular 应用至少有一个根模块(AppModule),核心模块分为:

模块名称 作用
BrowserModule 浏览器平台必备模块,提供 ngIfngFor 等内置指令,仅根模块导入。
CommonModule 包含 BrowserModule 的核心指令(无平台相关代码),子模块导入以复用指令。
FormsModule 提供模板驱动表单支持(如 ngModelngForm)。
ReactiveFormsModule 提供响应式表单支持(如 FormGroupFormControl),更适合复杂表单。
RouterModule 路由核心模块,通过 forRoot()(根模块)/forChild()(子模块)配置路由。
HttpClientModule 提供 HTTP 请求能力(替代 AngularJS 的 $http),支持拦截器、TypeScript 类型。

3. 什么是 Angular 组件(Component)?它的核心组成部分有哪些?

组件是 Angular 应用的最小视图单元,负责封装 UI 结构、样式和逻辑,是"页面积木"。一个完整的组件由 4 部分组成:

  1. 装饰器(@Component:定义组件元数据,核心属性包括:

    • selector:组件的 HTML 标签名(如 <app-user>),用于在其他模板中引用。
    • template/templateUrl:组件的 HTML 模板(内联模板/外部文件路径)。
    • styles/styleUrls:组件的 CSS 样式(内联样式/外部文件路径),默认样式隔离(仅作用于当前组件)。
    • providers:在组件层级注入依赖(覆盖模块级注入)。
  2. 类(Class) :包含组件的业务逻辑,如数据(property)、方法(method),通过模板绑定与视图交互。

  3. 模板(Template) :HTML 结构,通过 Angular 模板语法(如 {``{}}[]())绑定类中的数据和事件。

  4. 样式(Styles) :组件的样式,支持 scoped(默认)、::ng-deep(穿透子组件样式)等特性。

示例

typescript 复制代码
// user.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-user',
  template: `
    <h2>Hello, {{ userName }}!</h2>
    <button (click)="changeName()">Change Name</button>
  `,
  styles: [`h2 { color: blue; }`]
})
export class UserComponent {
  userName = 'Alice'; // 数据
  changeName() { // 方法
    this.userName = 'Bob';
  }
}

二、模板语法与数据绑定

1. Angular 有哪些数据绑定方式?分别用于什么场景?

Angular 提供 4 种核心绑定方式,覆盖"数据从类到视图""视图到类""双向同步"的场景:

绑定类型 语法示例 方向 作用场景
插值绑定 {``{ user.name }} 类 → 视图 展示类中的数据(文本内容)。
属性绑定 <img [src]="imgUrl"> 类 → 视图 绑定 HTML 属性/组件输入属性(如 [disabled])。
事件绑定 <button (click)="onClick()"> 视图 → 类 监听 DOM 事件/组件输出事件(如点击、输入)。
双向绑定 <input [(ngModel)]="user.name"> 类 ↔ 视图 数据在类和视图间同步(需导入 FormsModule)。

注意 :双向绑定本质是"属性绑定 + 事件绑定"的语法糖,等价于 <input [value]="user.name" (input)="user.name = $event.target.value">

2. 什么是 *ngIf*ngFor?它们的区别和注意事项是什么?

两者都是 Angular 内置结构型指令(前缀 * 表示"修改 DOM 结构"):

*ngIf:条件渲染
  • 作用:根据表达式布尔值决定是否在 DOM 中添加/移除元素(而非隐藏)。
  • 示例<div *ngIf="isShow">仅当 isShow 为 true 时显示</div>
  • 注意 :频繁切换可能触发 DOM 频繁创建/销毁,性能敏感场景可考虑 [hidden](仅隐藏,DOM 保留)。
*ngFor:循环渲染
  • 作用:根据数组/可迭代对象生成重复的 DOM 元素。
  • 核心语法*ngFor="let item of list; let i = index; trackBy: trackByFn"
  • 注意事项
    1. 必须加 trackBy :默认情况下,数组变化时 *ngFor 会重新渲染所有元素;trackBy 通过唯一标识(如 id)复用 DOM,提升性能。
    2. index 变量:获取当前循环的索引(从 0 开始)。

trackBy 示例

typescript 复制代码
// 组件类
trackByUserId(index: number, user: User): number {
  return user.id; // 用用户唯一 ID 跟踪
}

// 模板
<div *ngFor="let user of userList; trackBy: trackByUserId">
  {{ user.name }}
</div>

3. 什么是管道(Pipe)?常用的内置管道有哪些?

管道是 Angular 中用于转换模板数据格式 的工具(如日期、货币、大小写转换),语法为 {``{ 数据 | 管道名: 参数 }}

常用内置管道:
  • date:格式化日期,示例:{``{ today | date: 'yyyy-MM-dd HH:mm' }}(输出:2024-05-20 14:30)。
  • currency:格式化货币,示例:{``{ price | currency: 'CNY' }}(输出:¥100.00)。
  • uppercase/lowercase:转换大小写,示例:{``{ 'hello' | uppercase }}(输出:HELLO)。
  • async:处理异步数据(如 ObservablePromise),自动订阅/取消订阅,避免内存泄漏,示例:{``{ user$ | async }}
  • json:将对象转为 JSON 字符串,用于调试,示例:{``{ user | json }}
自定义管道:

通过 @Pipe 装饰器定义,需实现 PipeTransform 接口的 transform 方法。示例(首字母大写管道):

typescript 复制代码
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'firstUpper' }) // 管道名称
export class FirstUpperPipe implements PipeTransform {
  transform(value: string): string {
    if (!value) return '';
    return value[0].toUpperCase() + value.slice(1);
  }
}

// 模板使用:{{ 'angular' | firstUpper }} → 输出 Angular

三、组件通信

1. 父组件如何向子组件传递数据?

通过输入属性(@Input() 实现,步骤如下:

  1. 子组件中用 @Input() 装饰器定义可接收的属性:

    typescript 复制代码
    // 子组件:child.component.ts
    import { Input } from '@angular/core';
    
    export class ChildComponent {
      @Input() childName: string; // 父组件可传递的属性
      @Input('alias') age: number; // 可选:设置别名,父组件用别名传递
    }
  2. 父组件模板中通过属性绑定传递数据:

    html 复制代码
    <!-- 父组件模板 -->
    <app-child 
      [childName]="parentName"  <!-- 无别名:直接用子组件属性名 -->
      [alias]="parentAge"       <!-- 有别名:用别名传递 -->
    ></app-child>
  3. (可选)子组件监听输入属性变化:

    通过 ngOnChanges 生命周期钩子(监听多个属性)或 setter 方法(监听单个属性):

    typescript 复制代码
    // 方式1:setter 监听单个属性
    @Input() set childName(name: string) {
      this._childName = name ? name.trim() : '';
    }
    private _childName: string;
    
    // 方式2:ngOnChanges 监听多个属性
    import { OnChanges, SimpleChanges } from '@angular/core';
    
    export class ChildComponent implements OnChanges {
      @Input() childName: string;
      @Input() age: number;
    
      ngOnChanges(changes: SimpleChanges): void {
        // changes 包含所有变化的输入属性
        if (changes['childName']) {
          console.log('childName 变化:', changes['childName'].currentValue);
        }
      }
    }

2. 子组件如何向父组件传递数据?

通过输出属性(@Output() + EventEmitter 实现,本质是"子组件触发事件,父组件监听事件":

  1. 子组件中用 @Output() 装饰器定义事件发射器:

    typescript 复制代码
    // 子组件:child.component.ts
    import { Output, EventEmitter } from '@angular/core';
    
    export class ChildComponent {
      // 定义输出事件(可选设置别名)
      @Output() sendMsg = new EventEmitter<string>();
    
      // 子组件触发事件,传递数据
      onClick() {
        this.sendMsg.emit('Hello from Child!'); // 发送数据
      }
    }
  2. 父组件模板中通过事件绑定监听子组件事件:

    html 复制代码
    <!-- 父组件模板 -->
    <app-child (sendMsg)="receiveMsg($event)"></app-child>
  3. 父组件定义方法接收数据:

    typescript 复制代码
    // 父组件:parent.component.ts
    receiveMsg(msg: string): void {
      console.log('收到子组件消息:', msg); // 输出:收到子组件消息:Hello from Child!
    }

3. 非父子组件(如兄弟组件)如何通信?

非父子组件无直接关联,需通过"中间层"传递数据,常用方案有 3 种:

方案1:共享服务(推荐)

通过一个全局/模块级别的服务,利用 Observable(如 Subject)实现事件订阅/发布:

typescript 复制代码
// 共享服务:msg.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({ providedIn: 'root' }) // 全局注入
export class MsgService {
  private msgSubject = new Subject<string>(); // 私有 Subject(避免外部直接调用)
  msg$ = this.msgSubject.asObservable(); // 暴露为 Observable(仅允许订阅)

  // 发送消息的方法
  sendMsg(msg: string): void {
    this.msgSubject.next(msg);
  }
}

// 发送方组件(如 Brother1)
export class Brother1Component {
  constructor(private msgService: MsgService) {}
  send() {
    this.msgService.sendMsg('Hi from Brother1!');
  }
}

// 接收方组件(如 Brother2)
export class Brother2Component {
  constructor(private msgService: MsgService) {}
  ngOnInit() {
    // 订阅消息
    this.msgService.msg$.subscribe(msg => {
      console.log('Brother2 收到:', msg);
    });
  }
}
方案2:通过父组件中转

兄弟组件通过共同的父组件传递:

  • 兄弟 A → 父组件(@Output());
  • 父组件 → 兄弟 B(@Input())。
方案3:状态管理库(大型应用)

NgRx(Angular 官方推荐,基于 Redux 思想),统一管理全局状态,适合复杂应用(如多组件共享大量数据)。

四、生命周期钩子

1. Angular 组件的生命周期钩子有哪些?按执行顺序说明作用。

Angular 组件从"创建"到"销毁"会触发一系列生命周期钩子,核心钩子按执行顺序如下:

钩子名称 执行时机 核心作用
ngOnChanges 输入属性(@Input())变化时触发(首次赋值也触发) 监听输入属性变化,处理数据更新逻辑。
ngOnInit 组件初始化完成后触发(仅一次) 执行初始化逻辑(如调用接口、订阅数据)。
ngDoCheck 每次变更检测时触发(频率高) 自定义变更检测逻辑(谨慎使用,避免性能问题)。
ngAfterContentInit 组件内容投影(<ng-content>)初始化后触发(仅一次) 操作投影内容(如获取投影的 DOM 元素)。
ngAfterContentChecked 投影内容变更检测后触发 监听投影内容的变化。
ngAfterViewInit 组件视图(模板)初始化后触发(仅一次) 操作组件内部 DOM 元素(如通过 @ViewChild 获取元素)。
ngAfterViewChecked 组件视图变更检测后触发 监听视图的变化。
ngOnDestroy 组件销毁前触发(仅一次) 清理资源(如取消 Observable 订阅、清除定时器),避免内存泄漏。

示例:组件初始化时调用接口,销毁时取消订阅:

typescript 复制代码
import { OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { UserService } from './user.service';

export class UserComponent implements OnInit, OnDestroy {
  private userSub: Subscription;

  ngOnInit() {
    this.userSub = this.userService.getUser().subscribe(user => {
      console.log(user);
    });
  }

  ngOnDestroy() {
    this.userSub.unsubscribe(); // 清理订阅
  }
}

五、依赖注入(DI)

1. 什么是 Angular 的依赖注入?它的核心作用是什么?

依赖注入(Dependency Injection,DI)是 Angular 的核心设计模式,指**"组件/服务的依赖由框架自动注入,而非手动创建"**。

核心作用:
  1. 解耦:组件无需关心依赖的创建逻辑,只需声明依赖,降低代码耦合度。
  2. 复用:依赖(如服务)可在多个组件间共享,避免重复代码。
  3. 测试友好:可轻松替换依赖的模拟实现(如测试时用模拟服务替代真实接口服务)。
核心概念:
  • 注入器(Injector):负责创建和管理依赖实例的容器,Angular 有三级注入器(根注入器 → 模块注入器 → 组件注入器),层级越细优先级越高。
  • 提供商(Provider) :告诉注入器"如何创建依赖实例",常用 useClass(用类实例化)、useValue(用固定值)、useFactory(用工厂函数)。
  • 依赖令牌(Token):注入器查找依赖的唯一标识(通常是服务类本身)。

2. 如何创建和使用一个服务(Service)?

服务是 Angular 中用于封装共享逻辑(如接口调用、数据处理) 的类,通过 @Injectable 装饰器标记为可注入。

步骤1:创建服务(用 Angular CLI 命令)
bash 复制代码
ng generate service user  # 生成 user.service.ts
步骤2:定义服务逻辑
typescript 复制代码
// user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from './user.model';

@Injectable({
  providedIn: 'root'  // 根注入器:服务实例在整个应用中唯一
  // 也可设置为模块:providedIn: UserModule → 仅该模块内共享
})
export class UserService {
  constructor(private http: HttpClient) {} // 注入 HttpClient 依赖

  // 封装接口调用逻辑
  getUser(id: number): Observable<User> {
    return this.http.get<User>(`https://api.example.com/users/${id}`);
  }
}
步骤3:在组件中使用服务

组件只需在构造函数中声明依赖,Angular 会自动注入服务实例:

typescript 复制代码
// user.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';
import { User } from './user.model';

@Component({ selector: 'app-user' })
export class UserComponent implements OnInit {
  user: User;

  // 声明依赖(无需手动 new UserService())
  constructor(private userService: UserService) {}

  ngOnInit() {
    this.userService.getUser(1).subscribe(user => {
      this.user = user;
    });
  }
}

六、路由(Routing)

1. Angular 路由的核心概念有哪些?如何配置基础路由?

Angular 路由(@angular/router)负责管理组件间的导航,核心概念和配置步骤如下:

核心概念:
  • 路由配置(Route) :定义"路径 → 组件"的映射,如 { path: 'user', component: UserComponent }
  • 路由出口(<router-outlet>:路由组件的渲染容器,匹配的组件会替换该标签的内容。
  • 路由链接(<a routerLink="..."> :导航链接,替代原生 <a href>(避免页面刷新)。
  • 激活路由(ActivatedRoute):获取当前路由的信息(如参数、查询参数)。
  • 路由守卫(Route Guard):控制路由访问权限(如登录验证)。
基础路由配置步骤:
  1. 导入 RouterModule 和组件:

    typescript 复制代码
    // app.module.ts
    import { RouterModule } from '@angular/router';
    import { HomeComponent } from './home.component';
    import { UserComponent } from './user.component';
  2. 定义路由配置数组:

    typescript 复制代码
    const routes = [
      { path: '', redirectTo: '/home', pathMatch: 'full' }, // 默认路由(重定向到首页)
      { path: 'home', component: HomeComponent }, // 首页路由
      { path: 'user/:id', component: UserComponent }, // 带参数的路由(:id 是参数占位符)
      { path: '**', component: NotFoundComponent } // 通配符路由(匹配未定义的路径,通常用于404页面)
    ];
  3. @NgModuleimports 中配置路由:

    typescript 复制代码
    @NgModule({
      imports: [
        BrowserModule,
        RouterModule.forRoot(routes) // 根模块用 forRoot()
      ],
      // ...
    })
  4. 在根组件模板中添加 <router-outlet> 和导航链接:

    html 复制代码
    <!-- app.component.html -->
    <nav>
      <a routerLink="/home">首页</a>
      <a routerLink="/user/1">用户1</a>
    </nav>
    <router-outlet></router-outlet> <!-- 路由组件将在这里渲染 -->

2. 如何在组件中获取路由参数?

通过 ActivatedRoute 服务获取路由参数(如 user/:id 中的 id),常用两种方式:

方式1:快照(Snapshot)------ 参数不变时使用

适用于"参数一旦设置就不会变化"的场景(如从列表页进入详情页,参数固定):

typescript 复制代码
// user.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

export class UserComponent implements OnInit {
  userId: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    // 从快照中获取参数
    this.userId = this.route.snapshot.paramMap.get('id');
    console.log('用户ID:', this.userId);
  }
}
方式2:订阅(Subscribe)------ 参数可能变化时使用

适用于"同一组件内参数可能变化"的场景(如详情页内切换用户,组件不销毁,仅参数变化):

typescript 复制代码
export class UserComponent implements OnInit {
  userId: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    // 订阅参数变化
    this.route.paramMap.subscribe(params => {
      this.userId = params.get('id');
      console.log('用户ID(更新后):', this.userId);
    });
  }
}

补充 :获取查询参数(如 user?id=1&name=alice 中的 idname):

typescript 复制代码
// 快照方式
const id = this.route.snapshot.queryParamMap.get('id');
const name = this.route.snapshot.queryParamMap.get('name');

// 订阅方式
this.route.queryParamMap.subscribe(queryParams => {
  const id = queryParams.get('id');
});

七、性能优化

1. Angular 应用有哪些常见的性能优化手段?

Angular 性能优化需从"变更检测、渲染、资源加载"等维度入手,常用手段如下:

  1. 优化变更检测

    • 使用 OnPush 变更检测策略 :组件装饰器中设置 changeDetection: ChangeDetectionStrategy.OnPush,仅当输入属性(@Input())引用变化、组件内事件触发或手动调用 markForCheck() 时,才执行变更检测,减少不必要的检测。
    • 避免在模板中调用函数 :模板中 {``{ getUserName() }} 会在每次变更检测时执行,建议提前计算并存储为属性。
  2. 减少 DOM 操作

    • *ngFortrackBy:复用 DOM 元素,避免频繁重新渲染(见"模板语法"部分)。
    • 虚拟滚动(Virtual Scrolling) :使用 @angular/cdk/scrollingCdkVirtualScrollViewport,仅渲染可视区域的列表项(适合万级以上数据)。
  3. 资源优化

    • 懒加载模块(Lazy Loading) :路由配置中用 loadChildren 延迟加载模块,减少初始加载体积:

      typescript 复制代码
      const routes = [
        { 
          path: 'user', 
          loadChildren: () => import('./user/user.module').then(m => m.UserModule) 
        }
      ];
    • 压缩代码 :通过 Angular CLI 的 --prod 模式(ng build --prod)自动压缩 JS/CSS/HTML,开启 Tree Shaking(移除未使用代码)。

  4. 内存泄漏防护

    • 取消订阅Observable 订阅后,在 ngOnDestroy 中调用 unsubscribe(),或使用 async 管道(自动取消订阅)。
    • 清除定时器setTimeout/setIntervalngOnDestroy 中调用 clearTimeout/clearInterval

八、进阶问题

1. 什么是 Angular 拦截器(Interceptor)?如何实现一个 HTTP 拦截器?

拦截器是 Angular 中用于统一拦截和处理 HTTP 请求/响应 的服务(如添加 Token、处理错误、显示加载动画),需实现 HttpInterceptor 接口。

实现步骤:
  1. 创建拦截器类:

    typescript 复制代码
    // auth.interceptor.ts(添加请求 Token 的拦截器)
    import { Injectable } from '@angular/core';
    import {
      HttpRequest,
      HttpHandler,
      HttpEvent,
      HttpInterceptor
    } from '@angular/common/http';
    import { Observable } from 'rxjs';
    
    @Injectable()
    export class AuthInterceptor implements HttpInterceptor {
    
      constructor() {}
    
      // 拦截请求的核心方法
      intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
        // 1. 获取本地存储的 Token
        const token = localStorage.getItem('token');
    
        // 2. 克隆请求并添加 Token 头(请求对象不可变,需用 clone() 修改)
        if (token) {
          const authRequest = request.clone({
            setHeaders: { Authorization: `Bearer ${token}` }
          });
          // 3. 继续处理修改后的请求
          return next.handle(authRequest);
        }
    
        // 无 Token 时,直接处理原请求
        return next.handle(request);
      }
    }
  2. 在模块中注册拦截器:

    需在 @NgModuleproviders 中用 HTTP_INTERCEPTORS 令牌注册,多个拦截器按注册顺序执行:

    typescript 复制代码
    // app.module.ts
    import { HTTP_INTERCEPTORS } from '@angular/common/http';
    import { AuthInterceptor } from './auth.interceptor';
    
    @NgModule({
      providers: [
        {
          provide: HTTP_INTERCEPTORS,
          useClass: AuthInterceptor,
          multi: true // 允许注册多个拦截器
        }
      ]
    })
    export class AppModule {}

2. 模板驱动表单和响应式表单的区别是什么?如何选择?

Angular 提供两种表单实现方式,核心差异在于"逻辑的存放位置":

对比维度 模板驱动表单(Template-Driven) 响应式表单(Reactive)
逻辑位置 逻辑在模板中(依赖指令如 ngModel 逻辑在组件类中(依赖 FormGroup 等)
数据模型 隐式模型(由 ngModel 维护) 显式模型(组件类中定义 FormGroup
验证方式 模板中添加验证指令(如 required 类中定义验证器(如 Validators.required
灵活性 简单场景友好,复杂场景难扩展 灵活,支持动态表单(如动态添加字段)
测试友好性 难测试(需操作 DOM) 易测试(直接测试类中的表单模型)
适用场景 简单表单(如登录、注册) 复杂表单(如多步骤表单、动态字段)
选择建议:
  • 简单表单(字段少、验证逻辑简单):选模板驱动表单,开发速度快。
  • 复杂表单(动态字段、多验证规则、需频繁修改):选响应式表单,可维护性和扩展性更强。

3. 什么是 NgRx?它的核心组成部分有哪些?

NgRx 是 Angular 生态中的状态管理库,基于 Redux 思想和 RxJS,用于统一管理跨组件的全局状态(如用户信息、购物车数据),适合中大型应用。

核心组成部分(遵循"单向数据流"):
  1. Store :存储应用的全局状态(单一数据源),通过 select 方法获取状态,通过 dispatch 方法触发动作。
  2. Action :描述"发生了什么"的纯对象(如 { type: 'user/login', payload: { name: 'Alice' } }),是状态变化的唯一原因。
  3. Reducer :纯函数,接收当前状态和 Action,返回新状态(不修改原状态),公式:(state, action) => newState
  4. Effect:处理副作用(如接口调用、定时器),监听 Action 并触发异步操作,操作完成后派发新的 Action 更新状态。
  5. Selector :纯函数,从 Store 中提取指定状态(如 selectUser 提取用户信息),支持状态缓存和组合。
简单工作流:
  1. 组件通过 store.dispatch(action) 触发动作(如用户点击"登录")。
  2. Effect 监听该 Action,调用登录接口(副作用),接口成功后派发 loginSuccess Action。
  3. Reducer 接收 loginSuccess Action,更新 Store 中的用户状态。
  4. 组件通过 store.select(selector) 订阅状态变化,更新视图。

以上面试题覆盖了 Angular 从基础到进阶的核心知识点,理解原理并结合实际项目经验,才能在面试中灵活应对。

相关推荐
sheji34168 小时前
【开题答辩全过程】以 Web数据挖掘在电子商务中的应用研究为例,包含答辩的问题和答案
前端·人工智能·数据挖掘
whltaoin8 小时前
Vue 与 React 深度对比:技术差异、选型建议与未来趋势
前端·前端框架·vue·react·技术选型
敲敲敲-敲代码8 小时前
web系统(asp.net和C#)
前端·c#·asp.net
IT_陈寒8 小时前
Python开发者必坑指南:3个看似聪明实则致命的‘优化’让我损失了50%性能
前端·人工智能·后端
初圣魔门首席弟子9 小时前
c++中this指针使用bug
前端·c++·bug
AI视觉网奇14 小时前
rknn yolo11 推理
前端·人工智能·python
gplitems12314 小时前
Gunslinger – Gun Store & Hunting WordPress Theme: A Responsible
开发语言·前端·javascript
wyzqhhhh17 小时前
less和sass
前端·less·sass
Nan_Shu_61418 小时前
学习:uniapp全栈微信小程序vue3后台-额外/精彩报错篇
前端·学习·微信小程序·小程序·uni-app·notepad++
excel19 小时前
Vue3 中的双向链表依赖管理详解与示例
前端