尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

jquery-pjax 历史缓存深挖:cacheBackStack 与 cacheForwardStack 双栈设计如何让你秒开上一页

jquery-pjax 历史缓存深挖:cacheBackStack 与 cacheForwardStack 双栈设计如何让你秒开上一页 jquery-pjax 历史缓存深挖cacheBackStack 与 cacheForwardStack 双栈设计如何让你秒开上一页【免费下载链接】jquery-pjaxpushState ajax pjax项目地址: https://gitcode.com/gh_mirrors/jq/jquery-pjaxjquery-pjax 是一个经典的 jQuery 插件公式很简单pjax pushState ajax。它通过局部刷新容器 更新浏览器地址栏让前进/后退按钮在本地缓存中秒开上一页或下一页而无需重新请求服务器。本文带你深挖它的核心——cacheBackStack与cacheForwardStack双栈缓存设计看看浏览器历史是如何被复刻到内存里的。为什么前进后退按钮会卡一下传统 Web 应用中点击后退意味着浏览器重新发起一次完整的 HTTP 请求、解析完整 HTML、重新执行 JS 和 CSS。体验上的停顿就来自这里。jquery-pjax 的思路很巧妙既然 pjax 导航时容器container的内容本来就在页面上那离开某个页面前先把它的 DOM 克隆下来存进内存。之后用户按后退/前进时直接用内存里的 DOM 还原连服务器都不用打扰——这就是秒开的由来。全部实现都在核心文件 jquery.pjax.js 中缓存相关代码集中在文件末尾的三行声明和几个函数里见 jquery.pjax.js。双栈设计三个变量构成历史内存缓存机制由三个变量组成var cacheMapping {} // 状态ID → [容器选择器, DOM克隆] 的内容仓库 var cacheForwardStack [] // 前进栈状态ID数组 var cacheBackStack [] // 后退栈状态ID数组用一张表理解它们的分工变量类型作用cacheMapping对象真正的内容仓库key 是状态 IDvalue 是克隆好的 DOMcacheBackStack数组记录能退到哪些页的 ID 序列cacheForwardStack数组记录能进到哪些页的 ID 序列这正是浏览器历史模型的经典镜像当前页像一根指针左边是后退栈右边是前进栈。pjax 用两个 ID 数组 一个内容字典在内存里 1:1 复刻了这个结构。前进方向cachePush 把上一页存进后退栈当用户通过 pjax 点击链接跳转到新页面时新 URL 会pushState进浏览器历史。与此同时pjax 会调用 cachePush 做三件事存档把当前状态 ID 和容器 DOM 克隆写入cacheMapping并把 ID 压入cacheBackStack——这样后退才有东西可取清空前进栈trimCacheStack(cacheForwardStack, 0)。因为一旦向前推进了一条新历史原来前进可达的页面全部失效和浏览器行为一致对应的缓存也一并删除修剪后退栈把cacheBackStack裁剪到maxCacheLength长度默认 20见 jquery.pjax.js超出的最老条目直接从cacheMapping中删除防止内存无限增长。DOM 克隆由 cloneContents 完成它还有个细节把克隆体里的内联script标记为未执行过这样从缓存还原时脚本仍能再次运行保证页面逻辑正常。后退/前进方向cachePop 的双向搬运用户按浏览器按钮时触发popstate处理函数是 onPjaxPopstate。它先用状态 ID 的先后关系判断方向direction previousState.id state.id ? forward : back然后调用 cachePop 完成一次双向搬运后退back把当前页的 DOM 存入仓库并压入前进栈你以后还要进回来同时把后退栈的栈顶弹出并删除——因为你正退向它退到之后它就不需要缓存了前进forward方向相反当前页压入后退栈前进栈栈顶弹出删除。一句话总结离开哪条路缓存就补到另一条路上正在走的那条路栈顶条目可以删了。这个设计让缓存永远只覆盖未来可能访问的历史而不是所有去过的页面非常省内存。秒开时刻popstate 里先查缓存再决定是否发请求回到 onPjaxPopstate 的关键分支var cache cacheMapping[state.id] || [] var container $(cache[0] || state.container), contents cache[1] if (contents) { // 命中缓存直接 container.html(contents) 还原零网络请求 } else { pjax(options) // 未命中退回正常的 ajax 请求 }命中缓存时pjax 会跳过所有 XHR直接触发pjax:start→pjax:beforeReplace→ 还原 DOM →pjax:end这一串事件注意此时不会触发pjax:send/pjax:complete因为根本没有网络请求。这也是为什么官方文档建议用pjax:send和pjax:complete来驱动 loading 动画见 README.md——缓存命中时动画自然不会出现体验非常干净。另外cloneContents还原前还有一处强制重排container[0].offsetHeightjquery.pjax.js确保浏览器在恢复滚动位置前完成布局避免页面闪跳。缓存大小怎么调maxCacheLength 实战缓存深度由maxCacheLength控制默认20条见 README.md 的选项表。两个典型调法内存敏感场景调小它老缓存被 trimCacheStack 淘汰后会从cacheMapping中彻底删除彻底关闭本地缓存设为0每次前进后退都重新请求服务器。这两条行为都有对应的自动化测试值得看看官方是怎么验证的后退按钮遵守缓存上限test/unit/pjax.js前进按钮遵守缓存上限test/unit/pjax.jsmaxCacheLength 0禁用缓存test/unit/pjax.js测试技巧也很实用监听pjax:beforeSend事件标记是否真的访问了服务器以此区分缓存命中与真实请求。完整走一遍双栈如何随导航变化假设容器为#main依次访问 home → dinosaurs → aliens → 后退到 dinosaurs → 前进到 aliens操作cacheBackStackcacheForwardStack说明初始进入 home[home][]replaceState 建立初始状态pjax 到 dinosaurs[home, dinosaurs][]cachePushhome 存档前进栈清空pjax 到 aliens[home, dinosaurs, aliens][]同上dinosaurs 存档后退到 dinosaurs[home, dinosaurs][aliens]cachePopaliens 挪到前进栈aliens 旧栈位删除前进到 aliens[home, dinosaurs, aliens][]cachePop方向反向栈顶清理每一步cacheMapping里都恰好存着栈上所有 ID 对应的 DOM 克隆后退、前进因此都是纯本地操作。新手上手三行代码启用 pjax理解了缓存原理后启用它其实非常简单$(document).pjax(a, #pjax-container)这行代码为页面内所有链接挂上点击处理指定#pjax-container作为内容容器用法详见 README.md。服务器端只需识别X-PJAX请求头并返回不带布局的局部 HTML前后端配合后即获得完整的真链接 快导航 可用的前进后退体验。总结一张表看懂双栈缓存场景调用函数后退栈前进栈是否发请求点击链接向前跳cachePush压入旧页 ID清空是按后退按钮cachePop(back)弹出栈顶压入当前页 ID否命中时按前进按钮cachePop(forward)压入当前页 ID弹出栈顶否命中时缓存超限trimCacheStack删除最老条目——jquery-pjax 的双栈设计用不到百行代码就在内存里复刻了浏览器的历史模型。理解了cachePush/cachePop/trimCacheStack这三个函数你就掌握了它秒开上一页的全部秘密。如果想亲手验证可以运行测试服务器查看演示页面script/server # 然后打开 http://localhost:4567/演示环境搭建说明见 CONTRIBUTING.md测试入口为 test/run-qunit.js。【免费下载链接】jquery-pjaxpushState ajax pjax项目地址: https://gitcode.com/gh_mirrors/jq/jquery-pjax创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表