惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
量子位
H
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
MongoDB | Blog
MongoDB | Blog
小众软件
小众软件
爱范儿
爱范儿
博客园 - 【当耐特】
Vercel News
Vercel News
S
SegmentFault 最新的问题
M
MIT News - Artificial intelligence
F
Fortinet All Blogs
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
博客园 - 叶小钗
博客园_首页
V
Visual Studio Blog
宝玉的分享
宝玉的分享
B
Blog
MyScale Blog
MyScale Blog
C
Check Point Blog
博客园 - 三生石上(FineUI控件)
L
LangChain Blog
V
V2EX

OhYee 博客

小鹏辅助驾驶测评|OhYee 博客 小鹏非支持手机开启自动解锁|OhYee 博客 使用函数计算实现 301 重定向|OhYee 博客 针对 HTML 内容使用 Ant Design 图片弹框|OhYee 博客 博客进程泄露及僵尸进程解决|OhYee 博客 蓝易云服务器体验|OhYee 博客 SSH 调起本地 VSCode|OhYee 博客 【2022 秋招内推】阿里云后端研发工程师|OhYee 博客 使用函数计算获取 IP 地址信息|OhYee 博客 正确获取客户端 IP/HTTP Header 也可能重复|OhYee 博客 评测 Oculus Quest2 及 BigScreen|OhYee 博客 如何优雅地贴 gist 代码|OhYee 博客 Linux 精细化文件权限|OhYee 博客 VSCode 容器开发环境|OhYee 博客 Clash 的不兼容更新排查|OhYee 博客 Zeek 导出 PCAP|OhYee 博客 记一次 ssh 配置问题|OhYee 博客 Git Commit 规范化工具|OhYee 博客 谈谈《星之卡比-探索发现》|OhYee 博客 VSCode 快捷键绑定 Shell 命令|OhYee 博客 ASN.1 语法及 X.509 证书格式解析解析|OhYee 博客 腾讯企业邮箱忽略 MX 记录发信|OhYee 博客 Chrome/Edge 标签组插件|OhYee 博客 【应届内推】阿里云后端研发工程师|OhYee 博客 损坏的 Typecho 备份处理为 JSON|OhYee 博客 VS Code VIM 插件高效使用|OhYee 博客 SSH 正反向代理|OhYee 博客 Let's Encrypt 根证书过期引发的问题|OhYee 博客 OpenWRT 忽略内核依赖|OhYee 博客 V2Ray + TLS + WS(WSS) Docker 部署|OhYee 博客
NextJS 热重载保留状态|OhYee 博客
2022-06-23 · via OhYee 博客

这是一篇最后编辑于 4 年前 的文章,其内容可能与目前实际情况差异较大,请注意甄别

NextJS 热重载保留状态

虽然 Next.js 不仅是一个不错的前端服务端框架,在极端情况下也可以用作后端服务。通过 API Router,可以在 pages/api/ 目录下,添加相应的代码。对于没必要前后端分离的小型项目,前后端合在一起在部署可以提升开发效率。

众所周知,动态语言做开发,由于热重载的存在,每次改动代码时服务端会自动重启,会为开发者带来极佳的开发体验。通常而言,对于前端项目,由于 Web 的特性,页面本身不存在状态,因此热重载基本不存在副作用(Next.js 对于已经加载后的页面,目前也已经可以保留上次渲染的 state)。
而对于后端而言,服务往往是有状态的。比如后端服务需要与数据库建立连接,如果每次热重载都新建一条连接,并且由于热重载缺乏析构函数,相当于每次代码变动都会建立一条不会析构的数据库连接,这些连接以类似野指针的形式存在,会占用大量的资源

zhblogs 的开发中,使用了 LokiJS 作为内嵌数据库,以确保整个系统可以结合成为一个整体,方便部署和开发。需要确保数据可持久化更可控,因此这里需要自己实现的定时函数执行数据持久化。在热重载的加成下,5 min 一次的数据持久化飞速就变成了每秒一次,并且由于每个定时任务对应的实际上是不同的 LokiJS 实例,因此真正的数据修改可能会被之前已成为野指针的内存数据覆盖掉,导致真正的改动无法生效。

确定原因后,可以从两个方向解决

  • 实现一个缓存保存数据库实例,确保程序中只有一个在运行
  • 实现析构函数,在重载时销毁上一个实例

由于 JavaScript 本身不存在析构函数(甚至搜这个概念很多人都在讨论 GC,很少有生命周期的讨论),因此这里没法实现。那么另一个思路就是使用一个缓存机制。热重载并不是重启服务,而是重新加载用到的代码,因此在这个过程中 global 是不变的,因此只需要在 global 上写入一个全局变量,在初始化数据库前检查是否已设置该变量。如果未设置则正常初始化,否则返回之前的变量内容。
采用这个机制,也可以实现析构函数,不过这里不进一步讨论

相关代码如下

declare module globalThis {
  let loki: DatabaseLoki;
}
export function newLokiCached(path: string){
  /**
     * using cache in development mode
     * see https://github.com/vercel/next.js/tree/canary/examples/with-mongodb
     */
  if (!globalThis.loki) { 
    globalThis.loki = new DatabaseLoki(path);
  } else {
    log.log("使用已连接的 loki 数据库缓存");
  }
  
  return globalThis.loki;
}