本文將會介紹next.js的路由相關內容,做爲一個新手,筆者自知文章並無太多深層次的東西,只但願幫助到跟筆者同樣的入門級小夥伴。本文基礎爲上一篇next.js+koa2+antd環境輕鬆搭建javascript
在介紹路由以前,想先簡單說一下目錄結構,其中有些東西對於路由講解仍是頗有幫助的css
├── .next
│ ├── build-manifest.json
│ ├── react-loadable-manifest.json
│ ├── server
│ └── static
├── components
│ ├── head.js
│ └── nav.js
├── pages
│ ├── _app.js
│ └── index.js
├── static
│ └── favicon.ico
├── server.js
├── .babelrc
├── .gitignore
├── next.config.js
├── package.json
├── README.md
└── yarn.lock
複製代碼
這是next.js+koa2+antd環境輕鬆搭建一文中建立的next+koa2+antd的文件目錄,其中的
README.md
,package.json
,yarn.lock
,.gitignore
就不說了。vue
pages
和components
pages
是next.js中很是重要的一個目錄,其中每個js文件就表明一個頁面,可是有兩個例外,一個是上一篇文章中用到的_app.js
,一個是_document.js
。咱們在pages下再建立一個a.js和test/b.js,而後看看效果java
// a.js
export default () => <div>this is a page</div>
// test/b.js
export default () => <div>this is b page</div>
複製代碼
咱們能夠發現next.js會將pages下的js文件根據其路徑名和文件名自動生成對應的路由。
可是咱們再寫頁面的時候不可能將全部東西都放在pages下,咱們能夠將具體內容做爲組件放在components
目錄中,而後在pages
中相應的js文件中引入。若是是用腳手架工具生成項目的小夥伴能夠很直觀的看到components
目錄中有head.js
和nav.js
兩個組件,在pages/indx.js
中將其引入並使用react
.next: 在咱們運行過next.js項目以後,會自動生成.next
目錄,這裏存放的內容是next.js將咱們寫的pages和components等源碼打包編譯後的結果,咱們經過瀏覽器訪問能夠拿到的內容其實就來自這裏,在後續進行上限打包時候也會生成這個目錄.git
Tips:請不要修改這個目錄中的內容express
其餘 staic
目錄存放靜態文件,好比圖片,css,favicon.ico等 .babelrc
文件存放babel配置 next.config.js
存放next的配置 server.js
是咱們上一篇寫Koa服務器程序的代碼npm
Link
組件咱們能夠刪除index.js中的全部內容,重寫爲:json
import Link from 'next/link' //引入Link組件
import { Button } from 'antd' //引入antd中的Button組件
export default () => {
return (
<Link href="/a"> <Button>跳轉到A</Button> </Link>
)
}
複製代碼
這裏咱們引入Link組件並將Button包裹在Link組件中,Link的href
屬性可讓咱們選擇跳轉到的路由,這裏咱們/a
是跳轉到咱們上文中建立的a.js對應的頁面後端
值得說明的是,Link的機制並非在Button上增長了a標籤實現的跳轉,而是對其中的組件添加了click事件,而後在瀏覽器中建立一個路由,在最終渲染的結果上並不會對Button包裹任何元素,咱們也能夠看到點擊Button頁面也不會有刷新的狀況。
還有一點須要說明的是,Link經過React.Children.only
規定了它所包含的元素只能有一個,若是咱們再並列Button寫一個標籤就會報錯
<Link href="/a">
<Button>跳轉到A</Button>
<Button>也跳轉到A</Button>//報錯
</Link>
複製代碼
若是咱們有這種需求,能夠將兩個Button包裹在<div>
中
<div>
<Button>跳轉到A</Button>
<Button>也跳轉到A</Button>
</div>
複製代碼
此時點擊兩個按鈕都會跳轉到a頁面,(知道事件冒泡的小夥伴可能對爲何都能跳轉到a頁面,不清楚的小夥伴能夠去查查事件冒泡和捕獲)
next.js爲咱們提供的並不僅是
Link
組件,它還爲咱們提供了Router的方式 Tips: 這裏的Router不是一個組件而是一個路由對象,Link的實現原理也是基於Router對象
咱們先來看看代碼:
import Link from 'next/link'
import Router from 'next/router' // 新引入進來的
import { Button } from 'antd'
export default () => {
const goToB = () => {
Router.push('/test/b')
}
return (
<> <Link href="/a"> <Button>跳轉到A</Button> </Link> <Button onClick={goToB} >跳轉到B</Button> // 新增的一個Button組件 </> ) } 複製代碼
咱們能夠看到新增了一個Button組件,它有一個goToB
的onClick事件,點擊以後會執行goToB
函數。goToB
作了什麼呢?它給Router添加了一個路由/test/b
對應咱們以前/test/b.js
渲染的內容,此時咱們就能夠跳轉到B頁面了,是否是很簡單。
在next.js中,沒法經過
/test/:id
這種參數路由的方式獲取到參數,它只能經過query
的方式獲取參數,即/test?id=xx
的方式
寫法:
<Link href="/a?id=1">
<Button>跳轉到A</Button>
</Link>
複製代碼
使用Router的方式也能夠經過問號這種形式來寫,不過還能夠經過給Router.push()
傳遞一個對象來寫
const goToB = () => {
Router.push({
pathname: '/test/b',
query: {
id: 2
}
})
}
複製代碼
此時對應a頁面的內容要得到傳遞過來的id=1,就得稍微改寫一下:
import { withRouter } from 'next/router' //新引入的
const A = ({ router }) => <div>this is a page,參數是{router.query.id}</div>
export default withRouter(A)
複製代碼
咱們引入了withRouter
組件,它是一個高階組件(HOC),即參數爲被包裹的組件,返回值也是一個組件(不瞭解的小夥伴能夠去看看react文檔中高階組件的部分)。
咱們這裏不直接導出A組件了,而是導出withRouter包裹後的組件,而且給A組件傳入了router參數,經過router.query.id獲取傳遞過來的id。 咱們能夠看看效果
上面說過next.js中沒有參數路由,只能經過
/test?id=xxx
來傳遞參數,可是咱們就是想用參數路由怎麼辦,雖然咱們作不到,可是能夠模擬一下,經過/test/xxx
來傳遞參數
寫法:
<Link href="/a?id=1" as="/a/1">
<Button>跳轉到A</Button>
</Link>
複製代碼
是否是更優雅了,只不過咱們這裏寫死了而已
Router.push()的寫法:
Router.push({
pathname: '/test/b',
query: {
id: 2
}
}, '/test/b/2')
複製代碼
達到的效果是同樣的
請注意 咱們將http://localhost:3000/a/1複製在新標籤頁打開,會出現404
爲何會出現這種狀況? 有沒有小夥伴記得上文提到過,上文中說過一句話而後在瀏覽器中建立一個路由
此處不知道有沒有小夥伴會問:既然next.js路由跳轉都是在本地瀏覽器建立的路由,那爲何經過Button點擊進入的localhost:3000/a
複製以後在新標籤頁打開倒是正常的?這是由於服務器端咱們確實存在pages/a.js
啊,此處恰好瀏覽器端認識/a
路由,可是/a/1
加上id後瀏覽器端就沒定義過這個pages/a/1.js
文件,因此什麼都匹配不到
那這種狀況該怎麼處理,請不要擔憂,咱們安裝的koa會提供全面的路由,咱們只須要作個路由映射就行了,使用express或者egg.js也是一樣的道理。
首先咱們執行npm install koa-router --save
或者yarn add koa-router
(筆者使用的是yarn)安裝koa路由中間件 在server.js中引入並使用
/* * @Author: yishuai * @Date: 2019-04-21 09:57:53 * @Last Modified by: yishuai * @Last Modified time: 2019-04-21 12:22:22 */
const Koa = require('koa')
const Router = require('koa-router') // 引入路由
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = new Koa()
const router = new Router() // 定義路由
// 設置路由,與next.js的路由進行映射
router.get('/a:id', async (ctx) => {
// handle傳入的第三個參數跟咱們next.js中用Router.push({})傳入的數組同樣
await handle(ctx.req, ctx.res, {
pathname: '/a',
query: {
id
}
})
ctx.respond = false
})
// 使用路由
server.use(router.routes())
server.use(async (ctx, next) => {
await handle(ctx.req, ctx.res)
ctx.respond = false
})
server.listen(3000, () => {
console.log('server is running at http://localhost:3000')
})
})
複製代碼
具體映射方式在上方代碼的註釋中,就不詳細說了不熟悉koa的小夥伴去看看koa的文檔,瞭解一下koa路由的使用。 而後打開瀏覽器新建頁面從新輸入localhost:3000/a/1
就能夠正常訪問了
這裏說的鉤子,相信大多數了解過react或vue生命週期的小夥伴都知道生命週期鉤子,這些鉤子函數在必定條件下會自動執行,咱們若是在某些生命週期過程當中有自定義的需求,能夠藉助生命鉤子函數來完成。好比react中的
componentDidMount(){xxxxxx}
能夠在組件加載完成後作xxxxxxxx
事情,好比當組件加載完成後咱們要驗證用戶是否是登陸了,就能夠把xxxxxxx
替換成咱們的驗證邏輯。這裏的路由鉤子也是同樣的道理,當路由被觸發的時候就會在路由跳轉前,跳轉後等時間節點自動執行一些函數,這些函數就是路由鉤子
我在index.js中添加了
(不是重寫index.js)這樣一段代碼:
// 全部的路由鉤子名稱,寫在了一個數組中
const events = [
'routeChangeStart',
'routeChangeComplete',
'routeChnageError',
'beforeHistoryChange',
'hashChangeStart',
'hashChangeComplete'
]
// 經過一個高階函數在鉤子觸發後執行自定義的邏輯,這裏直接輸出了鉤子名稱和鉤子函數的參數
function makeEvent(type) {
return (...args) => {
console.log(type, ...args)
}
}
//經過forEach遍歷 綁定鉤子事件
events.forEach(event => {
Router.events.on(event, makeEvent(event))
})
複製代碼
上面註釋中說明了一些狀況,咱們能夠經過events名稱直觀地看到next的鉤子總共有六種分別是:路又開始時候,路由完成時候,路由出錯時候,路由歷史更改以前,哈希路由開始時候,哈希留有完成時候這6個時間節點會觸發相應的路由鉤子。
中間用了一個高階函數,可能有些小夥伴會有點看不懂,這裏簡單介紹一下:
高階函數簡單講就是一個函數做爲另外一個函數的參數,或者一個函數做爲另外一個函數的返回值。
若是說咱們只想綁定routeChangeStart
事件,則能夠這樣寫,當觸發routeChangeStart時候,輸出一個內容。
Router.events.on('routeChangeStart', function(...args){
console.log('routeChangeStart',...args)
})
複製代碼
咱們這裏爲了方便使用了forEach循環了上述代碼,傳遞的Router.events.on()
第二個參數要接收一個函數,咱們就能夠利用高階函數,執行高階函數後恰好返回一個函數,就用來作它的第二個參數,相信到這裏再看上面的代碼就清晰不少了。
咱們綁定了全部的鉤子,能夠去看看效果:點擊跳轉到A
按鈕,輸出如下內容
routeChangeStart
鉤子,輸出routeChangeStart,以及對應
...args
參數,而後此時瀏覽器的路由歷史會發生改變,由於跳轉後前一個歷史就是咱們的
localhost:3000
這個頁面,在歷史發生改變的前一刻觸發
beforeHistoryChange
,而後路由進行跳轉,結束後觸發
routeChangeComplete
路由更改完成的鉤子。
hash路由也是同樣的:相對來講hash路由在本頁跳轉不會更改歷史,因此咱們將會看到這樣的效果
這個實驗怎麼作呢?咱們能夠去更改/pages/a.js,引入Link組件,給原來返回的內容包裹上Link和a標籤import { withRouter } from 'next/router'
import Link from 'next/link' // 新引入的
// 外層加了個a標籤和Link標籤,Link標籤跳轉到hash路由#hello
const A = ({ router }) => <Link href="#hello"><a><div>this is a page,參數是{router.query.id}</div></a></Link>
export default withRouter(A)
複製代碼
好了,到此next.js的路由咱們就有了必定的認識。感謝小夥伴堅持到最後。
next.js的路由相對後端框架或者是react-router都要簡單不少,本質上就只是pages路徑下對應的js文件的目錄和文件名稱直接做爲路由,而後就是支持了query傳遞參數,路由跳轉是在本地瀏覽器上操做的,因此若是不借助外力,可能會有404錯誤,所以要藉助後端框架實現路由映射,最後咱們提到了路由鉤子。但願這篇文章對小夥伴們有用,感謝閱讀。