iT邦幫忙

2021 iThome 鐵人賽

DAY 5
0

嗨嗨嗨!昨天看完怎麼抓取資料的方式會影響到網頁的渲染模式之後,今天的主題是 Next.js 的 Pages 與 Routing。Next.js 的 routing 方式跟一般 React 使用的 react-router 不一樣,它採用 file-based routing

Next.js Pages & Routing

File-based Routing

File-based routing 的意思是每個檔案代表一個 route,而檔案名稱 (file name) 會變成 route 的 path name (路徑名稱)。Next.js 會把 pages 資料夾 / 目錄裡面的所有檔案看成 route (路徑) 的一部分,還不是很懂嗎?我們看看下面的例子~

  • pages/index.js 代表 / route
  • pages/about.js 代表 /about route

Index routes

每個 index 檔案代表根目錄,所以

  • pages/index.js 代表 / route
  • pages/posts/index.js 代表 /posts route

Nested (巢狀) routes

每一層資料夾每一層目錄會是 route 的一部分,所以

  • pages/posts/index.js 代表 /posts route
  • pages/posts/first.js 代表 /posts/first route
  • pages/categories/lifestyle/fashion.js 代表 /categories/lifestyle/fashion route

Dynamic (動態) routes

如果上面的 routes 都要我們自己定義每一個 file name 當 path name,那當我們有一系列的頁面怎麼辦?或是如果我們想要把資料庫裡的每一條資料寫個頁面怎麼辦?或是像iT邦幫忙的網址 https://ithelp.ithome.com.tw/users/20141378 應該不會是我們要新增 20141378 個頁面檔案吧?這時候我們需要 dynamic routes 來幫助我們!

  • pages/posts/[pid]/index.js 代表 /posts/1/posts/2,···
  • pages/posts/[pid]/comments/index.js 代表 /posts/1/comments/posts/2/comments,···
  • pages/posts/[pid]/comments/[cid].js 代表 /posts/1/comments/1-1/posts/2/comments/2-1,···

或是iT邦幫忙的網址 https://ithelp.ithome.com.tw/users/20141378 需要一個檔案 pages/users/[uid].js。這些動態的資訊可以在 route.query 物件裡面看到,/posts/a/comments/a-1?utm_source=ithomequery 物件會長這樣:

{ "pid": "a", "cid": "a-1", "utm_source": "ithome" }

Catch all routes

屬於 dynamic routes 的一種,不過 catch all 的意思是不需要一個一個定義動態的部分,把所有動態的資訊抓起來,讓 route 比較彈性,寫法跟其餘參數(rest parameter)一樣。

  • pages/posts/[...slug].js 可以代表 /posts/a 也可以是 /posts/a/b/posts/a/b/c

slug 可以是 param 或是其他東西都可以,這些動態資訊也會出現在 query 物件裡面,例如 /posts/a/b/cquery 會是:

{ "slug": ["a", "b", "c"] }

Optional catch all routes

知道了 catch all routes 之後,還有另一種更有彈性的 dynamic routes,只要把 catch all 的 [...slug] 改成 [[...slug]] 就會變成 optional catch all。意思是 slug 這個 array (陣列) 可以是不存在的,

  • pages/posts/[[...slug]].js 可以代表 /posts/a 也可以是 /posts/a/b/posts/a/b/c 等,不過也可以代表 /posts

所以 query 物件會是空物件 {}

注意:query 在 static (靜態) 頁面在第一次渲染的時候不會有東西,就是一個空物件 {},等到 hydration 完成之後,query 才會被更新

繼續到下一個章節之前,我們複習一下~
Next.js Pages & Routing

Client-side Routing

雖然 routing 方式不太一樣,不過 Next.js 還是跟 SPA (Single-page Application) 一樣可以做 client-side routing,意思是換頁的時候瀏覽器不用重新 reload,使得整個體驗非常順。Next.js 提供兩種方法:

  • <Link> (next/link)
  • useRouter (next/router)

