Next.js 在 Serverless 中從踩坑到破繭重生

CODING發表於2022-04-26

本文作者:楊蘇博

偏後端的全棧開發,目前負責騰雲扣釘的 Cloud Studio 產品。對 WebIDE 領域中的 VS Code 和 Theia IDE 有深入研究與豐富實踐;多年 Serverless 領域從業經驗,是 Serverless First Malagu 開源框架的作者;熱愛開源,敢於創新。

前言

Next.js 是由 Vercel 團隊研發的一款全棧應用開發框架,我們使用 Next.js 開發前端頁面以及一些輕量級的後端 API,前端和後端都用 Javascript 技術棧,並且是前後端一體化的(在同一個專案中開發前後端)。另一個被大家所熟知的特性是它的服務端渲染能力,對 SEO 友好。Vercel 自身是一個使用者體驗極佳的 Serverless 平臺,支援包括 Next.js 在內的幾十種開發框架一鍵部署到 Vercel 平臺。Vercel 平臺自身擁有極強的適配擴充套件能力,第三方框架可以按照 Vercel 平臺的適配規則自主進行適配。作為 Vercel 親兒子的 Next.js 可以完美適配 Vercel 平臺,通過 Next.js + Vercel,讓開發和部署都能擁有極致的體驗。Vercel 團隊信奉著“吃自己的狗糧”原則,很多應用都是基於自己的工具和平臺開發的。

美中不足

Next.js + Vercel 看起來是如此的完美:通過 Vercel CLI 一鍵部署 Next.js 到 Vercel。另外,Next.js 也能很方便地執行在雲主機上。但是 Vercel 作為國外的 Serverless 平臺,對於國內使用者,總是存在種種難以逾越的限制。如何將 Next.js 完美執行在國內的 Serverless 平臺變得尤為重要。

國內 Serverless 平臺官方在如何讓 Next.js 執行起來的問題上各顯神通。讓 Next.js 在 Serverless 平臺上執行不難,而要做到像 Vercel 一樣的極致部署執行體驗卻很有挑戰。

在嘗試將 Next.js 部署到國內 Serverless 平臺的時候,比如騰訊雲函式、阿里雲函式計算,可能會遇到如下一些坑:

  1. 執行適配困難:Next.js 的執行需要一個 HTTP Server,而事件函式提供的是一個簡單簽名函式,無法直接執行,需要將事件函式模擬成一個近似 HTTP Server 的代理服務;
  2. 程式碼體積過大:一個最簡單的 Next.js 應用的程式碼體積為 245MB 左右,打包壓縮後是 54MB 左右,而函式程式碼體積限制一般是在 50MB 以內(阿里雲函式計算通過 OSS 方式上傳程式碼可以超過 50MB 的限制,但不能超過 100MB)。程式碼體積過大往往帶來一系列副作用:

    a. 程式碼上傳時間長,且容易失敗,部署成本變大(可以通過 NFS 和 Layer 解決);

    b. 依賴更多雲服務,如使用物件儲存服務部署程式碼包,又或者把體積大的 node_modules 目錄上傳到 NFS 服務上,然後掛載到函式上。總之,讓應用架構變複雜;

    c. 冷啟動時間變長,函式在第一次執行的時候,需要先載入遠端的程式碼,如果程式碼包越大,則冷啟動時間越長。

不過,通過騰訊雲的 Web 函式和阿里雲函式計算的 Custom Runtime,可以解決第一個問題,因為它允許我們執行一個真正的 HTTP Server。而第二個問題要困難很多,雖然其中部分問題可以通過一定手段緩解,比如冷啟動可以通過預置併發解決,但是又會讓執行成本變得難以接受。所以解決問題的根本還是在程式碼體積上。

為什麼 Next.js 專案程式碼體積大

為了分析這個問題,我們需要先了解 Next.js 的架構。Next.js 是一種 React 的服務端渲染框架,整合度極高,框架自身整合了 Webpack、SWC、Babel、Express 等,使得開發者僅依賴 Next、React 和 React-dom 就可以方便地構建自己的 SSR React 應用,我們甚至可以不用關心路由。Next.js 的高度整合性,易於實現程式碼分割、路由跳轉、熱更新、服務端渲染和前端渲染。

