Angular 快速入门:从零搭建你的第一个应用
1. Angular 到底是什么?
你可能听过 Angular,也可能在纠结"学 Angular 还是学 React"。在动手之前,我们先花两分钟搞清楚它到底是什么。
Angular 是一个前端框架,而且是那种"全家桶"式的框架。
什么意思呢?打个比方:
- React 像一家只卖面粉的店。你买了面粉回去,想蒸馒头、擀面条、包饺子,都得自己再买其他工具和材料。
- Vue 像一家半成品的超市。馒头坯子、切好的菜都有,但有些调料你得自己去配。
- Angular 像一份外卖套餐。米饭、菜、汤、餐具全给你配齐了,打开就能吃。
Angular 把路由、HTTP 请求、表单处理、依赖注入、模块化这些东西全部内置了。你不需要像 React 那样去纠结"用 React Router 还是用 Next.js",也不需要像 Vue 那样去选"用 Vue Router 还是 Pinia"。Angular 直接给你一套官方方案。
这意味着两件事:
好处:团队不用吵架选技术方案了。Angular 项目里,大家的写法高度统一,换人接手代码的成本低很多。
坏处:学习曲线相对陡。因为 Angular 给你的东西多,你得先理解它的"世界观"才能上手。不像 React,你懂 JavaScript 就能开始写。
所以 Angular 特别适合大型企业级应用 和严肃的多人团队。你很少看到个人博客用 Angular,但很多银行的内部系统、企业管理后台、Google 自己的产品(比如 Google Cloud Console)都是 Angular 写的。
对了,我说的 Angular 是 Angular 2 之后的版本,不是那个老掉牙的 AngularJS(1.x)。这两个东西差别巨大,如果你搜资料看到 AngularJS,直接绕开。
另外,这篇教程基于 Angular 17+ ,也就是默认使用 standalone(独立组件)模式的版本。如果你用的 Angular 版本更老,项目里会多出一个
app.module.ts,我会在第 3 节简单提一下。
2. 开工:装环境、建项目
聊完了概念,直接上手。第一步,装 Angular 官方提供的命令行工具------Angular CLI。
bash
npm install -g @angular/cli
装完之后,你就有 ng 这个命令了。用它来创建一个新项目:
bash
ng new todo-app
这时候 CLI 会问你几个问题:
- Would you like to add Angular routing? 选
y。路由后面会用到。 - Which stylesheet format would you like to use? 选
CSS,最简单。
然后等它安装依赖。创建完成后:
bash
cd todo-app
ng serve
浏览器打开 http://localhost:4200,你就能看到一个默认页面。
就这么简单,你的第一个 Angular 应用已经跑起来了。
如果你用过 Vue 的
vue create或者 React 的create-react-app,会发现流程几乎一样。区别在于 Angular CLI 默认就给你配好了 TypeScript、测试框架、lint 工具------又是"全家桶"的体现。
3. 项目里都有啥?
用编辑器打开 todo-app 目录,你可能会被一堆文件吓到。别慌,大部分文件你以后才会接触到,现在只需要关注 src/ 下面的几个关键文件:
src/
├── main.ts # 应用的入口,启动整个应用
├── index.html # 主页 HTML
├── styles.css # 全局样式
└── app/
├── app.ts # ❤️ 根组件(新版 CLI 去掉了 .component 前缀)
├── app.html # ✅ 新版 CLI 简化了命名,不再是 app.component.html
├── app.css # ✅ 同理
└── app.config.ts # 应用的全局配置
有没有注意到少了什么? 没有 app.module.ts 了。
如果你搜 Angular 教程,很多老教程都会先讲 NgModule。但从 Angular 15 开始,官方推出了"独立组件"(standalone components),并在 Angular 17 把它变成了默认模式。新建的项目默认不生成模块文件了。
这是 Angular 团队这几年做的最重要的改变。为什么?因为他们自己也发现------"每次写个小组件还得注册到模块里"这件事太烦人了。Vue 和 React 从来没有这种负担。所以 Angular 决定把这个包袱砍掉。
不过你以后维护老项目还是会遇到 NgModule,它没有被删除。如果你想了解,我最后会简单提一下。
main.ts ------ 启动入口
打开 main.ts,会看到这样的代码:
typescript
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app';
bootstrapApplication(AppComponent, appConfig)
.catch(err => console.error(err));
看到区别了吗?不再是启动一个模块,而是直接启动根组件。 这跟 Vue 的 createApp(App).mount('#app') 和 React 的 createRoot(document.getElementById('root')).render(<App/>) 非常像了。
app.config.ts ------ 全局配置
在 Angular 里,路由、HTTP 拦截器这些全局功能,不是靠"模块"来配置的了,而是通过 app.config.ts:
typescript
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes)
]
};
这里用了一个叫 provide 函数 的模式。需要什么功能,就调用对应的 provideXxx() 函数。是不是有点熟悉?Vue 3 的 app.use(router) 和 React 的各种 Provider 也是类似的路数。
app.ts ------ 根组件(standalone模式)
typescript
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root', // 这个组件在 HTML 中的标签名
standalone: true, // ✅ 独立组件
imports: [RouterOutlet], // 组件中要用到的其他组件/指令
templateUrl: './app.html',
styleUrls: ['./app.css']
})
export class AppComponent {
title = 'todo-app';
}
注意三个关键点:
standalone: true:这就是它的身份标识。没有NgModule包装,组件自己就是完整的。imports: []:组件需要用到的其他组件或指令,直接在这里导入。这替代了老版本NgModule里的declarations和imports的作用。@Component装饰器 :用@开头的这种语法叫装饰器(Decorator)。它在告诉 TypeScript:"下面这个类,是一个组件,配置信息在这呢。"
对比一下其他框架的写法:
- Vue :单文件组件
.vue,<template>、<script>、<style>写在一个文件里。- React:函数组件,JSX 直接 return HTML 和 JS 混在一起。
- Angular :逻辑(
.ts)、模板(.html)、样式(.css)默认分三个文件,用@Component装饰器把它们关联起来。没有好坏之分。Angular 这么拆分的一个实际好处是:设计师改样式的时候只用动
.css文件,不会误碰代码逻辑。
补一句:老项目的 NgModule 是什么?
如果你进了个老项目,看到 app.module.ts,别慌。它长这样:
typescript
@NgModule({
declarations: [AppComponent, TodoComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
你可以把 NgModule 理解成一个"装箱清单"------里面声明"我有哪些组件、用了哪些外部库、根组件是谁"。Angular 以前靠它来组织代码。
但在 standalone 模式下,每个组件就是自己的模块 。你不再需要一个集中清单了,直接在组件里 imports 声明你需要的东西就行。这更符合直觉------跟我用 npm 导包是一个思路。
4. 第一个组件------开始写 Todo
有了项目骨架,我们来创建自己的第一个组件。顺便开始我们的 Todo List 应用。
用 CLI 生成组件
Angular CLI 提供了生成组件的快捷命令:
bash
ng generate component todo
简写也可以:
bash
ng g c todo
执行完之后,你会在 src/app/ 下看到新文件夹 todo/,里面包含四个文件:
app/todo/
├── todo.ts # 组件逻辑(新版 CLI 去掉了 .component 前缀)
├── todo.html # 模板
├── todo.css # 样式
└── todo.spec.ts # 测试(暂时忽略)
注意,CLI 没有去任何文件里注册这个组件。因为在 standalone 模式下,组件自己就是自足的。你直接用就行。
如果你用过老版本的 Angular,一定记得创建完组件还得手动或自动加到
NgModule的declarations里。忘加了就报错。这曾是 Angular 被吐槽最多的地方之一。standalone 模式彻底解决了这个问题------跟 Vue、React 一样,import 即用。
组件长什么样?
打开 todo.ts:
typescript
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-todo',
standalone: true, // ✅ 独立组件
imports: [CommonModule], // 使用 *ngFor、*ngIf 等内置指令需要导入 CommonModule
templateUrl: './todo.html',
styleUrls: ['./todo.css']
})
export class TodoComponent {
// 这里写组件的逻辑
}
看到 standalone: true 了吗?和 AppComponent 一样。在 Angular 17+ 里,CLI 生成的所有组件默认都是 standalone。
注意 imports 里多了一个 CommonModule。这是因为 standalone 模式下,*ngFor、*ngIf 这些内置指令不会自动全局可用 ,你需要从 @angular/common 里导入 CommonModule 才能用它们。
如果你用过老版本 Angular,模块模式下
CommonModule通常在根模块里一次性导入了,组件里直接写*ngFor就行。standalone 模式把这个显式化了------每个组件声明自己需要哪些功能。虽然多写一行imports,但好处是这个组件依赖什么一目了然,也方便做 tree-shaking。Vue 和 React 没有完全对等的概念,但 React 里你import { useState } from 'react'也是每个组件各自导入。
注意 selector 的值是 app-todo。这意味着你可以在其他组件的模板里这样用它:
html
<app-todo></app-todo>
把 Todo 组件显示出来
要让 <app-todo> 在页面上显示,需要在 AppComponent 里导入它。修改 app.ts:
typescript
import { Component } from '@angular/core';
import { TodoComponent } from './todo/todo'; // ← 导入
@Component({
selector: 'app-root',
standalone: true,
imports: [TodoComponent], // ← 加到 imports 数组
templateUrl: './app.html',
styleUrls: ['./app.css']
})
export class AppComponent { }
然后 app.html 删掉默认内容,换成:
html
<app-todo></app-todo>
这一步在 Vue 里是 import Todo from './Todo.vue' 然后 <Todo />;在 React 里是 import Todo from './Todo' 然后 <Todo />。Angular standalone 组件的用法已经跟他们基本一致了。 区别只是你需要把导入的组件列到 imports: [] 数组里------多了一步声明,但好处是模板里用了哪些东西一目了然。
写点数据
现在给 Todo 组件加点内容。修改 todo.ts:
typescript
export class TodoComponent {
title = '我的待办清单';
todos = [
{ id: 1, text: '学习 Angular 基础', done: false },
{ id: 2, text: '写一个 Todo 应用', done: true },
{ id: 3, text: '对比 React 和 Vue 的差异', done: false }
];
}
然后修改 todo.html:
html
<h2>{{ title }}</h2>
<ul>
<li *ngFor="let item of todos">
{{ item.text }} - {{ item.done ? '已完成' : '未完成' }}
</li>
</ul>
*ngFor 是 Angular 的循环指令,意思是"遍历 todos 数组,为每个元素生成一个 <li>"。我会在后面专门讲指令,这里先有个印象就好。
对比一下:
- React 里你会写
todos.map(item => <li key={item.id}>...)`- Vue 里你会写
<li v-for="item in todos" :key="item.id">...- Angular 用
*ngFor="let item of todos",看起来介于两者之间
保存,浏览器自动刷新(因为 ng serve 支持热更新),你就能看到 Todo 列表了。
组件嵌套的秘密
你现在看到的页面结构是这样的:
AppComponent
└── TodoComponent
AppComponent 是根组件,TodoComponent 是它的子组件。这种树形结构可以一直往下嵌套:
AppComponent
├── HeaderComponent
├── TodoComponent
│ └── TodoItemComponent
└── FooterComponent
Angular 应用本质上就是一棵组件树 。根组件(你看到的第一个 <app-root>)是树的根节点,所有其他组件都是它的子节点或孙节点。
关于组件之间怎么"说话"(传数据、通知事件),我们后面会用 @Input 和 @Output 来搞定。现在先消化一下:
组件 = 一段 HTML 模板 + 一个 TypeScript 类 + 一组样式。就这么简单。
5. 本章总结
到目前为止,你现在应该已经:
- 理解了 Angular 的"全家桶"定位,以及它和 React、Vue 的核心区别
- 装好了 Angular CLI,跑起来了一个新项目
- 看懂了项目里几个关键文件是干嘛的
- 创建了第一个组件,并且显示出了 Todo 列表
下一章,我们来深入 Angular 最核心的部分------模板和数据绑定,把"写死"的 Todo 变成一个可交互的列表。