從Angular2路由引起的先後端路由淺談

筆者的學習進度比較慢,直到兩年之前寫的網站都仍是以服務端爲主導的,即網站的全部視圖都由服務器視圖模板來渲染,筆者使用的是 DotNet MVC,開發套路就是在Controller裏面寫Action,在Views裏寫對應的Action.cshtml,使用ajax發起請求已是比較前端的事情了。這種時候因爲DotNet MVC框架繼承的微軟風格的懶人模式,甚至不須要去知道其路由是如何實現的,給人一種感受是隻要在瀏覽器裏敲進去Controller名與Action名,就訪問到視圖了。html

後來筆者開始使用ng1前端框架,起初的開發徹底放棄其路由功能,而使用服務端MVC路由來區分頁面,在各自頁面內寫ng1代碼。如今想來這樣作實在是浪費了ng1的能力,由於.Net MVC的視圖能力雖然說有佈局頁這些概念,但其仍然是多頁面的應用,視圖間的切換都會形成頁面的刷新,這會致使每切換一個頁面都會從新加載一次ng1近兩萬行的代碼。根本緣由是沒有利用這個強大的前端框架的精髓之一——前端路由。前端

拋開前端路由的底層,其所作的事情就是動態操做DOM來模擬頁面的切換,帶來的好處是巨大的,首先是不用再頁面切換時重複加載大量的腳本依賴了,還有就是頁面切換再也不是白屏讀條,而是能夠加上接近原生應用的切換效果。而使用了前端路由後要解決的最大問題,天然是如何與服務端路由分離,至少不形成衝突。html5

前端路由有兩種形式,一種是Html5的pushState風格,一種是使用#符號實現與服務端路由的分隔,筆者在基於微信公衆號開發的時候涉及到了使用前端路由的SPA與微信API交互的各類狀況,發現這兩種形式各有問題,只能說微信以及傳統的web服務器(至少IIS這個毒瘤)對如今這些先後端分離的SPA應用還不怎麼友好。node

 

1、ng2項目的部署webpack

因爲ng2採用了各類web新特性,包括TypeScript語法特性以及基於SystemJS或者webpack的代碼模塊化,已是個完全的客戶端web體系,能夠說若想要使用ng2來開發項目的話,就擺脫不了先後端分離的架構了。而說到web的先後端分離,其前端(客戶端)雖然說跟服務端(WebApi)分離了,但卻也仍然須要一個服務器來支撐前端入口頁面以及框架腳本的文件分發。這就涉及到如何部署ng2的網站了,筆者獲得的總結是隻要ng2使用webpack打包編譯獲得目標文件後,放到任何服務器上均可以——由於獲得的全都是靜態資源,須要的只是架設成一個靜態網站就足夠了。一種選擇是直接使用node圈子提供的服務器,並使用nginx完成反向代理等工做,因爲某些歷史緣由筆者將ng2網站部署到了IIS服務器上,也是能夠運行的。nginx

 

可是在IIS上部署ng2項目會有一個最直接的問題,那就是先後端路由衝突了(使用html5 pushState風格的狀況下)!解決的辦法有兩個:web

1. 老老實實使用#風格來區分先後端路由。其實先後端分離後沒有了後端路由,此時的網站地址永遠是 domain/#/url 格式的。ajax

2. 筆者在使用#方式時發現此方式下發起微信網頁受權的話會發生很尷尬的問題。後端

  配置給微信那邊的回調地址應該是這樣的格式:瀏覽器

domain/#/url?code=xxxxx&state=xxxxx

  可是至少目前的微信受權接口,會自做聰明處理#符號,致使其會回調到這樣的路徑: 

domain/?code=xxxxx&state=xxxxx/#/url

也就是說微信這廝竟然認爲#後邊的參數再也不參與路由了,強行把回調參數提到#號以前,簡直蛋疼。

因此使用pushState是不可避免了。(但其實在pushState風格下,微信的支付url配置又會有點問題,說多了都是淚)

 

網上大體有兩種辦法來防止被服務端路由影響:

1) 重寫url(沒去試過)

2) 筆者最後採用的辦法是修改404的重定向到網站入口文件

  雖然感受有一些暴力,可是仔細一想這也只是服務端的404頁面,自己也沒有任何意義了,真正的404頁面也應該是在前端路由中導航纔是,因此把全部的服務器端的404請求重定向了也沒什麼不妥的。

 

2、簡單介紹ng2下的路由配置

