Angular
常用命令
- 创建组件 ng g c < name > 全称 ng generate component < name >
- 创建指令 ng g d < name > 全称 ng generate directive < name >
代理到后端服务
目的:开发过程中的---服务代理(webpack)解决跨域等问题
编辑代理配置文件可参阅 webpack DevServer 文档
一般代理
- 在项目的 src/目录下创建一个 proxy.conf.json 文件
- 往这个新的代理配置文件中添加如下内容:
{
"/api": {
"target": "http://npmjs.org",
"secure": false,
"pathRewrite": { // 重写URL
"^/api": ""
},
"changeOrigin": true, // 允许跨域
"logLevel": "debug" // info(默认值)、debug、warn、error 和 silent。
}
}
- 在 CLI 配置文件 angular.json 中为 serve 目标添加 proxyConfig 选项:
...
"architect": {
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "your-application-name:build",
"proxyConfig": "src/proxy.conf.json"
},
...
- 要使用这个代理选项启动开发服务器, 请运行 ng serve (npm start)命令
代理多个条目
目的:把多个条目代理到同一个目标
- 将代理配置文件设置为 proxy.conf.js(代替 proxy.conf.json), 并指定如下例子中的配置文件
module.exports = const PROXY_CONFIG = [
{
context: [
"/my",
"/many",
"/endpoints",
"/i",
"/need",
"/to",
"/proxy"
],
target: "http://localhost:3000",
secure: false
}
]
- 在angular.json中,指向javascript配置文件
...
"architect": {
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "your-application-name:build",
"proxyConfig": "src/proxy.conf.js"
},
...
绕过代理
目的: 在请求钱动态修改 请求信息等,可以添加 bypass 字段
const PROXY_CONFIG = {
"/api/proxy": {
"target": "http://localhost:3000",
"secure": false,
"bypass": function (req, res, proxyOptions) {
if (req.headers.accept.indexOf("html") !== -1) {
console.log("Skipping proxy for browser request.");
return "/index.html";
}
req.headers["X-Custom-Header"] = "yes";
}
}
}
module.exports = PROXY_CONFIG;
使用公司代理
目的: 在使用了代理之后,无法访问局域网外的然和URL(CDN加载资源)时,需要将该服务代理公司的代理
- npm install --save-dev https-proxy-agent
- 定义环境变量 http_proxy 或HTTP_PROXY, 当运行 npm start 时, 就会自动添加一个agent来通过你的企业代理转发网络调用
var HttpsProxyAgent = require('https-proxy-agent');
var proxyConfig = [{
context: '/api',
target: 'http://your-remote-server.com:3000',
secure: false
}];
function setupForCorporateProxy(proxyConfig) {
var proxyServer = process.env.http_proxy || process.env.HTTP_PROXY;
if (proxyServer) {
var agent = new HttpsProxyAgent(proxyServer);
console.log('Using corporate proxy server: ' + proxyServer);
proxyConfig.forEach(function(entry) {
entry.agent = agent;
});
}
return proxyConfig;
}
module.exports = setupForCorporateProxy(proxyConfig);
路由
如何使Angular使用路由
- 将 AppRoutingModule 导入到AppModule
- 将 AppRoutingModule 添加到 imports 中 (在Angular 注册router)
- 在路由模块文件中(src/router/app-routing.module.ts)如下配置
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; // CLI imports router
import { FirstComponent } from './firstPage/firstPage.component';
const routes: Routes = [
{
path: 'first-component',
component: FirstComponent,
/**
嵌套路由 <router-outlet> 参考 vue <router-view /> 理解
*/
children: [
{
path: 'child-a', // 子路由路径
component: ChildAComponent, // 子路由页面组件
}
],
},
// 路由重定向
{
path: '',
redirectTo: '/first-component', // 重定向页面
pathMatch: 'full' // 重定向规则
},
{ path: '**', component: "404页面组件" } // 通配符路由 404 页面
]; // 路由表
// 将路由交给Angular 托管
@NgModule({
imports: [RouterModule.forRoot(routes), { useHash: true }], // // { useHash: true } 默认为history模式, 参数为 true 启用 hash 模式,
exports: [RouterModule]
})
export class AppRoutingModule { }
- < router-outlet > (参考vue中 < router-view > 理解)
- routerLink 标签路由跳转
<a routerLink="/first-component" routerLinkActive="active">First Component</a>
路由顺序规则
规则: 先到先得(代码从上倒下执行)
- 静态路径的路由
- 默认路由匹配的空路径路由
- 通配符路由
路由传参(ActivatedRoute)
- 在业务组件中引入如下代码
this.router.navigateByUrl('/first-component?id=John');
...
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
export class FirstComententComponent implements OnInit {
constructor(
private route: ActivatedRoute, // 获取路由传参
private router: Router // 路由实例
) { }
public title: any
ngOnInit(): void {
this.route.queryParamMap.subscribe(params => {
this.title = JSON.stringify(params)
})
}
}
...
相对路由
解释:相对路径允许你定义相对于当前 URL 段的路径。下面的例子展示了到另一个组件 second-component 的相对路由。FirstComponent 和 SecondComponent 在树中处于同一级别,但是,指向 SecondComponent 的链接位于 FirstComponent 中,这意味着路由器必须先上升一个级别,然后进入二级目录才能找到 SecondComponent。你可以使用 ../ 符号来上升一个级别,而不用写出到 SecondComponent 的完整路径。
<a routerLink="../second-component">Relative Route to second component</a>
除了 ../,还可以使用 ./ 或者不带前导斜杠来指定当前级别。
指定相对路由 --- 暂不考虑
路由守卫 (学习完Angular基础回头在学习)
配合服务理解
- CanActivate 控制是否允许进入路由
- CanActivateChild 控制是否允许进入所有子路由
- CanDeactivate 控制是否允许离开路由
- Resolve 路由激活之前,获取路由数据
- CanLoad 控制是否允许延迟加载整个模块
组件
使用CLI创建组件命令:ng g c YourComponentName
组件不管在何处使用,一般情况下,标签名都采用组件定义的标签名
每个组件都包括如下部分:
- 一个HTML模板,用于声明页面要渲染的内容
- 一个用于定义行为的Typescript类
- 一个CSS选择器,用于定于组件在模板中的使用方式
- 要应用在模板上的css样式
声明周期
生命周期开始到结束
- 实例化组件并渲染视图及其子视图时,生命周期就开始了
- 数据变化,也会触发生命周期,Angular会按需更新视图和组件实例
- 销毁组件实例并从DOM中移除了组件渲染模板时,生命周期结束
如何使用Anggular生命周期钩子
-
实现(implements)Angular core库中定义的生命周期钩子接口来一行营组件或指令生命周期中的事件
-
灭个接口都有唯一的钩子方法,名字是由 ng+接口名构成 如
let nextId = 1; // Spy on any element to which it is applied. // Usage: <div appSpy>...</div> @Directive({selector: '[appSpy]'}) export class SpyDirective implements OnInit, OnDestroy { private id = nextId++; constructor(private logger: LoggerService) { } ngOnInit() { this.logger.log(`Spy #${this.id} onInit`); } ngOnDestroy() { this.logger.log(`Spy #${this.id} onDestroy`); } }
声明周期钩子执行顺序
- ngOnChanges()
- ngOnInit()
- ngDoCheck()
- ngAfterContentInit()
- ngAfterContentChecked()
- ngAfterViewInit()
- ngAfterViewChecked()
- ngOnDestroy()
组件间传值
-
父传子
parentComponent: template: `
<child-component [hero]="hero">
`
childComponent: @Input() hero!: Hero
childComponent: @Input('master') masterName = '' // 给传进来的参数取别名
-
通过setter截取输入型属性发生变化
export class NameChildComponent {
@Input()
get name(): string { return this._name; }
set name(name: string) {
this._name = (name && name.trim()) || '<no name set>';
}
private _name = '';
}
也可通过 ngOnChanges() 声明周期监听输入型数据变化
-
子传父(父组件监听子组件的事件)
// 子组件
export class childComponent {
@Input() name = '';
@Output() childEmit = new EventEmitter<boolean>();
didVote = false;
vote(agreed: boolean) {
this.child.emit(agreed);
this.didVote = true;
}
}
// 父组件
@Component({
selector: 'parent',
template: `
<app-voter *ngFor="let voter of voters"
[name]="voter"
(childEmit)="onVoted($event)">
</app-voter>
`
})
export class Component {
agreed = 0;
disagreed = 0;
voters = ['Narco', 'Celeritas', 'Bombasto'];
onVoted(agreed: boolean) {
agreed ? this.agreed++ : this.disagreed++;
}
}
-
父组件与子组件通过 本地变量 互动
- 本地变量 只能在父组件模板中使用,不能在父组件类中使用,@ViewChild()解决此问题
- 父组件不能通过数据绑定使用子组件的方法
- 父组件在子组件标签上使用 #变量名 形式获取
// 子组件
@Component({
selector: 'app-countdown-timer',
template: '<p>{{message}}</p>'
})
// 父组件(注意timer)
<div class="seconds">{{timer.seconds}}</div>
<app-countdown-timer #timer></app-countdown-timer>
-
父组件中调用 @ViewChild()
目的: 在父组件类中调用子组件方法时,需要使用这种方式
- 当父组件的类需要这种访问时,可以把子组件作为ViewChild,注入到父组件里面
import { AfterViewInit, ViewChild } from '@angular/core';
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';
export class CountdownViewChildParentComponent implements AfterViewInit {
// 在父组件中注册
@ViewChild(CountdownTimerComponent)
private timerComponent!: CountdownTimerComponent;
seconds() { return 0; }
// 当前组件视图更新,调用此生命周期钩子函数
ngAfterViewInit() {
// 单项数据流会组织在同一周期内更新父组件视图
setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
}
start() { this.timerComponent.start(); }
stop() { this.timerComponent.stop(); }
}
-
父组件和子组件通过服务来通讯
思路:父组件和子组件共享一个服务, 利用该服务在组件家族内部实现双向通讯
-
该服务实例的作用域被限制在父组件和其子组件内,这个组件属之外的组件无法访问该服务或者他们们的通讯
- 新建一个服务
- 将该服务在父组件引入并使用
- 在父组件使用providers关键字将该服务注入进子组件中
- 在子组件构造函数中页注入该服务
- 由于 子组件 隶属于 父组件, 所以获取到也是父组件的服务实例
-
-
插槽(内容投影)
-
默认插槽(单插槽内容投影)
- 创建一个组件
- 在组件模板中,添加 ng-content 元素,映射外部插槽内容
import { Component } from '@angular/core'; @Component({ selector: 'app-zippy-basic', template: ` <h2>Single-slot content projection</h2> <ng-content></ng-content> ` }) export class ZippyBasicComponent {}
-
具名插槽(多插槽内容投影)
- 创建一个组件
- 在组件模板中,添加 ng-content 元素,映射外部插槽内容
- 将 select 属性添加到 ng-content 元素。Angular 使用的选择器 支持标签名、属性、CSS类和:not伪类的任意组合
// 插槽组件 import { Component } from '@angular/core'; @Component({ selector: 'app-zippy-multislot', template: ` <h2>Multi-slot content projection</h2> <ng-content></ng-content> <ng-content select="[question]"></ng-content> ` }) export class ZippyMultislotComponent {}
-
// 使用插槽组件
<app-zippy-multislot>
<p question>
Is content projection cool?
</p>
<p>Let's learn about content projection!</p>
</app-zippy-multislot>
```
- 条件插槽内容投影
__此处的替换不是单纯的直接替换,使用方式另有变化__
<font color=red>注意</font>: 使用 ng-template 元素代替 ng-content 元素
<font color=red>原因</font>: ng-content 无论何时,只要有组件映射进来都会被直接初始化, 而映射在ng-template 标签上的元素,只有显示的渲染出来, Angular才会初始化该元素。
- 创建一个组件
- 在接受ng-template 元素的组件中,使用ng-container元素渲染该模板
- 将ng-container元素包装在另一个元素中,然后应用条件逻辑
- 在要投影内容的模板中,将投影的内容包装在 ng-template 元素中
- 创建一个带有与这个模板的自定义属性相匹配的选择器指令,注入 TemplateRef 实例
- 在你要将内容投影到的组件中,使用 @ContentChild 获取此投影内容的模板
- 动态组件
模板
模板渲染变量选择遵循 上下文就近原则
-
插值
// 模板输入变量 customer <ul> <li *ngFor="let customer of customers">{{customer.name}}</li> </ul> // 模板引用变量 #customerInput <label>Type something: <input #customerInput>{{customerInput.value}} </label>
-
模板语句
-
管道(参考vue中 过滤器理解)
// 语法 {{ 变量名 | 函数 | 函数 ... }} <p>The hero's birthday is {{ birthday | date }}</p>
-
内置管道
- DatePipe 根绝本地环境中的规则格式化日期值
- UpperCasePipe 将文本转换成大写
- LowerCasePipe 将文本转换成小写
- CurrencyPipe 把数字转换成货币字符串,根据本地环境中的规则进行格式化
- DecimalPipe 把数字转换成带小数点的字符串,根绝本地环境中的规则进行格式化
- PercentPipe 把数字转换成百分比字符串,根据本地环境中的规则进行格式化
-
管道参数
// 管道能接受多个参数,就用冒号分隔这些值 // 语法 {{ 变量名 | 函数: 第二个参数:第三个参数:第四个参数... }}
-
自定义管道(检测符合对象中的非纯变更)
虽然非纯管道很实用,但要小心使用。长时间运行非纯管道可能会大大降低你的应用速度。
// 应用 <div *ngFor="let hero of (heroes | flyingHeroesImpure)"> {{hero.name}} </div> // 定义 import { Pipe, PipeTransform } from '@angular/core'; import { Hero } from './heroes'; @Pipe({ name: 'flyingHeroes', pure: false // 将该值设置为 false 则为非纯变更 }) export class FlyingHeroesPipe implements PipeTransform { transform(allHeroes: Hero[]) { return allHeroes.filter(hero => hero.canFly); } }
-
-
绑定语法
-
属性绑定 [] 语法 类似vue中 v-bind
- 左侧有 [] 则 右侧会计算出 绑定的 变量的值
- 左侧没有 [],则右侧会被Angular 视为 字符串
<img [src]="itemImageUrl">
-
属性(Attribute)绑定、类(class)绑定和样式(style)绑定
-
Attribute 绑定
不能直接绑定,需要挂载在attr 属性下 [attr.your-attr-name]-target
// 语法 <p [attr.attribute-you-are-targeting]="expression"></p>
-
class 绑定
- 绑定单个class
- 绑定多个class
[class.sale]="onSale" // onSale 为 真值时添加类,反之 移除 [class]="classExpression" type classExpression = 'string' || ('object' || 'array')
-
style 绑定
- 绑定单个 style
- 绑定多个 style
[style.background-color]="expression" [style.backgroundColor]="expression" [style]="styleExpression" type styleExpression = 'string' | 'object'
-
样式优先级 (遵从--类或样式绑定越具体,其优先级越高)
- null 将值设置为null 则会完全删除样式属性
- undefined 将值设置为undefined 样式渲染会选用低级别的样式
-
-
事件绑定
<button (click)="delete()">Save</button> @Output() deleteRequest = new EventEmitter<Item>(); delete() { this.deleteRequest.emit(this.item); this.displayNone = this.displayNone ? '' : 'none'; this.lineThrough = this.lineThrough ? '' : 'line-through'; } <app-item-detail (deleteRequest)="deleteItem($event)" [item]="currentItem"></app-item-detail>
-
双向绑定
- 语法是方括号和圆括号的组合 [()]
- @Output() 属性的名字必须遵循 inputChange 模式,中 input 是相应 @Input() 属性的名字
- 例如,@Input() 属性为 size ,则 @Output() 属性必须为 sizeChange 。
<app-sizer [(size)]="fontSizePx"></app-sizer> export class SizerComponent { @Input() size!: number | string; @Output() sizeChange = new EventEmitter<number>(); dec() { this.resize(-1); } inc() { this.resize(+1); } resize(delta: number) { this.size = Math.min(40, Math.max(8, +this.size + delta)); this.sizeChange.emit(this.size); } } <div> <button (click)="dec()" title="smaller">-</button> <button (click)="inc()" title="bigger">+</button> <label [style.font-size.px]="size">FontSize: {{size}}px</label> </div>
-
模板引用变量
- 如果在组件上声明变量,该变量就会引用该组件实例
- 如果在标准的 HTML 标记上声明变量,该变量就会引用该元素
- 如果你在 < ng-template > 元素上声明变量,该变量就会引用一个 TemplateRef 实例来代表此模板
- 如果该变量在右侧指定了一个名字,比如 #var="ngModel" ,那么该变量就会引用所在元素上具有这个 exportAs 名字的指令或组件
-
输入和输出
-
模板表达式运算符
-
模板中的SVG
指令
- 组件--- 带有模板的指令
- 属性型指令 ---更改元素、组件或其他指令的外观或行为的指令
- 结构型指令 --- 通过添加和删除DOM元素来更改DOM布局的指令
内置指令
-
NgClass —— 添加和删除一组 CSS 类。
<div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>
currentClasses: Record<string, boolean> = {};
/* . . . */
setCurrentClasses() {
// CSS classes: added/removed per current state of component properties
this.currentClasses = {
saveable: this.canSave,
modified: !this.isUnchanged,
special: this.isSpecial
};
}
<div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special.</div>
```
-
NgStyle —— 添加和删除一组 HTML 样式。
currentStyles: Record<string, string> = {}; /* . . . */ setCurrentStyles() { // CSS styles: set per current state of component properties this.currentStyles = { 'font-style': this.canSave ? 'italic' : 'normal', 'font-weight': !this.isUnchanged ? 'bold' : 'normal', 'font-size': this.isSpecial ? '24px' : '12px' }; } <div [ngStyle]="currentStyles"> This div is initially italic, normal weight, and extra large (24px). </div>
-
NgModel —— 将数据双向绑定添加到 HTML 表单元素。
- 导入FormsModule, 并将其添加到NgModule的imports列表中
import { FormsModule } from '@angular/forms'; // <--- JavaScript import from Angular @NgModule({ imports: [ BrowserModule, FormsModule // <--- import into the NgModule ], /* . . . */ }) export class AppModule { }
// 用法 一
<input [(ngModel)]="currentItem.name" id="example-ngModel">
// 用法 二
<input [ngModel]="currentItem.name" (ngModelChange)="setUppercaseName($event)" id="example-uppercase">
```
- NgIf —— 从模板中创建或销毁子视图。
- NgFor —— 为列表中的每个条目重复渲染一个节点。
- 在for 上下文中或存在一个index索引,指的是当前条目的索引,每次更新都会重新生成
- NgSwitch —— 一组在备用视图之间切换的指令。
- NgSwitchCase 当其绑定值等于开关值时将其元素添加到 DOM 中,而在其不等于开关值时将其绑定值移除。
- NgSwitchDefault 当没有选中的 NgSwitchCase 时,将其宿主元素添加到 DOM 中。
属性型指令 (如 NgClass等)
-
cli 使用命令创建指令 myDirective
-
从@angular/core 导入 ElementRef
- ElementRef 的 nativeElement 属性 会提供对宿主DOM元素的直接访问权限
-
在指令的 constructor() 中添加 ElementRef 以注入对宿主 DOM 元素的引用
-
向 myDirective类中添加逻辑
-
使用,如下
<p ng-MyFirstDirective>{{ title }}</p>
处理用户事件
- 从 '@angular/core' 导入 HostListener
- 添加两个事件处理程序,他们会在鼠标进入或离开时做出响应,每个事件处理程序都带有@HostListener 注解
// 用法
<p ng-MyFirstDirective [appHighlight]="color">{{ title }}</p>
// 指令
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[ng-MyFirstDirective]'
})
export class MyFirstDirectiveDirective {
constructor(
private el: ElementRef
) {}
@Input() appHighlight = '' // 传入的参数
@HostListener('mouseenter')
onMouseEnter():void {
this.setBackground(this.appHighlight)
}
@HostListener('mouseleave')
onMouseLeave() {
this.setBackground('')
}
setBackground(bac:string):void {
this.el.nativeElement.style.backgroundColor=bac
this.el.nativeElement.style.transition= 'all 1s'
}
}
通过NgNonBindable 停用 Angular 处理过程
- 该指令会停用模板中的插值、指令和绑定
- 自定义指令不受影响
结构型指令(如 NgIf 等)
创建结构型指令
-
使用 ng g d unless 创建指令 (unless是伪指令名称)
-
导入 Input、 TemplateRef、ViewContainerRef
- TemplateRef 获取 < ng-template > 的内容
- ViewContainerRef 访问视图容器
-
在指令的构造函数中将 TemplateRef 和 ViewContainerRef 注入成私有变量
-
添加一个带 setter 的 @Input() 属性 appUnless
服务
解释:广义概念--它包括应用所需的任何值、函数、或特性。 侠义概念 --- 服务是一个明确定义了用途的类,他应该做一些具体的事,并做好
理想情况下:
- 组件的工作只管用户体验,而不顾及其他
- 组件应该把诸如服务器获取数据、验证用户输入或直接王控制台中写日志等工作委托给各种服务
- 把处理任务定义到可注入的服务中,则该服务可以被任何组件所使用
服务范类
@Injectable()
export class Logger {
log(msg: any) { console.log(msg); }
error(msg: any) { console.error(msg); }
warn(msg: any) { console.warn(msg); }
}
依赖注入(DI) dependency injection
- 要创建一个服务,就要用 @Injectable() 装饰器来提供元数据,以变让Angular可以把它作为依赖注入到组件中
- 注入器是主要的机制,Angular在启动时,会自己维护一套。
- 注入器会创建依赖、维护一个容器来管理这些依赖,并复用(单例模式)
- 提供者是一个对象,用来告诉注入器应该如何获取或创建依赖
提供服务
- 默认情况下,cli 的 ng g s serviveName 命令会在 @Injectable() 装饰器中提供元数据来吧它注册到根注入器中
@Injectable({
providedIn: 'root',
})
- 当你使用特定的NgModule注册提供者时,该服务的同一个实例将会对该NgModule中的所有组件可用。要想在这一层注册,请用@NgModule() 装饰器中的 providers 属性
@NgModule({
providers: [
BackendService,
Logger
],
...
})
- 当你在组件级注册提供者时,你会为该组件的每一个新实例提供该服务的一个新实例。要在组件及注册,就要在@Component() 元数据的 providers 属性中注册服务提供者。
@Component({
selector: 'app-hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
模块(NgModules)
- 声明某些文件、指令和管道属于这个模块
- 公开其中的部分组件、指令和管道,以便其他模块中的组件模板中可以使用他们
- 导入其他带有组件、指令和管道的模块,这些模块中的原件都是本模块所需的
- 提供一些供应用中的其他组件使用的服务
每个Angular应用都至少有一个模块(根模块),引导此模块启动应用 对于那些只有少量组件的简单应用,跟模块就是你所需的一切。随着应用的成长,需要把这个模块重构成一个特性模块,它们代表一组密切相关的功能集。然后再把这些模块导入到根模块中。
NgModule 刨析
- declarations 属于该 NgModule 的组件、指令和管道。新应用项目的根模块中只有一个叫做 AppComponent 的组件。
- imports 你要用的其他 NgModule,这样你才可以使用它们的可声明对象。新生成的根模块会导入BrowserModule ,以便使用特定于浏览器的服务,比如 DOM 渲染、无害化处理和定位。
- providers 一些服务提供者,可供其他 NgModule 中的组件使用。新生成的根模块中没有提供者。
- bootstrap Angular 创建的入口组件,Angular 会创建它,并把它插入到宿主页面 index.html 中,从而引导该应用。这个入口组件 AppComponent 会同时出现在 declarations 和 bootstrap 数组中。
RxJs(响应式扩展的javascript版)
响应式编程是一种面向数据流和变更传播的一部编程范式。是一个使用可观察对象进行响应式编程的库,他让组合异步代码和基于毁掉的代码变得简单。
- 把现有的异步代码转换成可观察对象
- 迭代流中的各个值
- 把这些值映射成其他类型
- 对流进行过滤
- 组合多个流
Rxjs 提供了一些用来创建可观察对象的函数。这些函数可以简化根据某些东西创建可观察对象的过程,比如事件、定时器、承诺等等
理解: rxjs --> 可观察对象,提供了一套处理方式(pipe---管道),最后在通过subscribe调用