在 Next.js 專案中,不僅僅包含了執行時所需要的依賴,還包含了本地開發、構建所需要的開發時依賴,而且開發時依賴體積又大。我們常見的解決方案是簡單粗暴打包所有的依賴,從而導致 Next.js 專案程式碼偏大。

Vercel 官方如何打包部署 Next.js

Vercel 官方打包部署 Next.js 的方案比較複雜。Vercel 平臺底層基礎設施整合了 AWS Lambda,Next.js 本質是部署在 AWS Lambda 平臺上。為了能讓 Next.js 在 Lambda 上執行,Vercel 官方提供了一個專門用於構建 Next.js 專案的構建器:@vercel/next。該構建器的邏輯大致是把 Next.js 中的每一個 API 和服務端渲染的頁面都分別構建輸出為一個函式,這一系列函式都歸屬與 Vercel 平臺上的一個應用。這樣就保證了每個函式的程式碼體積足夠小。

Next.js 打包部署到國內Serverless 平臺最佳實踐

解決函式適配困難:我們可以通過 Web 函式或者 Custom Runtime 來解決(不推薦使用自定義映象的方式,因為自定義映象冷啟動很嚴重),並在其中執行一個 HTTP Server,且簡單適配 Next.js,這裡在 Next.js 官方有示例。

解決程式碼包體積過大問題:可以剔除掉執行時不需要的可選依賴和開發依賴,剔除方式如下:

npm install --omit optional --omit dev
 # 或者
yarn install --ignore-optional --prod

說明:因為 swc 構建工具是通過可選依賴安裝的,在執行時不需要,所以我們需要把可選依賴也剔除。

通過以上方式構建的程式碼體積由原來的 54MB 減小到了 18MB。另外,值得一提的是阿里雲函式計算 Custom Runtime 內建的 Node.js 版本為 v10.16.2,而 Next.js 最新版本要求必須是 Node.js 12.22.0+。所有直接部署在函式計算的 Custom Runtime 上的 Next.js 應用無法執行,此時我們需要自行將 Node.js 的二進位制下載到我們自己的程式碼中(也可以通過 Layer 實現),然後指定新的 PATH 環境變數。

如果每次都需要做上面的操作會很繁瑣,而且還需要自己寫適配入口程式碼,以及 Web 函式和 Custom Runtime 所必須的 bootstrap 檔案,且該檔案必須擁有可執行許可權,在執行時額外安裝新版 Node.js。其實這些能力在 Cloud Studio (https://cloudstudio.net/) 雲開發平臺中已經內建提供了。針對一個原生的 Next.js 應用,使用 Cloud Studio 雲開發平臺可以一鍵部署到騰訊雲函式或者阿里函式計算,對業務程式碼零侵入,零門檻,只需如下幾步:

  1. 登入進入 Cloud Studio 的 Dashboard 頁面。

https://cloudstudio.net/

  1. 選擇 Next.js 模板,並建立一個工作空間。

  1. 切換到 Cloud Studio 雲部署套件檢視。

  1. 選擇騰訊雲部署選項,並微信掃碼登入。

  1. 點選【開始部署】按鈕,一鍵部署 Next.js 應用。

  1. 點選【訪問】按鈕,即刻預覽部署後的效果。

說明:同樣的 Next.js 應用,無需做任何修改,採用上述一樣的步驟一鍵部署。

Cloud Studio 是基於瀏覽器的整合式開發環境(IDE),為開發者提供了一個永不間斷的雲端工作站。其包含程式碼高亮、自動補全、Git 整合、終端等 IDE 的基礎功能,同時支援實時除錯、外掛擴充套件等,可以幫助開發者快速完成各種應用的開發、編譯與部署工作。使用者在使用 Cloud Studio 時無需安裝,隨時隨地開啟瀏覽器就能使用。

目前 Cloud Studio 支援部署到騰訊雲函式和阿里雲函式計算,並且支援 15+ 前後端框架的一鍵部署。

寫在最後

從開始的胡亂打包,到後面的精緻打包,讓程式碼體積變小,可以幫助大家避免一系列的坑。至於我們為什麼不採用像 Vercel 那樣的極致方案,原因有三點:實現成本太高、對 Next.js API 深度依賴,維護成本高和構建成多個函式管理成本極大(我們不可能像 Vercel 一樣提供一個高階平臺)。通過 Cloud Studiohttps://cloudstudio.net/雲開發平臺,我們可以一鍵部署 Next.js 等流行框架,對框架零改造。

相關文章