本系列一共七章,Github 地址請查閱這裏,原文地址請查閱這裏。javascript
這是編寫前端框架系列的最後一章。本章,我將會討論前端路由和後端路由的不一樣以及爲何他們應被區別對待。html
網頁無非是後端渲染,前端渲染或者二者混合渲染。無論怎樣,一個半複雜的網頁不得不處理路由。前端
對於後端渲染,路由是由後端處理的。當 URL 路徑改變或者請求參數改變的時候會輸出一個新的頁面,這對於傳統網頁是完美的解決方案。然而網頁程序常常須要保持當前用戶的狀態,這在海量的服務端渲染的頁面之間是很難維護的。java
客戶端框架經過預讀取程序和在存儲的頁面間切換並保持狀態來解決這些問題。前端路由的實現與服務器端的路由很是類似。惟一的區別是它直接從前端而不是後端獲取資源。本篇文章中,我將會解釋爲何這二者須要稍微處理得有些不一樣。react
許多前端路由庫都是由後端啓發的。git
他們只是在 url 改變的時候運行適當的路由處理程序,從而啓動和渲染所需的組件。前端和服務端的結構是相似的,惟一的區別便是處理函數所作的事情。github
爲了演示其類似性,你能夠在以下的代碼中發現服務端 Express 框架,前端代碼 page.js 路由和 React 相同的路由代碼片斷。shell
// Express
app.get('/login', sendLoginPage)
app.get('/app/:user/:account', sendApp)
複製代碼
// Page.js
page('/login', renderLoginPage)
page('/app/:user/:account', renderApp)
複製代碼
<!-- React -->
<Router>
<Route path="/login" component={Login}/>
<Route path="/app/:user/:account" component={App}/>
</Router>
複製代碼
React 隱藏了一些 JSX 背後的邏輯,可是它們作的都是一樣的事情,在引入動態參數以前,它們都工做得很完美。express
在上面的例子中,一個用戶可能有多個帳號而且當前帳戶能夠隨意更改。若是在 App
頁面改變帳戶名,對應的處理程序爲新的帳戶名重啓或者重發相同的 App
組件 - 然而其實只須要更改現存組件裏面的一些數據便可。編程
對於虛擬 DOM 解決方案這只是小菜一碟,由於它們會查找 DOM 的差別,而後只更新須要的部分 - 可是對於傳統框架,這意味着更多沒必要要的工做。
當參數改變的時候從新渲染整個頁面是我想要避免的。爲了解決這個問題,我先從動態參數中分離出路由。
在 NX 中,路由會決定顯示哪一個組件或視圖,而後進入到 URL 路徑名。動態參數控制當前頁面顯示的數據,他們老是在查詢參數裏面。
這意味着 /app/:user/:account
將會轉換爲 /app?user=userId&account=accountId
。他略顯冗長,但更加清晰,而且它容許我把客戶端路由分爲頁面路由和參數路由。前者在 app 殼中導航,然後者在數據殼中進行導航。
你或許會熟悉 app 殼模型,它在谷歌的 PWA 程序中獲得推廣。
app 殼是一個用來驅動用戶界面的最小化的 HTML,CSS 和 JavaScript。
在 NX 中,路徑路由負責在 app 殼中導航。一個簡單的路由結構以下所示。
<router-comp>
<h2 route="login"/>Login page</h2>
<h2 route="app"/>The app</h2>
</router-comp>
複製代碼
這和以前的例子相似 - 特別是和 React 例子 - 可是這裏有一個主要的區別。它沒有處理 user
和 account
參數。相反,它只是在空的app shell中導航。
這讓它成爲一個很是簡單的樹遍歷問題。路由樹被遍歷 - 基於 URL 路由 - 而後它以它的方式顯示組件。
以上圖表解釋了當前視圖是如何切換爲 /settings/profile
地址的。你能夠找到以下相應的代碼。
nx.components.router()
.register('router-comp')
複製代碼
<a iref="home">Home</a>
<a iref="settings">Settings</a>
<router-comp>
<h2 route="home" default-route>Home page</h2>
<div route="settings">
<h2>Settings page</h2>
<a iref="./profile">Profile</a>
<a iref="./privacy">Privacy</a>
<router-comp>
<h3 route="profile" default-route>Profile settings</h3>
<h3 route="privacy">Privacy settings</h3>
</router-comp>
</div>
</router-comp>
複製代碼
這個示例展現了擁有默認和相對路由的嵌套路由結構。如你所見,只用 HTML 配置至關的簡單,而且它和大多數的文件系統運行原理相似。你能夠在它裏面使用絕對路徑 home
和相對路徑 ./privacy
連接來導航。路由片斷運行效果以下圖所示。
這個簡單的結構能夠被濫用來建立強大的模式。一個例子是並行路由,指的是同一時間遍歷多個路由樹。側邊菜單欄和 NX docs page 的內容都是這樣工做的。它有兩個並行的內嵌路由,同時改變側邊導航和頁面的內容。
和 app 殼不一樣,'data shell' 並非一個炒做的術語。事實上,它只供我使用,它指的是用動態參數池來驅動數據流。它不是更改當前頁面,它只更新頁面的數據。改變當前頁面一般會改變參數池,但改變參數池的參數不會致使頁面刷新。
一般狀況下,數據殼是由一組原始值-還有當前頁面所組成,它表示當前程序的狀態。所以,它能夠用來保存加載和分享狀態。爲了達到這個目的,它必須在 URL,localStorage 或者瀏覽器歷史中體現-這使它實質上是全局的。
NX 衆多組件之中的控制組件能夠經過聲明性配置鏈接到參數池,它會決定參數如何和組件的狀態,URL,瀏覽器歷史和網頁存儲進行交互。
nx.components.control({
template: require('./view.html'),
params: {
name: { history: true, url: true, default: 'World' }
}
}).register('greeting-comp')
複製代碼
<p>Name: <input type="text" name="name" bind/></p>
<p>Hello @{name}</p>
複製代碼
以上示例代碼建立了一個組件,使得 name
屬性與 URL 和瀏覽器歷史保持同步。你能夠在以下看到效果。
多虧了基於 ES6 代理的透明反射,同步是無縫的。你能夠書寫 vanilla JavaScript,全部的東西都會在後臺按需雙向綁定。下圖給出了一個這方面的深刻概述。
簡單的聲明式的語法鼓勵開發者在編程以前花幾分鐘時間設計頁面的網頁整合。並非全部的參數都應該進入 URL 或者當頁面改變的時候添加一個新的歷史記錄項。有大量的不一樣狀況,每種狀況都得進行適當配置。
url
參數,由於它應該能夠與其餘用戶共享。url
和 history
參數,由於目前的帳戶應該是可分享,而且改變它是足以用來添加一個新的歷史記錄項。這只是一些可能的設置。只須要最少的努力,你就能夠真正地讓這些參數完美地適用於你的使用場景。
路徑路由和參數路由是互相獨立的,他們被設計成能夠很好地一塊兒協做。路徑路由導航到 app 殼中的預期頁面,參數路由接收而且並管理狀態和數據外殼。
不一樣頁面中的參數池也許會不同,因此在 JavaScript 和 HTML 中都有一個顯式的 API 來改變當前頁面和參數。
<a iref="newPage" $iref-params="{ newParam: 'value' }"></a>
comp.$route({
to: 'newPage',
params: { newParam: 'value' }
})
複製代碼
在這個基礎上,NX 自動在激活的連接上面添加 active
樣式類名,這樣你可使用配置參數裏面的 options
配置全部的常見路由功能好比參數繼承和路由事件。
打開 routing docs 來查看更多的功能。
以下示例演示了參數路由與反應性數據流的結合。這是一個徹底能夠工做的 NX app。只要把以下代碼拷貝進一個空的 HTML 文件中, 而後在現代瀏覽器中打開它來試用。
<script src="https://www.nx-framework.com/downloads/nx-beta.2.0.0.js"></script>
<script>
nx.components.app({
params: {
title: { history: true, url: true, default: 'Gladiator' }
}
}).use(setup).register('movie-plotter')
function setup (comp, state) {
comp.$observe(() => {
fetch('http://www.omdbapi.com/?r=json&t=' + state.title)
.then(response => response.json())
.then(data => state.plot = data.Plot || 'No plot found')
})
}
</script>
<movie-plotter>
<h2>Movie plotter</h2>
<p>Title: <input type="text" name="title" bind /></p>
<p>Plot: @{plot}</p>
</movie-plotter>
複製代碼
狀態中的 title
屬性會自動與 URL 和 瀏覽器歷史保持同步。傳入函數中的 comp.$observe
會被監聽,當標題改變的時候,它會自動抓取對應的電影情節。這會建立一個強大的完美地和瀏覽器結合的反應式數據流。
這個 app 沒有展現路徑路由。想看更多的完整示例請查看 intro app,NX Hacker News clone 或者 path routing 和 parameter routing 文檔頁面。都有可編輯示例。
今日頭條招人啦!發送簡歷到 likun.liyuk@bytedance.com,便可走快速內推通道,長期有效!國際化PGC部門的JD以下:c.xiumi.us/board/v5/2H…,也可內推其餘部門!