:TypeScript + Nodemon 開發(fā)自定義 Server 的完整指南)
Next.js 自定義服務器實戰(zhàn)TypeScript Nodemon 開發(fā)自定義 Server 的完整指南【免費下載鏈接】next.jsThe React Framework項目地址: https://gitcode.com/GitHub_Trending/next/next.js本篇基于 Next.js 官方示例 custom-server 展開講解如何在 Next.js 項目中用 TypeScript 同時編寫服務端與客戶端代碼并通過 Nodemon 實現服務器代碼的熱重載。讀完本文你將掌握next()包裝 API 的核心用法getRequestHandler、prepare、服務端獨立的 tsconfig 編譯策略以及開發(fā)態(tài)server.ts與生產態(tài)dist/server.js兩種入口的切換機制能夠把 Next.js 嵌入到任意自定義 Node.js 服務中。一、自定義服務器能解決什么問題Next.js 默認提供自己的 Node.js 服務進程next dev/next start。但在真實工程中經常需要接管 HTTP 服務層接入已有的網關或監(jiān)聽邏輯、在服務端做統(tǒng)一鑒權/日志/中間件、復用公司內部的服務框架。自定義服務器custom server就是讓開發(fā)者自己創(chuàng)建 HTTP 服務再把請求委托給 Next.js 去渲染。官方示例examples/custom-server的定位見 README在服務器端和客戶端同時使用 TypeScript并用 Nodemon 實現服務器代碼的實時熱重載同時不影響 Next.js 自身的 universal 代碼熱更新。兩個關鍵結論先擺出來開發(fā)態(tài)入口是server.ts生產態(tài)入口是dist/server.js編譯產物目錄dist應加入.gitignore。二、用 create-next-app 腳手架啟動示例倉庫 README 給出了三種包管理器的引導命令任選其一npx create-next-app --example custom-server custom-server-appyarn create next-app --example custom-server custom-server-apppnpm create next-app --example custom-server custom-server-app執(zhí)行后會在custom-server-app目錄下生成完整示例項目結構如下對應倉庫中的目錄examples/custom-server/ ├── app/ # App Router 路由 │ ├── layout.tsx # 根布局 │ └── b/page.tsx # /b 頁面 ├── pages/ # Pages Router 路由 │ ├── index.tsx # 首頁含導航鏈接 │ └── a.tsx # /a 頁面 ├── server.ts # 開發(fā)態(tài)服務器入口 ├── nodemon.json # Nodemon 配置 ├── tsconfig.json # 客戶端 通用 TS 配置 ├── tsconfig.server.json # 服務端專屬 TS 配置 └── package.json值得注意的是這個示例同時保留了app/與pages/兩套路由目錄。首頁 pages/index.tsx 中用next/link同時鏈接到 Pages Router 的/a和 App Router 的/bimport Link from next/link; export default function Home() { return ( ul li Link href/a/a (Pages Router)/Link /li li Link href/bb (App Router)/Link /li /ul ); }這驗證了自定義服務器對兩種路由體系是透明且通用的——無論頁面來自哪套路由最終都由同一個handle(req, res)處理。三、server.ts自定義服務器的核心代碼逐行解析整個示例的服務端邏輯全部集中在 server.ts共 19 行import { createServer } from http; import next from next; const port parseInt(process.env.PORT || 3000, 10); const dev process.env.NODE_ENV ! production; const app next({ dev }); const handle app.getRequestHandler(); app.prepare().then(() { createServer((req, res) { handle(req, res); }).listen(port); console.log( Server listening at http://localhost:${port} as ${ dev ? development : process.env.NODE_ENV }, ); });逐步拆解const dev process.env.NODE_ENV ! production以NODE_ENV判斷當前是開發(fā)還是生產模式這是 Next.js 自定義服務器的慣例判據。const app next({ dev })調用next默認導出創(chuàng)建一個 Next.js 應用包裝實例。這個實例是連接自定義 HTTP 服務與 Next.js 渲染引擎的橋。const handle app.getRequestHandler()拿到 Next.js 的請求處理函數。任何進入該函數的req/res都會按 Next.js 的路由規(guī)則被渲染頁面、靜態(tài)資源、API 等對開發(fā)者完全透明。app.prepare().then(() { ... })prepare是啟動前置鉤子用于完成 Next.js 內部的初始化加載配置、構建信息等。必須等prepare完成后再開始監(jiān)聽否則首個請求會因內部狀態(tài)未就緒而出錯。createServer((req, res) { handle(req, res); })用 Node 內置http模塊創(chuàng)建服務器把每個請求原樣轉交給handle。端口從process.env.PORT讀取缺省 3000便于部署平臺注入端口。這段代碼的最小骨架可以總結為三行next({ dev })→app.prepare()→handle(req, res)。從源碼看 getRequestHandler 的官方地位在 Next.js 源碼 packages/next/src/server/next.ts 中NextWrapperServer接口明確注釋了“這里的成員是自定義服務器的公開 API改動時需要考慮向后兼容”interface NextWrapperServer { // NOTE: the methods/properties here are the public API for custom servers. // Consider backwards compatibilty when changing something here! options: NextServerOptions ... getRequestHandler(): RequestHandler prepare(serverFields?: ServerFields): Promisevoid close(): Promisevoid ... }同一文件中還定義了一個warnDeprecatedCustomServerMethod機制next.ts#L108-L114像render、renderToHTML、renderError、logError、revalidate等舊式方法在自定義服務器場景下已被標記廢棄調用時會輸出一次性警告統(tǒng)一引導開發(fā)者使用app.getRequestHandler() 自行調整 parsed URL的方式。也就是說示例中只暴露一個handle函數的寫法正是官方推薦的現代姿勢——不要依賴那些細粒度的 render 方法。四、TypeScript 雙配置客戶端與服務端各管一攤示例中最有工程價值的設計是兩套 tsconfig 分工這解決了瀏覽器代碼與 Node 服務器代碼模塊格式不同的矛盾。客戶端配置 tsconfig.jsontsconfig.json 負責頁面、組件等會被 Next.js 編譯的代碼{ compilerOptions: { target: es5, lib: [dom, dom.iterable, esnext], allowJs: true, skipLibCheck: true, strict: false, forceConsistentCasingInFileNames: true, noEmit: true, esModuleInterop: true, module: esnext, moduleResolution: node, resolveJsonModule: true, isolatedModules: true, jsx: react-jsx, incremental: true, plugins: [{ name: next }], strictNullChecks: true }, include: [next-env.d.ts, **/*.ts, **/*.tsx, .next/types/**/*.ts], exclude: [node_modules] }要點noEmit: true類型檢查交給 Next.js 的構建流水線SWCtsc 只做類型校驗不產出文件plugins: [{ name: next }]啟用 Next.js 的 TS 語言服務插件提供路由類型檢查等能力lib包含dom因為客戶端代碼需要瀏覽器 API 類型。服務端配置 tsconfig.server.jsontsconfig.server.json 只編譯服務器入口{ extends: ./tsconfig.json, compilerOptions: { module: commonjs, outDir: dist, lib: [es2019], target: es2019, isolatedModules: false, noEmit: false }, include: [server.ts] }四個關鍵覆蓋項module: commonjsNode.js 服務端直接node dist/server.js運行CommonJS 最穩(wěn)妥無需處理 ESM 加載問題noEmit: falseoutDir: dist真正產出編譯文件到disttarget/lib提升到es2019服務端運行在現代 Node 上不必像瀏覽器那樣回落到es5include: [server.ts]嚴格限定只編譯服務器文件避免把頁面組件也打進dist。這種一個配置管類型、一個配置管編譯的拆分正是 README 標題TypeScript Nodemon中服務端 TypeScript落地的方式。五、Nodemon讓服務器代碼支持熱重載開發(fā)態(tài)下next dev本身會熱更新 Next.js 的頁面代碼但自定義的server.ts屬于純 Node 代碼不在其熱更新范圍內。示例用 Nodemon 補上這塊nodemon.json{ watch: [server.ts], exec: ts-node --project tsconfig.server.json server.ts, ext: js ts }watch: [server.ts]只監(jiān)聽服務器入口文件避免頁面文件變化觸發(fā)不必要的重啟exec檢測到變化時重新執(zhí)行ts-node --project tsconfig.server.json server.ts——用ts-node直接運行 TS 源碼省去開發(fā)態(tài)編譯一步并顯式指定服務端 tsconfig 以保證commonjs模塊格式正確ext: js ts將.js與.ts擴展名都納入重載判斷。由此形成開發(fā)態(tài)的雙層熱更新變更對象負責熱更新的機制頁面/組件pages/、app/Next.js 自身的開發(fā)時 HMR服務器入口server.tsNodemon 重啟進程這正是 README 所說live reload the server codewithout affectingthe Next.js universal code的含義。六、package.json 中的三個腳本package.json 定義了完整的開發(fā)/構建/啟動鏈路{ scripts: { dev: nodemon, build: next build tsc --project tsconfig.server.json, start: cross-env NODE_ENVproduction node dist/server.js } }dev啟動 Nodemon讀取上面的nodemon.json即運行ts-node加載server.ts。開發(fā)時執(zhí)行npm run devbuild兩步串行——先next build產出.next中的頁面與資源再用tsc --project tsconfig.server.json把server.ts編譯成dist/server.jsstart用cross-env跨平臺設置NODE_ENVproduction后運行編譯產物。cross-env作為依賴^7.0.3解決了 Windows 下無法直接用NODE_ENV...內聯環(huán)境變量賦值的問題。依賴方面運行時依賴僅next、react、react-dom、cross-env四個nodemon、ts-node、typescript、types/*全部位于 devDependencies——這與開發(fā)態(tài)跑server.ts源碼、生產態(tài)跑編譯產物的設計完全吻合生產環(huán)境不再需要 TypeScript 工具鏈。七、生產部署注意事項結合 README 與示例代碼部署自定義服務器版 Next.js 應用時有幾條硬性約束入口切換生產環(huán)境運行的是node dist/server.jsstart腳本而不是server.ts。dev與start兩條鏈路必須與build的產物嚴格對應dist目錄忽略README 明確要求將dist加入.gitignore編譯產物不入庫NODE_ENV語義server.ts中dev process.env.NODE_ENV ! production因此任何非production的NODE_ENV如test都會進入開發(fā)模式分支啟動日志也會顯示對應的環(huán)境名。這一點在start腳本中通過cross-env NODE_ENVproduction保證了正確性prepare前置無論怎樣擴展自定義邏輯鑒權中間件、額外路由都要保證它們注冊在app.prepare().then(...)回調內確保 Next.js 初始化完成避免使用廢棄的細粒度方法如前文源碼所示app.render等方法已被廢棄并輸出警告新代碼應始終只使用getRequestHandler()如需攔截/改寫請求應在傳給handle之前調整req或 parsed URL。八、小結這個示例的工程價值examples/custom-server雖只有十余行服務端代碼但它把三件容易踩坑的事給出了官方參考答案API 面自定義服務器只需next({ dev })、prepare、getRequestHandler三個接觸點源碼next.ts中它被明確標注為面向自定義服務器的公開 API類型工程用tsconfig.jsonnoEmit管類型tsconfig.server.jsoncommonjs outDir管編譯雙配置隔離瀏覽器與 Node 的模塊差異開發(fā)體驗Nodemon ts-node 只監(jiān)聽server.ts讓自定義服務代碼獲得與 Next.js 頁面同等的熱重載體驗。當你需要把 Next.js 嵌入自研服務、加統(tǒng)一網關邏輯或復用現有 Node.js 基礎設施時直接以 examples/custom-server 為模板起步即可create-next-app --example custom-server拉取骨架按上文三節(jié)改配置、按 README 約定管理dist產物?!久赓M下載鏈接】next.jsThe React Framework項目地址: https://gitcode.com/GitHub_Trending/next/next.js創(chuàng)作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考