尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

devalue核心原理解析:深入源码剖析uneval与stringify的3大实现机制

devalue核心原理解析:深入源码剖析uneval与stringify的3大实现机制 devalue核心原理解析深入源码剖析uneval与stringify的3大实现机制【免费下载链接】devalueGets the job done when JSON.stringify cant项目地址: https://gitcode.com/gh_mirrors/de/devaluedevalue 是一个高性能的 JavaScript 数据序列化库一句话定位当JSON.stringify搞不定时它就出场了。它补齐了 JSON 序列化的全部短板——循环引用、undefined、BigInt、Date、RegExp、Map/Set、Typed Array 等同时提供uneval与stringify/parse两套实现机制兼顾紧凑输出与安全反序列化。本文带你深入源码看懂它的设计精髓。devalue 是什么解决 JSON.stringify 的 7 个痛点原生JSON.stringify遇到下面这些值就会哑火或直接丢失信息痛点JSON.stringify 的表现devalue 的处理循环引用抛出异常自动生成引用变量undefined静默丢弃用哨兵值-1标记NaN/Infinity/-0变null用负数哨兵精确保留Date/RegExp退化为字符串带类型标签往返BigInt直接报错[BigInt,123]编码Map/Set/ Typed Array变{}结构化序列化/script注入XSS 风险全量字符转义项目入口非常干净四个 API 一次导出index.js 中的uneval、stringify、stringifyAsync、parse/unflatten。更多特性清单见 README.md。两条路线uneval 与 stringify 如何选择理解 devalue先要分清它的两条序列化路线——这是整个源码的骨架uneval代码路线直接把值变成一段可eval的 JavaScript 代码输出最紧凑、无需携带解析器。核心实现见 src/uneval.js。stringifyparse数据路线生成标准 JSON 字符串parse负责还原。适合不能执行任意 JS 的场景。核心实现见 src/stringify.js 与 src/parse.js。 选型口诀信任边界内、追求体积最小 →uneval跨安全边界、要求纯数据解析 →stringify/parse。uneval 源码剖析两遍遍历生成最紧凑代码uneval的实现是一个经典的两遍扫描设计分三步走第 1 步walk 统计引用次数。递归遍历整棵值树用Map给每个对象计数src/uneval.js。出现次数大于 1 的对象才会被提升为具名变量——这保证了输出里没有任何冗余拷贝。第 2 步stringify 递归生成表达式。普通对象输出字面量被提升的对象直接输出变量名src/uneval.js。每个内置类型都有专门的紧凑写法Date用new Date(毫秒数)、ArrayBuffer用new Uint8Array([...]).buffer、Typed Array 共享 buffer 时自动.subarray切片。第 3 步IIFE 包装。只要有被提升的变量最终就包装成自执行函数(function(a){a.messagehello;a.selfa;return a}({}))变量名由 62 个字母数字的进制法生成冲突保留字时自动补0src/uneval.js。两个细节值得新手学习稀疏数组启发式遇到洞很多的大数组时会比较Array(n)字面量与Object.assign(Array(n),{...})的字节成本选更省的一种src/uneval.js。参数上限保护变量超过 65534 个时改用单一数组参数 解构规避引擎函数参数上限src/uneval.js。stringify 源码剖析扁平化数组 负数哨兵stringify的思路完全不同先把值扁平化flatten成一个数组再JSON.stringify输出。这个扁平化数组就是数据库值与值之间全部用下标互相引用——循环引用天然被解决。哨兵值设计是精华所在。JSON 数字可以是负数而合法下标是非负整数于是 devalue 用负数段标记特殊值src/constants.js哨兵含义-1undefined-2稀疏数组的洞-3/-4/-5NaN/Infinity/-Infinity-6-0-7稀疏数组编码头flatten函数src/stringify.js的工作流程非常清晰先查表去重indexesMap 记录值 → 下标遇到重复引用直接返回已有下标这是循环引用和共享引用被消除的关键哨兵快路径NaN、-0等特殊数字立即返回负数不占表位类型标签编码非 JSON 原生类型编码成[类型名, 载荷]数组如[Date,2024-01-01T00:00:00.000Z]、[ArrayBuffer,base64...]base64 编解码见 src/base64.js普通对象直接输出 JSON 对象属性值替换为下标保证整个输出仍是合法 JSON可以嵌进更大的 JSON 文档里。还原端parse/unflatten则是一个带缓存的递归hydratesrc/parse.jshydrated数组充当已恢复记忆遇到已恢复的下标直接返回——自引用对象因此不会死循环。parse只做JSON.parse后调用unflatten后者还能独立处理devalue 数据只是大 JSON 一部分的场景。安全设计XSS 缓解与错误定位对做服务端渲染的开发者devalue 的安全设计是隐藏福利全量字符转义、换行、行分隔符\u2028等全部转义为 Unicode 序列src/utils.js、src/utils.js/script再也无法从字符串中逃出去彻底堵住预加载脚本注入的 XSS 通道拒绝危险对象函数、Symbol、非 POJO、含__proto__键的对象一律抛错从源头禁止序列化会执行的东西精确的错误定位序列化失败抛出的DevalueError携带.path如.object.array[0]快速定位数据里哪颗钉子出了问题src/utils.js。进阶机制operations 可插拔接口devalue 5.9 起把所有值内省动作抽象成operations接口src/operations.jsstringify从不直接触摸值一切读取都经过typeOf、get、entriesOf等操作函数。这带来两个高级能力无副作用序列化用属性描述符替代直接读取getter 和 Proxy trap 永远不会被触发跨运行时序列化把值换成另一个 JS 运行时node:vm、WASM 引擎、远程进程里的句柄算法主体零改动。parse端有镜像的ParseOperations接口fromXxx与toXxx一一对应还支持跨 realm 恢复用目标 realm 的Date/Map构造函数重建对象。快速上手3 行代码接入 devalueimport { stringify, parse, uneval } from devalue; const data { list: new Set([1, 2, d new Date()]) }; const s stringify(data); // [{list:[Set,1,2,7]}...] parse(s); // 原样恢复含 Date uneval(data); // new Set([1,2,new Date(1719999999999)])自定义类型只需传入 reducer/reviver 两个字典例如把Vector序列化为[x, y]再还原为实例完整示例见 README.md 的 Custom types 一节。写在最后从 devalue 学到的 3 个工程思想负数哨兵复用数字空间——不破坏 JSON 合法性的前提下编码额外状态比加字段更省两遍遍历先统计后决策——uneval先数引用次数再决定提升策略是全局信息优于局部贪心的典范把内省动作接口化——operations让核心算法与具体值的访问方式解耦才支撑起跨运行时这种看起来不可能的扩展。源码总量不足千行却把序列化领域的边界情况稀疏数组、-0、子数组视图、引擎参数上限打磨得一干二净非常适合作为阅读小型 Node 库源码的入门范本。【免费下载链接】devalueGets the job done when JSON.stringify cant项目地址: https://gitcode.com/gh_mirrors/de/devalue创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表