「Leo,我一直有個問題想問你。」Wayne 神秘兮兮的說。
「什麼問題?」看他這麼神秘兮兮的我也緊張了起來。
「既然是 SPA ,就代表整個 Web 從頭到尾其實只有一頁對吧?」Wayne 認真地問道。
「對阿!怎麼了?」看他這麼認真,我也正經了起來。
「那如果我想要用某個特定的連結就能直接到某個頁面的話要怎麼辦?用參數嗎?」Wayne 煞有其事的問道。
「對,也不對。」切,還以為是什麼嚴肅的話題呢。
「這話是什麼意思?」Wayne 不解地說道。
「我的意思是...」
這個詞彙或許對只有開發過一般傳統網頁的朋友來說會比較陌生一點,因為這一般都是開發後端的朋友設定的。
什麼意思呢?就拿 iT 邦幫忙的的網站來舉例好了,當我們在瀏覽器的網址列輸入 ithelp.ithome.com.tw
並按下 Enter 之後,就會看到 iT 邦幫忙的網站。
然後如果我們要到其他地方,比如說「即時通知中心」。除了點擊「即時通知」的連結之外,我們其實可以直接在 iT邦幫忙的網址後面加上 /notifications
,像是這樣: ithelp.ithome.com.tw/notifications
。
如果我想要到「技術問答」的列表頁面,除了點擊「技術問答」的連結之外,也可以像上述一樣,直接在 iT 邦幫忙的網址後面加上 /questions
。
而當我們在做上述動作的時候,其實是在向伺服器發送請求,跟伺服器說:我想要取得與這個 /notifications
字串相對應的資源,然後伺服器覺得你可以取得相對應的資源的話,它就會吐回相對應的資源給你。
這時或許有人就會問:所以 /notifications
這個是路徑名稱嗎?
我覺得它會比較像是一個協定、約定。
舉例來說,如果我在伺服器設定當我收到要求取得 /wtf
這個字串的時候,我會回他 <h1>Welcome to Facebook</h1>
這串東西的話,這時候你在網址後面加上 /wtf
其實就會真的看到 <h1>Welcome to Facebook</h1>
。
只是說如果在 UI 上沒有把當我們要求取得 /wtf
這個字串的資源會收到的回覆顯示出來的話,一般人是不可能知道我們伺服器端上面有做這個設定的 (除非你會通靈!) 。
但現在是寫 SPA ,整個網站實際上只有一個頁面,這時就不適用上述的方式,改由前端來處理上述這類的事情。而Angular 身為一個強大的前端開發框架,路由的機制與設定當然也是早就準備的妥妥當當只等人家來學。
那要怎麼開始呢?
這還用說,趕快用 CLI 建立一個新專案來練習阿:
ng new RoutingPracticeForAngular30Days --routing
Angular CLI 非常貼心地在我們使用 new
來建立新專案時,提供了 --routing
這個參數讓我們使用。
當使用了這個參數建立完之後你會發現,在 app
資料夾底下多了一個名為 app-routing.module.ts
的檔案,裡面大概是長這個樣子:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
這個 AppRoutingModule ,就是我們之後設定路由的地方,容我後續再來說明。
接著我們打開 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';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Angular CLI 也很貼心地幫我們把 AppRoutingModule 引入到 AppModule 裡。
然後再打開 app.component.html
拉到最下面會發現多了一個之前沒看過的元素:
<router-outlet></router-outlet>
其實上述這些就是如果我們要在 Angular 裡使用 Routing 時,必備的一些前置作業。也就是說,不是一定要在新建專案的時候加上 --routing
的參數所建立出來的專案才有 Routing 的功能,就算一開始建立專案的時候忘記加 --routing
這個參數,我們也能自己把相關程式碼加上去,使其具有 Routing 的功能。
話不多說,我們趕快來練習吧!為了更清楚地看到效果,我們先把 app.component.html
清空,使其只留下:
<router-outlet></router-outlet>
然後用 CLI 建立兩個 Component:
ng generate component home
ng generate component about
接著開啟 app-routing.module.ts
,引入剛剛建立的的 Component:
import { AboutComponent } from './about/about.component';
import { HomeComponent } from './home/home.component';
然後在 const routes: Routes = [];
裡加入以下設定:
const routes: Routes = [
{
path: 'home',
component: HomeComponent
},
{
path: 'about',
component: AboutComponent
}
];
上述設定的意思是,當有人要瀏覽 localhost:4200/#/home
這個網址的時候,我們想讓他看到的是 HomeComponent ;當有人要瀏覽 localhost:4200/#/about
這個網址的時候,我們想讓他看到的是 AboutComponent。
然後我們打開瀏覽器,在網址列輸入 localhost:4200/#/home
看看,如果有設定好的話,我們應該會看到 home works!
這個字串。
但我們現在應該只會看到...一片空白?!
沒錯,就是一片空白。
有時候如果我們在設置 Routing 時遇到像上述這樣的情況,明明設好了路由卻不知道發生了什麼問題,要追還真不知道要從哪裡追起。
不過 Angular 依舊貼心地提供了一個功能給我們,只要在 RouterModule
的 forRoot
函式裡多傳入一個 { enableTracing: true }
的物件如:
@NgModule({
imports: [RouterModule.forRoot(routes, {
enableTracing: true
})],
exports: [RouterModule]
})
開啟這個功能之後,在每次 Routing 改變的時候都會在控制台裡印出像是這樣子的 Log :
從這個 Log 中可以看到,Angular 目前實際的 url
是 /#/home
,但我們剛剛設定的值是 home
,難怪會一片空白。
那怎麼辦?直接改成 /#/home
嗎?
當然不是,其實我們只要在剛剛那個傳入 forRoot
函式裡的物件,多加一個名為 useHash
的屬性:
@NgModule({
imports: [RouterModule.forRoot(routes, {
enableTracing: true,
useHash: true
})],
exports: [RouterModule]
})
加完這個屬性之後,畫面上就會出現 home works!
的字樣,而且也會看到控制台裡的 Log 已經變成:
Router Event 變多、且也不會再看到 /#/
的字串。
接下來我們來加個連結,不然每次都用打字的好累。打開 app.component.html
檔,然後加上:
<ul>
<li><a [routerLink]="'/home'">Home</a></li>
<li><a [routerLink]="'/about'">About</a></li>
</ul>
這樣就完成了基本的路由設定以及換頁效果了:
今天就先到這邊,消化一下,明天再來解釋 Angular 是怎麼辦到的。
有任何的問題歡迎在底下留言,我們明天見!
LEO大大您好:
跟您請教,路由上有這個 # 的好處是什麼 ?
為什麼需要這個 # ?
有什麼方法能不要在網址上出現這個 # 卻有一樣的效果?
Hi WT,
翻了一下,原來我沒提到用 #
的原因。
基本上,這跟瀏覽器的 History 與後端的行為有關,我直接從優缺點來分析:
沒有 #
的優點
沒有 #
的缺點
404
。有 #
的優點
#/
之後的路由怎麼變,瀏覽器都不會發送請求到後端。有 #
的缺點
好的! 謝謝您的回答,感謝^^
有什麼方法能不要在網址上出現這個 # 卻有一樣的效果?
作者文中有提到NgModule 下有個 useHash改為false,就可以不需要那個#符號了。
Hi thuartlynn,
那個是 RouterModule 的參數,不是 NgModule 的唷!
Loe 大您好,首先謝謝您系列的文章,受益良多~~
另外,想問您一個問題,
在您文章中那些自製的 gif 圖檔,都是用哪一種套件做成的呢?
謝謝您~
Hi stealing610,
我是螢幕錄影之後 google 線上的影片轉 gif 的網站轉出來的
好的,謝謝 Leo 大大。