昨天我们遗留了一个问题,在 malita dev
启动之后,修改配置之后,虽然页面有重新加载,但是配置数据的变更并没有被真正的覆盖到原来的逻辑,原因是我们仅仅重启了我们的浏览器端,但是并没有重启我们的服务端。
希望朋友们能够牢记这个问题,时刻提醒自己你当前编写的代码,最终是运行在服务端还是浏览器端,是编译时还是运行时,这个会让我们的框架在后续开发调试中减少非常多的异常,比如在服务端用 window 对象之类的。
服务端重启
常规的服务端重启,会采用 kill 子进程再重新启动的方式,如
import { fork } from 'child_process'; const run = ({scriptPath})=>{ const child = fork(scriptPath); child.on('message', (data: any) => { if (data === 'RESTART') { child.kill(); run({ scriptPath }); } process.send?.(data); }); return child; } 复制代码
这有个问题,我们重启服务的时候,要重启所有的流程。
通过分析,我们的项目中,其实修改配置文件促发的重启,我们只是要重新构建主入口和html,因此我们将这两个生命周期单独提取出来。
const buildMain = async ({ appData }: { appData: AppData }) => { // 获取用户数据 const userConfig = await getUserConfig({ appData, sendMessage }); // 获取 routes 配置 const routes = await getRoutes({ appData }); // 生成项目主入口 await generateEntry({ appData, routes, userConfig }); // 生成 Html await generateHtml({ appData, userConfig }); } 复制代码
接下来解决就简单了,我们只要在监听到 REBUILD 的时候,重新执行一下 buildMain 就可以,这将会触发重新生成主入口文件,然后主入口文件变更,会触发 esbuild 重新构建,向浏览器端发送 reload
事件,完成服务端和客户端的同时重启。
修改 getUserConfig
,将 const malitaServe = createServer(app);
传进去
const userConfig = await getUserConfig({ - appData, sendMessage + appData, malitaServe }); 复制代码
我们主要是使用它来触发事件
onRebuild: (err, res) => { if (err) { console.error(JSON.stringify(err)); return; } + malitaServe.emit('REBUILD', { appData }); } 复制代码
然后在 dev 中监听这个事件
malitaServe.on('REBUILD', async ({ appData }) => { await buildMain({ appData }); }) 复制代码
编写完成之后,发现一个“奇怪”的表现,整个构建重启环节都没有问题,你可以尝试在每个环节中查看数据变化,都是正确的,包括 userConfig 但是实际页面并没有变化,主入口和 html 也重新构建了,但是结果却是错的。
config = require(path.resolve(appData.paths.absOutputPath, 'malita.config.js')).default; 复制代码
最终定位排查问题,是 require
的时候会读取缓存。我们把缓存删了,强制读取最新的文件内容。
const configFile = path.resolve(appData.paths.absOutputPath, 'malita.config.js'); delete require.cache[configFile]; config = require(configFile).default; 复制代码
效果演示
感谢阅读,由于我在网上搜索如何重启 node 服务,找到的都不是我要的东西,我想要的是“如何重启”搜出来的都是“如何保活”,所以我就把这内容当作单独的一篇文章了。
预告:明天的内容是 Proxy 代理和本地 Mock 数据