实现思路:将启动文件中的 router、proxy 文件分离出主文件,对分离出去的文件实现热更新。关键问题点:
- 如何更新模块代码
- 如何使用新模块处理请求
- 如何释放老模块的资源
如何更新模块代码
引入 `chokidar` 当监听文件修改时,清除该模块缓存,delete require.cache[path]
如何使用新模块处理请求
使用koa框架,app.use 操作会保存旧的 router.js 模块,因此模块更新了,请求依然会使用老模块
// 利用闭包的特性获取最新的router对象,避免app.use缓存router对象
app.use((ctx, next) => router.routes()(ctx, next)).use(router.allowedMethods())
// 监听文件修改重新加载代码
const watcher = chokidar.watch(path.resolve(__dirname, '../www'), { ignored: /index\.js/ })
watcher.on('ready', () => {
logger.info(`will watching at '${path.resolve(__dirname, '../www')}`)
watcher.on('all', (event, file) => {
logger.info(`server-side hot-reload due to: ${file}`)
Object.keys(require.cache).forEach((id) => {
if (/\/www\//.test(id)) {
cleanCache(id)
try {
if (file.endsWith('router.js')) {
router = require('./router')
}
if (file.endsWith('proxies.js')) {
proxies = require('./proxies')
}
} catch (ex) {
console.error(`module ${file} update failed`)
}
}
})
})
})
function cleanCache (module) {
var path = require.resolve(module);
delete require.cache[path];
}
如何释放老模块的资源
让老模块的代码更新后,确保没有对象保持了模块的引用 ,Node.js 会自动为所有模块添加一个引用
function cleanCache(modulePath) {
var module = require.cache[modulePath];
// remove reference in module.parent
if (module.parent) {
module.parent.children.splice(module.parent.children.indexOf(module), 1);
}
require.cache[modulePath] = null;
}
解决了 koa 热更新问题。