本文目錄
- 一、項(xiàng)目起步
- 二千埃、編寫路由組件
- 三、編寫頁面組件
- 1.編寫單一組件
- 2.模擬數(shù)據(jù)
- 3.編寫主從組件
- 四忆植、編寫服務(wù)
- 1.為什么需要服務(wù)
- 2.編寫服務(wù)
- 五放可、引入RxJS
- 1.關(guān)于RxJS
- 2.引入RxJS
- 3.改造數(shù)據(jù)獲取方式
- 六、改造組件
- 1.添加歷史記錄組件
- 2.添加和刪除歷史記錄
- 七朝刊、HTTP改造
- 1.引入HTTP
- 2.通過HTTP請(qǐng)求數(shù)據(jù)
- 3.通過HTTP修改數(shù)據(jù)
- 4.通過HTTP增加數(shù)據(jù)
- 5.通過HTTP刪除數(shù)據(jù)
- 6.通過HTTP查找數(shù)據(jù)
本項(xiàng)目源碼放在github
四耀里、編寫服務(wù)
截止到這部分,我們的BooksComponent
組件獲取和顯示的都是本地模擬的數(shù)據(jù)拾氓。
接下來我們要開始對(duì)這些進(jìn)行重構(gòu)冯挎,讓聚焦于為它的視圖提供支持,這也讓它更容易使用模擬服務(wù)進(jìn)行單元測(cè)試咙鞍。
1.為什么需要服務(wù)
我們不應(yīng)該讓組件來直接獲取或保存數(shù)據(jù)房官,它們應(yīng)該聚焦于展示數(shù)據(jù),而數(shù)據(jù)訪問的工作交給其他服務(wù)來做奶陈。
這里我們需要?jiǎng)?chuàng)建一個(gè)名為BooksService
的服務(wù)易阳,讓我們應(yīng)用中所有的類都使用它來獲取書本列表的數(shù)據(jù),使用的時(shí)候吃粒,只需要將它通過Angular的依賴注入機(jī)制注入到需要用的組件的構(gòu)造函數(shù)中潦俺。
知識(shí)點(diǎn):
服務(wù)可以實(shí)現(xiàn)多個(gè)不同組件之間信息共享,后面我們還會(huì)將它注入到兩個(gè)地方:
BooksService
中徐勃,使用該服務(wù)發(fā)送消息事示。
IndexService
中,使用該服務(wù)來展示消息僻肖。
接下來我們使用命令行肖爵,創(chuàng)建BooksService
:
ng g service books
在生成的books.service.ts
文件中:
// books.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
新導(dǎo)入了@Injectable
裝飾器,是為了讓BooksService
提供一個(gè)可注入的服務(wù)臀脏,并且它還可以擁有自己的待注入的依賴劝堪,簡單理解就是如果你的服務(wù)需要依賴冀自,那么你就需要導(dǎo)入它。
并且它接收該服務(wù)的元數(shù)據(jù)對(duì)象秒啦。
2.編寫服務(wù)
接下來我們開始編寫books.service.ts
服務(wù)熬粗。
- 導(dǎo)入服務(wù)所需組件
這里我們導(dǎo)入Books
和BookList
,并添加一個(gè)getBooks
方法來返回所有書本的數(shù)據(jù)余境,并且還需要添加一個(gè)getBooks
方法來返回指定id的書本信息:
// index.component.ts
import { Books } from './books';
import { BookList } from './mock-books';
@Injectable({
providedIn: 'root'
})
export class BooksService {
constructor() { }
getBookList(): Books[] {
return BookList;
}
getBook(id: number): Books{
return BookList.find(book => book.id === id)
}
}
在我們使用這個(gè)服務(wù)之前驻呐,需要先注冊(cè)該服務(wù),因?yàn)槲覀冊(cè)谑褂?code>ng g service books命令創(chuàng)建服務(wù)時(shí)芳来,CLI已經(jīng)默認(rèn)為我們添加了注冊(cè)了含末,這是方法就是上面代碼中的:
providedIn: 'root'
表示將我們的服務(wù)注冊(cè)在根注入器上,這樣我們就可以把這個(gè)服務(wù)注入到任何享用的類上了即舌。
- 修改
IndexComponent
先刪除BookList
的引入佣盒,并修改books
屬性的定義:
// index.component.ts
import { BooksService } from '../books.service';
export class IndexComponent implements OnInit {
books : Books[];
ngOnInit() {}
}
然后注入我們的BooksService
服務(wù),需要先往構(gòu)造函數(shù)中添加一個(gè)私有的booksservice
顽聂,使用注入的BooksService
作為類型沼撕,理解成一個(gè)注入點(diǎn):
// index.component.ts
constructor(private booksservice: BooksService) { }
之后我們需要添加一個(gè)getBooks
方法來獲取這些書本數(shù)據(jù),并在生命周期函數(shù)ngOnInit
中調(diào)用:
export class IndexComponent implements OnInit {
ngOnInit() {
this.getBooks();
}
getBooks(): void{
this.books = this.booksservice.getBookList();
}
}
- 修改
DetailComponent
我們先改造書本詳情頁的HTML結(jié)構(gòu):
<!-- detail.component.html -->
<div *ngIf="books" class="detail">
<h3>《{{books.title}}》介紹</h3>
<div>
<img src="{{books.url}}">
</div>
<p>書本標(biāo)題: {{books.title}}</p>
<p>書本作者: {{books.author}}</p>
<p>書本id: {{books.id}}</p>
</div>
<div *ngIf="!books" class="detail">
<h3>暫無信息</h3>
</div>
知識(shí)點(diǎn):
這里使用了*ngIf
指令芜飘,當(dāng)條件為true
則顯示其HTML內(nèi)容。
// detail.component.ts
import { Books } from '../books';
import { BooksService } from '../books.service';
export class DetailComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private location: Location,
private booksservice: BooksService // 引入BooksService服務(wù)
) { }
books: Books; // 定義books類型
ngOnInit() {
this.getDetail()
}
getDetail(): void{
const id = +this.route.snapshot.paramMap.get('id');
this.getBooks(id);
}
getBooks(id: number): void {
this.books = this.booksservice.getBook(id);
}
}
這段代碼磨总,主要定義了getBooks
方法嗦明,當(dāng)剛進(jìn)入頁面時(shí),將書本id
傳入getBooks
方法蚪燕,去BooksService
去獲取對(duì)應(yīng)id的書本信息娶牌,并復(fù)制給變量books
,然后展示到頁面馆纳。
改造之后诗良,我們的頁面顯示依舊正常。
但是我們要知道鲁驶,這背后的邏輯已經(jīng)改變了鉴裹。
五、引入RxJS改造項(xiàng)目
1.關(guān)于RxJS
這里簡單介紹關(guān)鍵概念钥弯,具體可以查看 RxJS 官網(wǎng)径荔,也可以參考 淺析Angular之RxJS。
什么是RxJS
RxJS全稱Reactive Extensions for JavaScript
脆霎,中文意思: JavaScript的響應(yīng)式擴(kuò)展总处。
RxJS主要是提供一種更加強(qiáng)大和優(yōu)雅的方式,來利用響應(yīng)式編程的模式睛蛛,實(shí)現(xiàn)JavaScript的異步編程鹦马。
RxJS優(yōu)點(diǎn)
- 純凈性胧谈;
- 流動(dòng)性;
RxJS核心概念
RxJS 是基于觀察者模式和迭代器模式以函數(shù)式編程思維來實(shí)現(xiàn)的荸频。RxJS 中含有兩個(gè)基本概念:Observables
與 Observer
菱肖。
Observables
作為被觀察者,是一個(gè)值或事件的流集合试溯;而 Observer
則作為觀察者蔑滓,根據(jù) Observables
進(jìn)行處理。它們之間的訂閱發(fā)布關(guān)系(觀察者模式) 如下:
訂閱:Observer
通過 Observable
提供的 subscribe()
方法訂閱 Observable
遇绞。
發(fā)布:Observable
通過回調(diào) next
方法向 Observer
發(fā)布事件键袱。
———— 來源Angular修仙之路 RxJS Observable
另外這里列出來一些核心,具體還是看官網(wǎng)咯摹闽,并且下面使用到的時(shí)候會(huì)具體介紹蹄咖。
-
Observable
(可觀察對(duì)象): 表示一個(gè)概念,這個(gè)概念是一個(gè)可調(diào)用的未來值或事件的集合付鹿。 -
Observer
(觀察者): 一個(gè)回調(diào)函數(shù)的集合澜汤,它知道如何去監(jiān)聽由Observable
提供的值。 -
Subscription
(訂閱): 表示Observable
的執(zhí)行舵匾,主要用于取消Observable
的執(zhí)行俊抵。 -
Operators
(操作符): 采用函數(shù)式編程風(fēng)格的純函數(shù) (pure function
),使用像map
坐梯、filter
徽诲、concat
、flatMap
等這樣的操作符來處理集合吵血。 -
Subject
(主體): 相當(dāng)于EventEmitter
谎替,并且是將值或事件多路推送給多個(gè)Observer
的唯一方式。 -
Schedulers
(調(diào)度器): 用來控制并發(fā)并且是中央集權(quán)的調(diào)度員蹋辅,允許我們?cè)诎l(fā)生計(jì)算時(shí)進(jìn)行協(xié)調(diào)钱贯,例如setTimeout
或requestAnimationFrame
或其他。
2.引入RxJS
在我們的真實(shí)應(yīng)用中侦另,我們必須要等到服務(wù)器響應(yīng)后秩命,我們才能獲取到數(shù)據(jù),因此這天生就需要用異步思維來操作淋肾。
由于Angular中已經(jīng)自帶RxJS硫麻,所以我們只要在需要使用的時(shí)候,引入即可使用:
3.改造數(shù)據(jù)獲取方式
了解完RxJS的一些概念后樊卓,我們開始改造下這些書本的數(shù)據(jù)獲取方式拿愧。
- 改造
BooksService
首先我們從 RxJS 中導(dǎo)入 Observable
和 of
符號(hào):
// books.service.ts
import { Observable, of } from 'rxjs';
知識(shí)點(diǎn):
Observable
: 觀察者模式中的觀察者,具體可以參考 Angular修仙之路 RxJS Observable
of
: 用來獲取觀察者拿到的數(shù)據(jù)碌尔,通常是一個(gè)Observable
浇辜。
然后修改getBookList
方法
// books.service.ts
getBookList(): Observable<Books[]> {
return of(BookList);
}
這里 of(BookList)
返回一個(gè)Observable<Books[]>
券敌,它會(huì)發(fā)出單個(gè)值,這個(gè)值就是這些模擬書本的數(shù)組柳洋。
- 改造
IndexComponent
這里也要修改getBooks
方法待诅,使用subscribe
去訂閱服務(wù)返回回來的值:
// index.component.ts
getBooks(): void{
this.booksservice.getBookList()
.subscribe(books => this.books = books);
}
由于原本直接賦值數(shù)據(jù),在實(shí)際場(chǎng)景中是不可能這樣同步的熊镣,所以這里subscribe
函數(shù)卑雁,會(huì)在Observable
發(fā)出數(shù)據(jù)以后,再把書本列表傳到里面的回調(diào)函數(shù)绪囱,再復(fù)制給books
屬性测蹲。
使用這種異步方式,當(dāng) BooksService
從遠(yuǎn)端服務(wù)器獲取英雄數(shù)據(jù)時(shí)鬼吵,不用擔(dān)心還沒拿到數(shù)據(jù)就執(zhí)行后面扣甲。
下一步,我們就要改造一下項(xiàng)目了齿椅。
本部分內(nèi)容到這結(jié)束
Author | 王平安 |
---|---|
pingan8787@qq.com | |
博 客 | www.pingan8787.com |
微 信 | pingan8787 |
每日文章推薦 | https://github.com/pingan8787/Leo_Reading/issues |
JS小冊(cè) | js.pingan8787.com |
微信公眾號(hào) | 前端自習(xí)課 |