文章目录
环境搭建
- 安装
Angular-cli
脚手架
npm install -g @angular/cli
- 创建项目
> ng new angulardemo
# 添加路由?
? Would you like to add Angular routing? (y/N) y
# 选择使用的 css 预编译器
? Which stylesheet format would you like to use? (Use arrow keys)
CSS
> SCSS [ https://sass-lang.com/documentation/syntax#scss ]
Sass [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]
Less [ http://lesscss.org ]
Stylus [ http://stylus-lang.com ]
- 运行
# 运行
ng serve
# 运行并打开
ng serve --open
VS Code
代码提示插件
Angular Snippets
项目结构
文件 | 含义 |
---|---|
main.ts |
入口文件 |
styles.scss |
全局样式 |
src/app |
组件 |
src/app/app.module |
默认根组件 |
main.ts
-> src/app/app.module
-> 渲染默认根组件
创建组件
获取提示信息,查看ng g
可以创建的东西
ng g
在app/component
下创建一个test
组件
ng g component component/test
在app.module.ts
中同时添加了新增的组件信息
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TestComponent } from './component/test/test.component';
@NgModule({
declarations: [ // 组件
AppComponent,
TestComponent
],
imports: [ // 模块
BrowserModule,
AppRoutingModule
],
providers: [], // 服务
bootstrap: [AppComponent] // 默认组件(根组件)
})
export class AppModule { }
test.component.ts
可以查看创建的test
组件的名字,添加绑定数据等
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-test', // 名字
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit {
public title = "hello test"; // 添加的公共 title 数据
constructor() { } // 构造函数
ngOnInit() { // 生命周期(初始化加载)函数
}
}
可以在test.component.html
引用绑定的数据
{{ title }}
可以在根组件视图引用该组件
<app-test></app-test>
创建服务
- 创建
在app/services
下创建一个test
服务
ng g service services/test
2. 在app.module.ts
注册,和组件不同,服务不会自动注册
import { TestService } from './services/test.service' // 引入
@NgModule({
declarations: [
AppComponent,
TestComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule
],
providers: [TestService], // 注册服务
bootstrap: [AppComponent]
})
export class AppModule { }
- 使用
/* ... */
import { TestService } from '../../services/test.service'; // 引入
export class TestComponent implements OnInit {
constructor(private test: TestService) { // 引到构造函数
test.say(); // 使用服务
console.log(test.createAuther);
}
/* ... */
}
- 服务对象内容
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class TestService {
// 自定义内容 ↓
// 仅当前类
private serviceName: string = "testService";
// 当前及其子类
protected serviceVersion: string = "v1.0.0";
// 公共
public createDate: number = 10;
// 默认(公共)
createAuther: string = "ZhangSan";
public say() {
console.log(this.serviceName + " " + this.serviceVersion);
}
// 自定义内容 ↑
constructor() { }
}
打包和编译
打包后无法找到资源
修改根目录,修改 /src/index.html
:
<!-- <base href="/"> -->
<base href="./">
打包方式及区别
先了解一下 JIT 和 AOT:
- JIT(Just In Time即时编译)是运行时优化,AOT(Ahead Of Time)是编译时优化。JIT 在运行时由机器自动侦测使用频率高的代码,将其编译为机器代码提高速度;AOT 是提前将所有代码编译为机器代码,机器无需翻译直接执行。
- JIT 特点是编译快,体积大,运行相对较慢;AOT 特点是编译慢,体积小,运行相对较快。
- JIT 适合调试使用,AOT 适合上线使用。
ng 通常有3种编译模式:
模式 | 特点 |
---|---|
ng build | 常规压缩,体积最大 |
ng build --aot | 预编译,体积较小 |
ng build --prod | 预编译,并进一步精简,体积最小 |
--prod
本身会执行 --aot
,所以无需使用 ng build --prod --aot
来打包
生命周期
函数名 | 备注 |
---|---|
constructor |
构造函数(非生命周期函数),可以进行简单的局部变量初始化 |
ngOnChanges |
(重新)设置数据绑定输入属性时触发,如props 发生改变 |
ngOnInit |
初始化,可进行数据请求 |
ngDoCheck |
数据发生改变时触发 |
ngAfterContentInit |
组件投影初始化 |
ngAfterContentChecked |
组件投影内容发生改变时触发 |
ngAfterViewInit |
视图渲染初始化,可进行DOM 操作 |
ngAfterViewChecked |
视图内容发生改变时触发 |
ngOnDestroy |
组件销毁前调用 |
例子1
init
仅在初始化时触发一次;
check
用来检查(监测)数据/视图的变化;
多函数同时触发时,按上表依上至下顺序执行
public info: string = "aaa";
// 设置间隔1s改变info的值一次
ngOnInit() {
setInterval(() => {
this.info = "bbb";
},1000)
}
// 每次数据发生改变时都会触发
ngDoCheck() {
console.log("ngDoCheck");
}
// 执行一次
ngAfterContentInit() {
console.log("ngafterViewInit")
}
// 每次组件投影内容发生改变时都会触发
ngAfterContentChecked() {
console.log("ngafterViewChecked")
}
// 执行一次
ngAfterViewInit() {
console.log("ngafterViewInit")
}
// 每次视图内容发生改变时都会触发
ngAfterViewChecked() {
console.log("ngafterViewChecked")
}
↓
ngDoCheck
ngafterViewInit
ngafterViewChecked
...1s...
ngDoCheck
ngafterViewChecked
...1s...
ngDoCheck
ngafterViewChecked
...
例子2 - ngOnChanges
注意change
仅在数值发生改变时触发,check
每次修改即使没改变值也触发
// 接收父元素的一个 prop
@Input() title: string;
// 每当父元素那边的 title 绑定数据发生改变时都触发
ngOnChanges() {
console.log(this.title);
}
// 注意: 子组件这边自己改变不会触发 ngOnChanges
public changeTitle(): void {
this.title = "bbb";
}
数据渲染
绑定
文本数据绑定:
<p>{{ title }}</p>
属性绑定:
<div [title]="msg">hover to show title</div>
HTML绑定:
<div [innerHTML]="h"></div>
Class,Style绑定:
<div [ngClass]="{'red': true, 'blue': false}" <!-- 后面跟布尔值 -->
[ngStyle]="{'border': '1px solid red'}"> <!-- 值什么的都随意,可以是变量或直接'' -->
hello
</div>
循环
<ul>
<li *ngFor = "let person of persons; let i = index">
{{ i }} : {{ person.name }} {{ person.age }}
</li>
</ul>
和
vue
一样,let i = index
<==>:key = "index"
管道符
预定义管道符
名称 | 示例 | 结果 |
---|---|---|
日期 | public today: Date = new Date(); |
2001-01-20 12:12:56 |
{{ today | date:'yyyy-MM-dd HH:mm:ss' }} |
||
大小写 | {{ "hEllO" | lowercase }} |
hello |
{{ "hEllO" | uppercase }} |
HELLO | |
数字保留 | {{ 123.5678 | number:'5.2-3' }} |
00,123.568 |
(最少整数位数.最少小数位数-最多小数位数) | ||
(不够最小整数位数补0,超过最多小数位数四舍五入,不够最少小数位数补0) | ||
json序列化 | {{ {name: 'tom', 'age': 20} | json }} |
{ "name": "tom", "age": 20 } |
字符串截取slice | {{ "abcdefg" | slice:2:5 }} |
cde |
管道链 | {{ "abcdefg" | slice:2:5 | uppercase }} |
CDE |
自定义管道符
暂时跳过
判断
<p *ngIf="flag">true</p>
switch
<ul [ngSwitch]="n">
<li *ngSwitchCase="1"> 1 </li>
<li *ngSwitchCase="2"> 2 </li>
<li *ngSwitchDefault> * </li>
</ul>
必须用类似
ul li
这种,然后改下样式,可能是为了好看?
事件
触发
方法直接写数据写的那里,调用用()
,和vue
的@
一样
<input type="button" (click)="say()" />
$event
<input type="button" (click)="say($event)" />
say(e) { }
双向数据绑定
- 引入,在
app.module.ts
引入FormsModule
/* ... */
import { FormsModule } from '@angular/forms'; // 引入的组件
@NgModule({
/* ... */
imports: [
BrowserModule,
AppRoutingModule,
FormsModule // 引入的组件
],
/* ... */
})
export class AppModule { }
- 使用
<input [(ngModel)]="n" />
{{ n }}
DOM操作
html
标记
<div #myBox>hello test</div>
- 引入,操作
// ViewChild, ElementRef 为新增对象
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
/* ... */
export class TestComponent implements OnInit {
// 'myBox' 为 html 标记名,mybox 为自定义变量名
@ViewChild('myBox') myBox:ElementRef;
/* ... */
ngOnInit(): void { } // 初始化生命周期函数
ngAfterViewInit(): void { // 视图渲染完毕生命周期函数
let obj = this.myBox.nativeElement;
// 使用方法同原生 DOM 对象
obj.style.border = "1px solid red";
obj.style.fontSize = "30px";
}
}
父子组件通信
父 <–方法-- 子
假设存在header
(子)和test
(父)两个组件,父组件要调用子组件的方法:
- 父组件引用子组件并命名
<app-header #headerApp></app-header>
- 调用
import { Component, OnInit, ViewChild } from '@angular/core';
/* ... */
export class TestComponent implements OnInit {
@ViewChild('headerApp',null) headerApp: any; // 这里用 any 类型可以防止调用方法时报错
/* ... */
ngAfterViewInit(): void {
this.headerApp.say(); // 调用子组件的方法
}
}
父 --方法–> 子
类似vue
的$emit
- 子组件
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
/* ... */
export class HeaderComponent implements OnInit {
// 子组件实例化`EventEmitter`
@Output() private say = new EventEmitter<string>();
// 调用
public fatherSay(): void {
this.say.emit(); // 无参
this.say.emit("this is a message from header"); // 有参(该方法仅能接收一个字符串参数)
}
/* ... */
}
- 父组件
<!-- 注意这里有参传过来的话要用 $event 来接收,即使只能传过来一个字符串
<app-header (say)="runSay($event)"></app-header>
// 用 string 接收
public runSay(msg: string): void {
if(msg){
console.log(msg);
return;
}
console.log("this is a message from test");
}
父 --数据–> 子
假设存在header
(子)和test
(父)两个组件
header
引用子组件,用[]
传值给子组件
<app-header [title]="message"></app-header>
test
接收数据
import { Component, OnInit, Input } from '@angular/core';
/* ... */
export class HeaderComponent implements OnInit {
@Input() title: string;
constructor() { }
ngOnInit() {
console.log(this.title); // 使用
}
}