React/Vue等框架預設都是CSR(Client-Side-Rendering),也就是畫面上任何元素的呈現,都是透過js去向後端拿資料後渲染在畫面上。因此,無論是SPA或MPA在該頁面的第一次載入會需要執行大量的js,速度會比較慢,但是之後的互動就會快很多,因為每次都是更動小區塊的畫面,使用者體驗很好。
然而,也因為CSR的特性,在SEO方面通常會較差,雖然Google爬蟲號稱可以爬取CSR渲染後的HTML,但可能還需要時間驗證效果如何。
因此,為了解決SEO問題,傳統的SSR(Server-Side-Rendering)又被拿出來討論了(現在講的SSR通常是指前端框架的SSR),許多大型專案紛紛開始採用SSR技術,如Next.js就是React的SSR框架,使用者透過瀏覽器進入某個頁面後,由Server產生完整的HTML並回傳瀏覽器,解決了第一次載入速度慢的問題,最重要的是解決了SEO問題。
安裝設置:
$ npx create-next-app@latest
安裝完成後,專案目錄結構如下:
執行$ npm run dev 可直接透過http://localhost:3000進入首頁,對應到pages/index.js。
#開發模式下啟動 Next.js 專案
$ npm run dev
Prod記得每次要先build再start:
#build next prod application
$ npm run build
#啟動next prod server
$ npm run start
接著看看它的source code,可以看到body中的html內容是完整的:
反之,CSR source code 只有root div,html內容皆是透過渲染產生的。
接著在pages下新增一個about.js,該頁面對應到http://localhost:3000/about,Next幫我們把Routing都處理好了,相當方便:
Next.js提供兩種pre-rendering的方式,一個是靜態頁面生成SSG(Static Site Generation),一個是伺服器渲染(SSR),預設為SSG,如上面建立的about頁面。
官方建議若是可以預先渲染的頁面,都採用SSG是比較好的方式,SSG的HTML是在build time建立好的,可以被CDN緩存,速度會比SSR快許多。相反的,若是頁面無法預先渲染,例如資料會頻繁更新或是會根據不同request產生不同內容,則適合用SSR。
Example:
- SSG: 透過外部api取得資料建立靜態頁面
使用getStaticProps透過外部api取得data傳入Article中,並渲染出畫面。
後端api回傳資料如下:
pages/article.js:
export async function getStaticProps() {
const res = await fetch('http://localhost/redis/public/api/article')
const data = await res.json()
return {
props: {
data,
},
}
}
function Article({ data }) {
return (
<ul>
{data.message.map((article) => (
<li>{article.title} - {article.create_datetime}</li>
))}
</ul>
)
}
export default Article
2. SSG: 動態路由應用,建立多個靜態頁面。
後端api回傳資料:
export async function getStaticPaths() {
const res = await fetch('http://localhost/redis/public/api/article')
const data = await res.json()
const paths = data.message.map((article) => ({
params: { title: article.title.toString() },
}))
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
const res = await fetch(`http://localhost/redis/public/api/article?title=${params.title}`)
const data = await res.json()
return { props: { data } }
}
function Article({ data }) {
return (
<ul>
<li>{data.message.title} - {data.message.create_datetime}</li>
</ul>
)
}
export default Article
- articles/[title].js 這種使用方式,目的為使用文章title來建立預渲染的路徑。
- getStaticPaths: 用來生成所有需要預渲染的路徑,這邊是設定三篇文章的title。
- getStaticProps: 根據params的文章title透過外部api取得文章資料,渲染該文章靜態頁面。
- SSG方式需特別注意,只要DB資料有變動,例如有新增、修改文章或文章被刪除,就需要重新build code,產生新的靜態頁面。(npm run dev模式下會讓人誤以為是動態的,需特別注意)
3. SSR: 每次request頁面時,動態重新產生頁面的HTML。
export async function getServerSideProps() {
const res = await fetch(`http://localhost/redis/public/api/article`)
const data = await res.json()
return { props: { data } }
}
function ArticleSSR({ data }) {
return (
<ul>
{data.message.map((article) => (
<li key={article.id}>{article.title} - {article.create_datetime}</li>
))}
</ul>
)
}
export default ArticleSSR
- getServerSideProps: 每次request進來會執行這邊,取得資料後送到ArticleSSR渲染HTML。
- 由於是每次request都會動態拿資料,因此適合變動頻繁的系統,不須重新build code。
結論:
- Next.js框架支援SSR/SSG可以讓開發者根據情境選擇適合的用法,加上配置好的Routing,不須像React SPA轉MPA一樣要經過繁雜的設定,可說是相當的方便。
- 關於SSR/SSG的選擇,建議除非有必要性,否則一律使用SSG速度會比較快,畢竟是預渲染好的頁面。若是使用情境會需要頻繁更動資料,無法忍受每次都要重build code的話,就選擇SSR。
- Next.js不僅解決了SEO問題,還可以混用CSR,使用SSR/SSG,讓頁面第一次載入速度變快,小區塊UI變動則使用CSR,不必整個頁面重新生成,等於結合了各方優點,只要適當的使用,必能大大提升應用程式的使用者體驗。