本來寫本文時只打算寫點ng2路由是如何代碼實現的,可是其實ng2的路由配置也就那麼一點內容,感受更麻煩的反而是整個的前端路由這個概念,因此到後半段纔來說講ng2的路由配置。

路由的目標

首先要明確路由的目標,就是組件。

ng2的頁面都是基於組件的,而路由要作的就是切換各個組件,其須要依賴的就是<router-outlet>標籤,並在ng2模塊的定義中聲明便可。

好比通常咱們的根模塊作的事情除了加載核心依賴以外就是要作好根路由的配置,決定頁面首先要顯示哪一個頁面,以及哪幾個url對應哪幾個組件,當url匹配時就加載這些組件。而根組件中也是,除了其餘全局標籤以外,必不可少的就是一個<router-outlet>標籤,全部的路由都是由這個路由插座展開的。

 

路由的跳轉與參數

路由的跳轉有兩種方式,一是在界面中使用路由指令:

<a [routerLink]="['/url',參數]">Click Me</a>

二就是在腳本中手動跳轉:

router.navigate(['/url', 參數])

而在新頁面中獲取路由參數也很簡單,只要使用angular2的系統路由模塊提供的ActivatedRoute服務便可。

 

懶加載路由

ng2的路由是支持懶加載的,確切的說懶加載的對象是ng2模塊。

由於ng2應用規模變大之後會有多個模塊,而這些模塊不可能同時被使用,確定是當路由指向到模塊包含的某個組件(頁面)後才真正須要加載這個模塊,這時就要用到路由的懶加載能力了。其實現方式很簡單:

{ path: 'url', loadChildren: '目標模塊的文件路徑#目標模塊名' }

這樣在url匹配時,纔會先找到此模塊文件的位置,而後加載此模塊,好處就是不須要再應用一開始就加載全部模塊,能節省資源,ng2甚至有模塊預加載的能力,即後臺異步提早加載好指定的懶加載模塊,這樣在須要此模塊時,模塊其實已經加載完畢,速度更加提高了。

 

至此筆者使用的路由配置通常是這樣的形式:

1. 在應用的根模塊中配置根路由,根路由只配置子頁面所屬模塊的懶加載配置,以及必要的入口頁面對應組件的直接加載。

複製代碼
 1 const routes: Routes = [
 2     { path: 'directive', loadChildren: './directive/directive.module#DirectiveModule' },
 3     { path: '', redirectTo: '/', pathMatch: 'full'},
 4     { path: '**', redirectTo: '/', pathMatch: 'full' }
 5 ];
 6 
 7 @NgModule({
 8     imports: [RouterModule.forRoot(routes)],
 9     exports: [RouterModule],
10     declarations: [ ],
11     providers: [ ]
12 })
13 export class AppRoutingModule { }
複製代碼

2.具體的模塊中配置子路由,在子路由中詳細指定自路徑對應的子組件

複製代碼
const routes: Routes = [
    {
        path: '', component: DirectiveComponent, children: [
            { path: 'home', component: DirectiveHomeComponent }
        ]
    },
    { path: '**', component: DirectiveHomeComponent }
];

@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule],
    providers: []
})
複製代碼

具體的路徑匹配規則與路由模塊的聲明方式等這裏就不提了,沒有什麼難度可言,關鍵在於其路由方式的理解。

 

路由動畫的問題

筆者遇到的目前ng2的路由體系的一個很大的問題就是路由動畫。

因爲ng2的路由依賴於一個<router-outlet>標籤,而實際的組件(頁面)的切換過程都是:

初始化頁面1 -> 定向到頁面2 -> 銷燬頁面1 -> 初始化頁面2

ng2的路由組件切換很是盡職的會先銷燬前一個組件再建立後一個組件,這致使即便你給組件配置了很酷炫的切換動畫,前一個被銷燬的組件也會直接蒸發,然後一個組件會正常按動畫來出現。看到的效果就是,每次路由切換,頁面都會先變成一片空白,而後新頁面姍姍來遲,滑動進來也好,淡出顯示也好,都給人一種尷尬的感受。StackOverflow上有大牛給出了很厲害的解決方案,但給筆者的感受是,僅爲了路由的切換這也太不優雅了,期待ng2官方在將來的更新中給出一套更優雅的路由切換動畫方案。

 

總結:

如本文的篇幅同樣,ng2的路由其實沒多少東西,可是卻問題重重,一般要解決的不是路由路配置,而是如何讓這個還比較新的前端路由體系適應於當前已有的互聯網服務的大環境。

相關文章
相關標籤/搜索