Express服務(wù)器端代碼熱加載的實(shí)現(xiàn)代碼
創(chuàng)建項目
首先,創(chuàng)建一個 express 的創(chuàng)建項目
mkdir your-project cd your-project npm init -y npm install --save express
添加 src/main.js
// ./src/main.js const express = require('express'); function bootstrap() { const app = express(); app.all('*', (req, res) => { res.send(`hello, world!\n`); }); app.listen(3000, function () { console.log('listen on http://localhost:3000'); }); } bootstrap();
修改 package.json
,增加 start 命令
{ ... "scripts": { "start": "node src/main.js" }, ... }
啟動服務(wù)
npm run start
OK,這就啟動了一個基本的 express app 了。
但是,每次修改代碼后,都需要手動重新啟動服務(wù),對于開發(fā)來說,這是個非常煩人的工作,下面讓我們一步一步來優(yōu)化它。
使用 nodemon 自動重啟
使用 nodemon 檢測文件變動,重啟服務(wù),這種方式很簡單,不需要修改現(xiàn)有代碼。
安裝 nodemon
npm install --save-dev nodemon
修改 package.json,增加 dev 命令,使用 nodemon 啟動,其它都不用改
{ ... "scripts": { "dev": "nodemon src/main.js", "start": "node src/main.js" } ... }
配置好后,使用 npm run dev
啟動服務(wù),nodemon 會檢測文件改動自動重啟服務(wù)器,這樣你不用再頻繁的重啟服務(wù),歡快地去寫代碼了。
如果你需要排除一些文件的監(jiān)控,比如僅檢測 src
目錄下的 js 文件,并忽略測試代碼,可以添加 nodemon 的配置文件 nodemon.json
{ "watch": ["src/"], "ext": "js", "ignore": ["*.test.js", "*.spec.js"] }
如上所示,nodemon 的使用非常簡單,配合 ts-node 它還能支持 typescript,已經(jīng)能滿足大多數(shù)用戶的使用場景了。
不過,當(dāng)項目變的越來越大,每次改動一個地方就重新啟動服務(wù)就變得有點(diǎn)麻煩了。
使用 webpack HMR 實(shí)現(xiàn)模塊熱加載
webpack 的 HMR 功能會通知到哪些文件發(fā)生了變化需要重新加載,這個功能被廣泛用在前端開發(fā)框架中,修改代碼后立即刷新頁面,其實(shí)它也還可以被用在服務(wù)器端代碼的加載過程中,讓我們來看看如何實(shí)現(xiàn)。
首先,添加依賴包
npm install --save-dev webpack webpack-cli webpack-node-externals run-script-webpack-plugin rimraf
添加兩個新文件,用來測試熱加載
// ./src/count.js let n = 0; export function inc() { n++; return n; }
// ./src/hello.js export function greet(name = 'World') { return `Hello, ${name}!`; }
修改 src/main.js
,引入上面的文件,并添加響應(yīng)熱加載的代碼,由于使用了 webpack,現(xiàn)在可以在代碼中使用 ES6 的 import 了,改動后的 main.js
代碼如下:
// ./src/main.js import express from 'express'; import { createServer } from 'http'; import { inc } from './count'; import { greet } from './hello'; function bootstrap() { const app = express(); // 默認(rèn)情況下 express 會自動創(chuàng)建 server,這里手動創(chuàng)建 server // 是為了在后面調(diào)用 server.close() 關(guān)閉舊的服務(wù) const server = createServer(app); app.all('*', (req, res) => { const n = inc(); res.send(`${n}: ${greet('bob')}\n`); }); server.listen(3000, function () { console.log('listen on http://localhost:3000'); }); if (module.hot) { module.hot.accept(); module.hot.dispose(() => server.close()); } } bootstrap();
修改 package.json
{ ... "scripts": { "dev": "rimraf dist && webpack --config webpack-hmr.config.js --watch", "build": "rimraf dist && webpack --config webpack.config.js", "start": "node dist/server.js" }, ... }
添加 build 的 webpack 配置文件 webpack.config.js
// ./webpack.config.js const path = require('path'); const nodeExternals = require('webpack-node-externals'); module.exports = { mode: 'production', entry: ['./src/main.js'], target: 'node', externals: [nodeExternals()], resolve: { extensions: ['.js'], }, output: { path: path.join(__dirname, 'dist'), filename: 'server.js', }, };
添加 dev 的 webpack 配置文件 webpack-hmr.config.js
// ./webpack-hmr.config.js const webpack = require('webpack'); const path = require('path'); const nodeExternals = require('webpack-node-externals'); const { RunScriptWebpackPlugin } = require('run-script-webpack-plugin'); module.exports = { mode: 'development', entry: ['webpack/hot/poll?100', './src/main.js'], target: 'node', externals: [ nodeExternals({ allowlist: ['webpack/hot/poll?100'], }), ], resolve: { extensions: ['.js'], }, plugins: [ new webpack.HotModuleReplacementPlugin(), new RunScriptWebpackPlugin({ name: 'server.js', autoRestart: false }), ], output: { path: path.join(__dirname, 'dist'), filename: 'server.js', }, };
開發(fā)模式下,我們使用 npm run dev
啟動服務(wù),啟用熱加載功能。
接下來,我們來測試一下熱加載
curl http://localhost:3000/
輸出
1: Hello, boy!
當(dāng)我們修改 main.js
后,服務(wù)器端僅需要重新編譯并加載 main.js
一個文件
... asset server.js 46.1 KiB [emitted] (name: main) asset main.44ac5c7bc9372be2efa5.hot-update.js 2.58 KiB [emitted] [immutable] [hmr] (name: main) asset main.44ac5c7bc9372be2efa5.hot-update.json 28 bytes [emitted] [immutable] [hmr] Entrypoint main 48.7 KiB = server.js 46.1 KiB main.44ac5c7bc9372be2efa5.hot-update.js 2.58 KiB runtime modules 23.5 KiB 9 modules cached modules 4.69 KiB [cached] 7 modules ./src/main.js 677 bytes [built] [code generated] webpack 5.89.0 compiled successfully in 32 ms [HMR] Updated modules: [HMR] - ./src/main.js [HMR] Update applied. listen on http://localhost:3000/
再次訪問服務(wù)
curl http://localhost:3000/
輸出
2: Hello, boy!
我們可以看到,count.js
中的計數(shù)并沒有重置
再試下修改 hello.js
,webpack 會重新編譯并加載 hello.js
,因?yàn)?nbsp;main.js
引用了 hello.js
,所以雖然不會重新編譯 main.js
,但是它也會被重新加載。
... asset server.js 46.1 KiB [emitted] (name: main) asset main.56dcdc90891aac75fe1c.hot-update.js 1.37 KiB [emitted] [immutable] [hmr] (name: main) asset main.56dcdc90891aac75fe1c.hot-update.json 28 bytes [emitted] [immutable] [hmr] Entrypoint main 47.5 KiB = server.js 46.1 KiB main.56dcdc90891aac75fe1c.hot-update.js 1.37 KiB runtime modules 23.5 KiB 9 modules cached modules 5.28 KiB [cached] 7 modules ./src/hello.js 72 bytes [built] [code generated] webpack 5.89.0 compiled successfully in 65 ms [HMR] Updated modules: [HMR] - ./src/hello.js [HMR] - ./src/main.js [HMR] Update applied. listen on http://localhost:3000/
再次訪問服務(wù)
curl http://localhost:3000/
輸出
3: Hello, boy!
可以看到,count.js
中的計數(shù)任然沒有被重置,說明只要不修改 count.js
及其依賴項,count.js
就不會被重新加載。
發(fā)布到生產(chǎn)環(huán)境
生產(chǎn)環(huán)境下,我們不需要熱加載功能,那么我們可以運(yùn)行 npm run build
構(gòu)建代碼,然后再運(yùn)行 npm start
,使用構(gòu)建后的代碼啟動服務(wù),這樣優(yōu)先保證線上環(huán)境的性能。
動態(tài)加載目錄的問題
另外還有一個常見的問題,有時候我們需要動態(tài)的加載某個目錄下的所有文件,這個可以用 await import 來加載模塊來完成。
讓我們來改一下 main.js
,將 bootstrap
改成 async
方法,再增加一個 loadControllers
方法
// ./src/main.js // ... import { readdir } from 'fs/promises'; import { resolve } from 'path'; async function bootstrap() { // ... const server = createServer(app); // 動態(tài)加載 controllers 目錄下的所有文件 await loadControllers(app); app.all('*', (req, res) => { /* ... */ }); //... } async function loadControllers(app) { try { // 注意,這里應(yīng)該是掃描 src 目錄,而不是 dist 目錄 const files = await readdir(resolve('./src/controllers')); for (let i = 0; i < files.length; i++) { const file = files[i]; const name = file.substring(0, file.length - 3); // remove ext '.js' const module = await import('./controllers/' + name); app.use(`/${name}`, module.default); } } catch (err) { console.error(err); } } bootstrap();
提示
遍歷目錄文件的時候需要使用源文件的目錄,而不能使用文件的相對目錄
錯誤的代碼
// 編譯后的代碼會提示找不到目錄 ./dist/controllers readdir(path.join(__dirname, 'controllers'));
正確的代碼
readdir(path.resolve('./src/controllers'));
再添加一個 controller 文件 src/controllers/posts.js
// ./src/controllers/posts.js import { Router } from 'express'; const router = Router(); router.get('/', (req, res) => { res.send([ { id: 1, title: 'post 1', content: 'content of the post', }, ]); }); export default router;
測試下新加的 controller
curl http://localhost:3000/posts
輸出
[{"id":1,"title":"post 1","content":"content of the post"}]
這種方式存在一個問題,每次都要去掃描 src 目錄,導(dǎo)致部署的時候還需要將 src 目錄復(fù)制到服務(wù)器,而這些 src 目錄下的文件除了提供一個 filename 就沒有其它作用了,我認(rèn)為這不是一個好的代碼。
如果要避免這種隱式的動態(tài)加載,可以將它改成如下代碼:
// 顯示聲明有哪些 controllers const controllerNames = [ 'posts', ]; async function loadControllers(app) { const controllers = controllerNames.map((name) => ({ name })); for (let i = 0; i < controllers.length; i++) { const controller = controllers[i]; try { const module = await import(`./controllers/${controller.name}`); controller.router = module.default; } catch (err) { // console.error(err); } } controllers.forEach(({ name, router }) => { if (router) { const path = `/${name}`; app.use(path, router); console.log(`mount controller '${name}' on '${path}'`); } else { console.error(`cannot find controller '${name}'`); } }); }
以上就是Express服務(wù)器端代碼熱加載的實(shí)現(xiàn)代碼的詳細(xì)內(nèi)容,更多關(guān)于Express代碼熱加載的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Node.js連接MongoDB數(shù)據(jù)庫產(chǎn)生的問題
Node.js是使用JavaScript 編寫的可以運(yùn)行在服務(wù)端的JS語言。node.js和mongodb碰撞會產(chǎn)生一系列問題,下面通過本文給大家分享Node.js連接MongoDB數(shù)據(jù)庫,需要的的朋友參考下2017-02-02npm安裝的全局包/工具不能使用,不是內(nèi)部/外部命令的解決方法
這篇文章主要給大家介紹了關(guān)于npm安裝的全局包/工具不能使用,不是內(nèi)部/外部命令的解決方法,文中通過圖文將解決的方法介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03node express如何實(shí)現(xiàn)json轉(zhuǎn)Excel
這篇文章主要介紹了node express如何實(shí)現(xiàn)json轉(zhuǎn)Excel問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08