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