當網站開發完成或有導入 CI/CD,在準備發布網站前,我們會將 Nuxt 網站的開發專案透過 Nuxt 提供的指令,我們可以來建構出正式環境所需要的版本,這個建構的過程你可以理解為專案將會打包需要的依賴套件、編譯與轉換相關的 Vue SFC 與樣式等,甚至幫你壓縮這些檔案等許多步驟,這些繁瑣的過程都透過 Nuxt 的建構指令來幫你完成,你可以結合一些設定參數與指令,來幫助你建構出正式環境所需要的網站專案,最後再進行部署的動作。
當我們建立一個乾淨的 Nuxt 3 專案,可以使用 package.json 中 scripts 內的指令來進行建構或預渲染產生靜態頁面。
或者你也可以使用 Nuxt CLI 來達到一樣的效果
generate
指令會觸發 build
的指令並帶有 prerender 為 true 的參數,最終產生出來的目錄可以直接進行部署,不需要再執行一次 build
的指令。
這邊我們以這個系列文章所建立的部落格作為範例,來嘗試以預設的配置進行建構,目前 Nuxt 3 預設 nuxt.config.ts
中 ssr
屬性為 true
,也就使用通用渲染 (Universal Rendering)。
我們進入專案目錄,執行下列指令:
npm run build
不需要在額外的配置或步驟,Nuxt 就會自動幫我們打包並建構出可以部署的網站。
建構完成後會在專案目錄下多出一個 .output
目錄,public
就是在網站根目錄直接公開打包後的 JS、CSS 圖片等相關檔案。由於通用渲染使用 Nitro 作為服務引擎,所以會需要使用 Node.js 來啟動我們的服務,所以server
則是會放置伺服器端的 Nitro、Server API 處理邏輯等。
我們可以先在本地執行下列指令來預覽建構出來的網站與 Nitro 是否能運作正常。
node .output/server/index.mjs
當確認沒問題後,就能以 .output
目錄進行網站部署。
在前一篇文章有提到,我們可以在 Nuxt 3 建構時期來進行預渲染 (pre-rendering),將專案內需要打 API 請求及動態渲染元件的地方,先渲染生成出 HTML 網頁,這個過程也稱之為靜態網站生成 (Static Site Generation),進而建構出全靜態頁面的網站。
執行下列指令進行靜態頁面生成:
npm run generate
generate
指令會觸發build
的指令並帶有 prerender 為 true 的參數,最終會提示產生的目錄 .output/public
可以用來部署至靜態託管伺服器。
當建構完成後,會在專案目錄下多出一個 .output
目錄同時也會有 dist
目錄,可以發現 .output/public
目錄內容與 dist
目錄相同。
靜態頁面的生成因為Nuxt 使用基於爬蟲的技術為每個頁面產生 HTML 與 Payload 檔案,可以發現產生的靜態頁面目錄結構,也對應著我們專案的路由頁面,而頁面元件資料夾內會由一個 index.html
與 _payload.js
組成。
下圖所產生的結構,是因為我們的部落格資料庫內已經有三筆文章資料,/articles
頁面會取得所有的文章,並列出對應著 /articles/1
、/articles/2
與 /articles/3
目錄,每個頁面內也都包含預先從 Server API 請求好的文章資料並存於 _payload.js
之中。
至此我們就可以將 .output
目錄部署至伺服器或靜態託管服務,而且也不需要在使用 Node.js 伺服器來服務這些靜態檔案。
我們使用 generate
指令所產生的全靜態頁面網站,是基於 Nuxt 的爬蟲技術來分析頁面,如同前個例子文章頁面的產生所描述,如果我們的資料庫存在著 200 筆資料,但 /articles
頁面所打的 API 總是只回傳最新的 10 筆,那麼那些沒有路由連結可以連結過去的文章頁面,就無法被 Nuxt 的爬蟲所分析到,也就無法產生對應的靜態頁面。
為了解決這個問題,我們可以藉由配置 Nitro 的預渲染路由,來手動的設定哪些頁面要進行預渲染產生靜態頁面,或者忽略產生靜態頁面。
export default defineNuxtConfig({
nitro: {
prerender: {
ignore: [], // 忽略特定路由不進行預渲染
routes: [], // 指定路由進行預渲染
crawlLinks: true // 啟用 Nuxt 爬蟲蒐集頁面連結來進行預渲染
}
}
})
當然,也可以搭配 build
或 generate
指令做使用,但會稍微有些不一樣。
build
例如,設置如下,並使用 npm run build
指令。
export default defineNuxtConfig({
nitro: {
prerender: {
routes: ['/articles', '/articles/2']
}
}
})
那麼建構出來的通用渲染網站,就會包含部分頁面是已經預渲染好的靜態頁面。
generate
當我們使用 generate
時,因為是進行全站的靜態頁面生成,所以基本上涵蓋了所有路由頁面,並且 nitro.prerender.crawlLinks
屬性預設為 true
會啟用 Nuxt 爬蟲來蒐集整的網站的路由頁面,進而開始預渲染產生靜態頁面。
例如,設置如下,並使用 npm run generate
指令。
export default defineNuxtConfig({
nitro: {
prerender: {
ignore: ['/login', '/register'],
routes: ['/articles/2'],
crawlLinks: true
}
},
})
那麼建構出來的 .output
目錄,就會因為設置了 nitro.prerender.ignore
而忽略 /login
與 /register
頁面的靜態生成 ,而 /articles/2
在網站中因為沒有任何頁面能連結到此,所以 Nuxy 爬蟲無法蒐集到,所以可以手動添加進 nitro.prerender.routes
職中。
如果我們將 nitro.prerender.crawlLinks
屬性設為 false
,那麼在產生靜態頁面的時候,就算 /articles
包含了所有文章的連結,Nuxt 爬蟲也就不會蒐集連結來產生靜態頁面,而是僅依照專案目錄下的 pages
所產生出的路由但不包含 [id].vue
這類動態匹配的路由來產生靜態頁面。
同樣的使用部落格作為範例,來嘗試將 nuxt.config.ts
中 ssr
屬性設為 fasle
,也就讓網站僅在客戶端渲染 (Client-side Only Rendering)。
執行下列指令進行建構:
npm run build
建構完成後,會在專案目錄下多出一個 .output
目錄,可以發現 public
目錄結構與通用渲染建構出的不大一樣,多出了像 Vue 專案建構完成後的 index.html
檔案,來作為顯示的容器。
這邊比較需要留意的地方是,當我們將 ssr
屬性設為 fasle
,也還是會在 .output
建構出 server
目錄,也擁有著 .output/server/index.mjs
。
那這不就意謂著我還是得有 Node.js 服務才能部署嗎?其實你可以這麼理解,當我們設定 Nuxt 僅在客戶端進行渲染,但 Nuxt 專案中仍然可能有Server API,這個屬於伺服器端的 API,總不可能一同打包至前端去做架設吧!所以使用 npm run build
建構出來的專案,都會使用 Nitro 引擎來啟動服務,若有 Server API 的處理邏輯,則會連帶打包進 .output/server
目錄之下。
如果你確定 Nuxt 的專案內沒有自己實作的 Server API,全部是依賴非專案內的 Server API,那麼你可以直接將 public
目錄,視為 Vue 建構出的 dist
目錄來進行部署,因為選擇不再需要 Nitro 引擎來為我們提供 Web Server 的服務,且專案內的前端也都打包完在 public
下可以視為 SPA 網站。
相信看到這裡,可能對建構與產生靜態頁面有一點混亂了,這邊稍微整理了一下,大家可以再依據需求來啟用相對應的配置。
ssr: true
Nuxt 3 預設的建構參數,使用通用渲染 (Universal Rendering) 模式。
使用通用渲染模式,頁面中需要打 API 動態產生的連結而被 Nuxt 爬蟲所蒐集到的頁面將被預渲染成靜態頁面。
使用通用渲染模式,但指定頁面預渲染成靜態頁面。
ssr: false
僅在客戶端渲染 (Client-side Only Rendering) 的模式,部署時同樣需要使用 server
中的 index.mjs
來啟動 Nitro 伺服器,這樣才能正確的運作 Server API;除非確認專案內完全無依賴專案內的 Server API,則可以直接將 .output/public
視為 Vue 的 dist
目錄進行部署。
ssr: true
使用預渲染產生靜態頁面,預設產生全站靜態頁面,generate
下 nitro.prerender.crawlLinks
屬性預設變為 true
,整個網站包含 Nuxt 爬蟲所蒐集到的頁面將被預渲染成靜態頁面。
使用 generate
已經包含整個網站,如果想要忽略某些頁面進行預渲染,可以添加路由至 nitro.prerender.ignore
中。但是需要注意,這些被忽略的頁面如果有使用專案內的 Server API 需求,可能就無法正確的在靜態託管平台運作,而外部的倒是不受影響。
雖然使用 generate
已經包含整個網站,但 Nuxt 爬蟲所蒐集到的頁面僅為頁面中產生的,如果連結不存在這些頁面內,只能只接從瀏覽器網址列輸入進入,那麼我們可以將路由添加至 nitro.prerender.routes
來額外補充需要預渲染的頁面。
同樣會產生全站靜態頁面,但不使 Nuxt 爬蟲所蒐集到的連結頁面,所以這些動態匹配或需要打 API 獲取資料再渲染的頁面,將不會是靜態頁面,因此也需要注意,如果這些頁面有使用專案內的 Server API 需求,可能就無法正確的在靜態託管平台運作,而外部的倒是不受影響。
其它跟建構有關的參數屬性可以再參考官方文件,Vite 與 webpack 建構工具也都能額外設定參數屬性。
Nuxt 的建構指令 build
與產生靜態頁面的 generate
指令,讓我們可以依據不同的情境來決定渲染模式與預渲染的頁面,最終產生的 .output
目錄也會有不一樣的結構。預渲染的頁面與單純的 SPA 也要注意使否有使用到 Nuxt 專案建立的 Server API,再決定是否需要 Nitro 伺服器來啟動正式環境的網站服務。當一切就緒之後最後就能將輸出的目錄打包進行部署。
感謝大家的閱讀,這是我第一次參加 iThome 鐵人賽,請鞭小力一些,也歡迎大家給予建議 :)
如果對這個 Nuxt 3 系列感興趣,可以訂閱接收通知,也歡迎分享給喜歡或正在學習 Nuxt 3 的夥伴。