next/link 提供 Link component 可以直接用在我們的 react components 裡面,跟 react-router-dom 裡的 Link 差不多的用法。當我們想要在畫面上顯示一個連結的時候,可以用 Link,他底層用 HTML 的 a 實作出來的。比較特別的是,當使用者畫面上有顯示 <Link /> components, Next.js 會把所有採用 SSG / ISR 的頁面先做 prefetch (事先抓取) 這些頁面的檔案與資料,所以當使用者點下其中一個連結,瀏覽器可以馬上顯示新的畫面不需要等待,提升使用者換頁的體驗。想要用 Link 有幾種方法:

連去 Static Paths

import Link from "next/link"

function Home() {
  return (
    <div>
      <Link href="/">
        <a>Home</a>
      </Link>
      <Link href="/about">
        <a>About</a>
      </Link>
    </div>
  )
}

export default Home

string (字串) 當 href"/" 會連到 pages/index.js 的檔案,然後 "/about" 會連到 "pages/about.js" 檔案。

連去 Dynamic Paths

import Link from "next/link"

function Posts({ posts }) {
  return (
    <div>
      {posts.map(post => (
        <Link href={`/posts/${post.id}?utm_source=ithome`}>
          <a>Post #{post.id}</a>
        </Link>
      ))}
    </div>
  )
}

export default Posts

想要導頁去 dynamic routes 一樣可以把 path 組成 stringhref,例如 /posts/${post.id} 然後 post.id 是動態的。或是可以把 path 拆成 object (物件) 給 href,像這樣~

{
  pathname: "/posts/[pid]", // 跟 file name 一樣
  query: { pid: post.id }, // path 中動態的部分,在這例子是 pid
}

query 不只可以包含 path 中的動態部分,也可以包含 URL 中的 query string,像 /posts/1?utm_source=ithome 這個 path 可以寫成:

{
  pathname: "/posts/[pid]",
  query: { pid: post.id, utm_source: "ithome" },
}

useRouter

除了把連結顯示在畫面上讓使用者點下去以外,我們也可以用程式碼做導頁,就是用 useRouteruseRouter 回傳 router 物件,裡面包含各式各樣與 routing 有關的資訊,例如 pathname (當下的路徑), query (該路徑的 query string), isFallback (伺服器是否正在顯示備份版本的頁面), isReady (瀏覽器是否已經拿到 route 相關資訊) 等資訊。不過 router 還包含一些 methods,有幾個跟導頁有關的:

  • push:導去某個頁面
  • replace:導去某個頁面可是把目前所在的記錄去換掉
  • back:回到上一頁

Shallow (淺) Routing

什麼是 shallow routing 或是淺層換頁 (淺層路由)?換頁的過程中伺服器不會重跑 data fetching 的 functions,也不會 re-render (重新渲染) 整個頁面保持該頁的狀態。雖然頁面不會被換掉,可是 router 中的 pathnamequery 會拿到最新值。

router.push("/?id=1", undefined, { shallow: true })

不過要注意,shallow routing 只能用在同一個頁面,/?id=1 換去 /about?id=1 是不同的頁面,所以 Next.js 會跟平常一樣去下載新的頁面。

Next.js Client-side Routing

小結

File-based routing 是不是看起來很棒呢?~ 不過還是要注意小細節喔,避免遇到 routing 相關問題。明天跟大家分享讓 Next.js 變成一個全端框架的原因,就是 API routes!
祝大家中秋節快樂!

晚安 <3

看更多


上一篇
#04 No-code 之旅 — Next.js 中的 Pre-render 與 Data Fetching
下一篇
#06 No-code 之旅 — 用 Next.js 解決前後端?API Routes 簡介
系列文
製作你的無程式碼(No-code)個人網頁 ft. Next.js, SWR, 串 Youtube, IG, Github, Notion API ~30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言