Files
blog/themes/Ying/assets/js/modules/mypjax.js
T
zqlit 46edf44904 fix(pjax): 回退 cacheBust —— 恢复重复导航秒出
实测(usj.cc):cacheBust 让每次 PJAX 多付 ~200-380ms 网络 RTT,
用户可感知卡顿;关掉后重复导航走浏览器缓存(实测 1ms)。
新鲜度兜底:① pjax:success 版本同步(部署换指纹自动整页刷一次)
② CDN HTML 缓存 TTL 待调短(控制台配置)。
2026-10-07 23:12:39 +08:00

253 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 定义pjax 预设pjax
(function() {
if (typeof Pjax === 'undefined') {
console.error('Pjax library is not loaded.');
return;
}
var pjax = new Pjax({
// 这里填写页面中改变的部分(和 CSS 选择器用法一样)
selectors: ["title", "#pjax-container"],
// ★ cacheBust 保持 false:重复导航靠浏览器 HTTP 缓存秒出(实测 1ms)。
// 开 true 会给每次 PJAX 加时间戳绕开缓存 → 每次导航多付 ~200-380ms
// 网络往返(2026-10-07 实测,用户可感知的「点 nav 卡一下」)。
// 新鲜度由两层兜底:① 部署换样式表指纹后,下方 pjax:success 的版本
// 同步自动整页刷一次;② CDN 的 HTML 缓存 TTL 应调短(控制台配置)。
cacheBust: false,
elements: "a", // 拦截所有链接
switches: {
"#pjax-container": Pjax.switches.default
}
});
// 将 pjax 实例挂载到 window 以便调试或外部访问
window.pjax = pjax;
console.log("Pjax initialized:", pjax);
})();
// ── 静态资源版本同步(修复「部署后 PJAX 页面样式裸奔」)──
// ─────────────────────────────────────────────────────────────
// 根因:Pjax 只换 title 和 #pjax-container,<head> 里的指纹 CSS
// (style.min.<hash>.css)一旦因部署换了指纹,**旧会话**里所有后续
// PJAX 页面都跑在旧样式上 —— 新 HTML 的类(.ab-* 等)在旧 CSS 里
// 不存在 → 内容裸奔成纯文本(2026-10-07 关于页上线实锤)。
//
// 解法:pjax:success 时事件对象上带有本次请求(pjax 库把 options 的
// 键直接拷到 event 上,options.request = XMLHttpRequest),从
// responseText 解析出新页面的样式表指纹,与当前 document 比对;
// 不一致 → 整页 reload 一次拿全新资源(sessionStorage 保证每次会话
// 至多一次,防异常情况下的循环刷新)。
document.addEventListener("pjax:success", function (e) {
var request = e.request;
if (!request || typeof request.responseText !== "string" || !request.responseText) return;
var fetchedLinks, currentLinks, i, changed = false;
try {
var doc = new DOMParser().parseFromString(request.responseText, "text/html");
fetchedLinks = doc.querySelectorAll('link[rel="stylesheet"]');
currentLinks = document.querySelectorAll('link[rel="stylesheet"]');
} catch (err) { return; }
if (fetchedLinks.length !== currentLinks.length) {
changed = true;
} else {
for (i = 0; i < fetchedLinks.length; i++) {
if (fetchedLinks[i].getAttribute("href") !== currentLinks[i].getAttribute("href")) {
changed = true;
break;
}
}
}
if (!changed) return;
try {
if (sessionStorage.getItem("pjaxVerReload")) return; // 本会话已刷过
sessionStorage.setItem("pjaxVerReload", "1");
} catch (err) { /* 隐私模式等拿不到 sessionStorage:直接刷,概率极低 */ }
console.info("[pjax] 检测到样式表版本变化,整页刷新以加载新资源");
window.location.reload();
});
// UIKit 重新扫描(PJAX 必需)
// ─────────────────────────────────────────────────────────────
// UIKit 只在页面加载时做一次「全量 boot」:它遍历 document.body,把带
// uk-* 属性的元素逐个 connect 成组件。PJAX 换入的 DOM 是我们用
// innerHTML 塞进去的,UIKit 的 MutationObserver 虽然监听了 childList,
// 但因为「换入前属性就已存在」,observer 拿不到 attribute 的新增事件,
// 加上 PJAX 的替换发生在 UIKit 首次 boot 之后,组件就永远绑不上。
//
// 症状:uk-scrollspy 的元素停在 visibility:hidden(动画类的初始态),
// 下折叠的内容永久不可见,滚动也不会出现。
//
// 解法:换页后手动调一次 UIkit.update()。它会从传入节点向上走到
// document.body,再对整棵子树递归 connect —— 正好补上这次 boot。
function reinitUIKit(root) {
if (typeof window.UIkit === 'undefined' || typeof window.UIkit.update !== 'function') return;
var target = root || document.getElementById('pjax-container') || document.body;
try {
window.UIkit.update(target);
} catch (e) {
console.warn('UIkit.update failed:', e);
}
}
// 暴露给页面脚本:动态插入带 uk-* 属性的内容后,调一次即可让 UIKit 生效
window.reinitUIKit = reinitUIKit;
// pjax预先加载代码
function pjax_reload() {
console.log("Pjax reload triggered");
// 给pjax容器加入css过渡动画
// var pjaxContainer = document.getElementById("pjax-container");
// if (pjaxContainer) pjaxContainer.classList.add("uk-animation-slide-bottom-small");
// 评论区过渡动画
var selectors = [
".comment-meta",
".comment-author vcard",
".comment-metadata",
".comment-content",
".reply"
];
selectors.forEach(function(selector) {
document.querySelectorAll(selector).forEach(function(el) {
el.setAttribute("uk-scrollspy", "cls: uk-animation-fade; delay: 100");
});
});
// 说说页面过渡动画
var shuoshuoBody = document.getElementById("shuoshuo_body");
if (shuoshuoBody) {
shuoshuoBody.setAttribute("uk-scrollspy", "target: > .shuoshuo; cls: uk-animation-fade; delay: 250");
}
}
function refreshHeatmap() {
if (typeof renderHeatmap === 'function') {
try {
renderHeatmap();
} catch (e) {
console.error('renderHeatmap error:', e);
}
}
}
// pjax发送时期
document.addEventListener("pjax:send", function () {
// pjax动画减少圆圈动画
NProgress.configure({
showSpinner: false
});
// pjax执行动画开始
NProgress.start(); //加载动画效果开始
});
// Pjax 成功后,重新加载下面的函数
document.addEventListener("pjax:success", function () {
// 重载图片懒加载插件的js
if (typeof lazyLoadInit === 'function') {
lazyLoadInit({
// coverColor: "white",
// coverDiv: "<div style='display: flex; justify-content: center; align-items: center; height: 200px; color: #000000;'><span>优世界·一个记录生活点滴的小站</span></div>",
offsetBottom: 50,
offsetTopm: 50,
showTime: 1100,
queueInterval: 100
});
}
// 激活文章图片灯箱
if (window.ViewImage) {
ViewImage.init('.post-content img');
}
if (typeof window.refreshFloatingTools === 'function') {
window.refreshFloatingTools();
}
});
// pjax加载完成
document.addEventListener("pjax:complete", function () {
// 立即执行:进度条、基础 UI
if (typeof NProgress !== 'undefined') NProgress.done();
pjax_reload();
// ★ 关键:pjax_reload() 里刚给评论/说说等元素新加了 uk-scrollspy 属性,
// 此刻连同换入的整棵新 DOM 一起交给 UIKit 重新 boot一遍。
// 必须在 pjax_reload() 之后调用,否则新加的属性会被漏掉。
// (关于页的分节动画也走 UIKit scrollspy,所以同样靠这一步复活。)
reinitUIKit();
// 动态加载页面特定JS(PJAX适配)
// 检测当前页面是否为文章详情页,如果是则加载page-only.js
var currentPath = window.location.pathname;
var isArticlePage = document.querySelector('#Comments') !== null ||
document.querySelector('.post-content') !== null;
if (isArticlePage && !window._pageOnlyLoaded && window._pageOnlyScriptUrl) {
// 动态加载page-only.js(使用Hugo fingerprint后的正确URL)
var script = document.createElement('script');
script.src = window._pageOnlyScriptUrl;
script.onload = function() {
window._pageOnlyLoaded = true;
console.log('page-only.js loaded for PJAX navigation');
// 初始化Artalk等
if (typeof window.initArtalk === 'function') {
try { window.initArtalk(); } catch(e) { console.error('initArtalk error:', e); }
}
if (typeof window.initParagraphComments === 'function') {
try { window.initParagraphComments(); } catch(e) {}
}
};
document.body.appendChild(script);
}
// 下一帧执行:轻量初始化
requestAnimationFrame(function() {
if (typeof initCodeCopy === 'function') initCodeCopy();
if (typeof window.initSearch === 'function') {
try { window.initSearch(); } catch(e) {}
}
if (typeof window.refreshFloatingTools === 'function') {
window.refreshFloatingTools();
} else if (typeof window.initFloatingTools === 'function') {
try { window.initFloatingTools(); } catch(e) {}
}
// 文章动画
if (typeof window.initPostScrollspy === 'function') window.initPostScrollspy();
});
// 空闲时执行:非关键功能
var idle = window.requestIdleCallback || function(fn) { return setTimeout(fn, 200); };
idle(function() {
if (typeof window.initParagraphComments === 'function') {
try { window.initParagraphComments(); } catch(e) {}
}
if (typeof window.initInfiniteScroll === 'function' && window.enableInfiniteScroll) {
window.initInfiniteScroll();
}
if (typeof window.initLinkStatus === 'function') {
try { window.initLinkStatus(); } catch(e) {}
}
if (typeof window.initArtalk === 'function') {
try { window.initArtalk(); } catch(e) {}
}
if (typeof window.bsz_fetch === 'function') {
try { window.bsz_fetch(); } catch(e) {}
}
if (typeof window.initImageFrameReveal === 'function') {
try { window.initImageFrameReveal(); } catch(e) {}
}
// 链接卡片和中英文间距(DOM 扫描,非关键)
if (typeof window.initLinkify === 'function') window.initLinkify();
if (typeof window.initPangu === 'function') window.initPangu();
});
// 延迟:代码折叠
setTimeout(function() {
if (typeof window.initCodeFold === 'function') window.initCodeFold();
}, 300);
});