Files
blog/themes/Ying/assets/js/modules/utils.js
T
zqlit e3e3cce1a4 perf(comments): 评论加载提速 + 骨架屏 + 友链弹窗 pjax 修复 + toast 统一 Message.js
优化:
- initArtalk 加节点级幂等守卫(mount.__atkInited,失败回滚),
  消除 artalk.html/mypjax 多入口导致的 4× comments + 4× pv 重复请求
- fetch 包装层对无 body 的 GET/HEAD 去掉多余 content-type,
  消掉 Artalk 对 GET /comments 的 CORS 预检(按完整 URL 缓存,
  每篇文章都重付一次 RTT)。实测评论列表 +1150~1565ms → +626ms

修复:
- 评论列表骨架屏改注入 .atk-list-body(原骨架在 #Comments 内,
  被 Artalk.init 清空),list-loaded/故障/12s 兜底移除
- 友链申请弹窗抽成 modules/friendlink.js 进 page-only bundle
  (原内联脚本在 #pjax-container 外,pjax 后 flApplyOpenModal 未定义)
- toast 统一走本地化 Message.js/Qmsg 适配层(保留 window.Toast/showToast 旧 API)

文档:
- README / 架构总览 更新;新增 评论加载优化方案.md
2026-10-04 15:00:52 +08:00

145 lines
5.4 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.
function themeSweep(newTheme, callback) {
// View Transition API — best quality if available
if (document.startViewTransition) {
document.startViewTransition(function() { callback(); });
return;
}
// UIkit animate fallback
if (window.UIkit && UIkit.animate) {
var body = document.body;
UIkit.animate(body, 'uk-animation-fade uk-animation-fast');
setTimeout(function() {
callback();
UIkit.animate(body, 'uk-animation-fade uk-animation-fast');
}, 150);
return;
}
// Last resort: simple opacity
callback();
}
// Toast 快捷方式已统一迁到 modules/toast.js(Message.js/Qmsg 适配层),
// 这里不再重复定义 window.showToast,避免双实现漂移。
// Theme Toggle Logic
window.initThemeToggle = function() {
var avatar = document.getElementById('theme-toggle-avatar');
if (!avatar) return;
var newAvatar = avatar.cloneNode(true);
avatar.parentNode.replaceChild(newAvatar, avatar);
newAvatar.addEventListener('click', function(e) {
e.preventDefault();
var html = document.documentElement;
var currentTheme = html.getAttribute('data-theme');
var newTheme = currentTheme === 'dark' ? 'light' : 'dark';
var apply = function() {
html.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
window.showToast('已切换至 ' + (newTheme === 'dark' ? '深色' : '浅色') + '模式', 'success');
if (window.artalk && typeof window.artalk.setDarkMode === 'function') {
window.artalk.setDarkMode(newTheme === 'dark');
}
window.dispatchEvent(new Event('resize'));
};
themeSweep(newTheme, apply);
});
};
// Code Folding Logic
window.initCodeFold = function() {
var codeBlocks = document.querySelectorAll('.highlight');
codeBlocks.forEach(function(block) {
if (block.parentNode && block.parentNode.classList.contains('code-fold-container')) return;
var pre = block.querySelector('pre');
if (!pre) return;
// Use pre's content height; 0 means layout not ready, skip
var contentHeight = pre.scrollHeight || block.scrollHeight;
if (!contentHeight || contentHeight <= 360) return;
var wrapper = document.createElement('div');
wrapper.className = 'code-fold-container collapsed';
block.parentNode.insertBefore(wrapper, block);
wrapper.appendChild(block);
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'code-expand-btn';
wrapper.appendChild(btn);
var syncButton = function() {
var collapsed = wrapper.classList.contains('collapsed');
btn.innerHTML = collapsed
? '<span><i class="ri-arrow-down-s-line" style="margin-right:4px;"></i> 展开代码 </span>'
: '<span><i class="ri-arrow-up-s-line" style="margin-right:4px;"></i> 收起代码 </span>';
};
btn.onclick = function() {
wrapper.classList.toggle('collapsed');
syncButton();
};
syncButton();
});
};
// Code Copy Logic
window.initCodeCopy = function() {
var codeBlocks = document.querySelectorAll('pre');
codeBlocks.forEach(function(pre) {
if (pre.parentNode.querySelector('.copy-code-btn')) return;
if (pre.querySelector('.copy-code-btn')) return;
var btn = document.createElement('button');
btn.className = 'copy-code-btn';
btn.innerHTML = '<i class="ri-file-copy-line"></i>';
btn.title = "复制代码";
btn.style.cssText =
'position:absolute;top:-6px;right:5px;background:transparent;border:none;' +
'color:#888;cursor:pointer;padding:5px;border-radius:4px;' +
'transition:all 0.2s;opacity:0;z-index:10;';
pre.addEventListener('mouseenter', function() { btn.style.opacity = '1'; });
pre.addEventListener('mouseleave', function() { btn.style.opacity = '0'; });
if (typeof window.isMobile === 'function' && window.isMobile()) { btn.style.opacity = '1'; }
btn.addEventListener('click', function(e) {
e.stopPropagation();
var code = pre.querySelector('code') ? pre.querySelector('code').innerText : pre.innerText;
if (navigator.clipboard) {
navigator.clipboard.writeText(code).then(function() {
window.showToast('代码已复制 / Copied!', 'success');
btn.innerHTML = '<i class="ri-check-line"></i>';
setTimeout(function() { btn.innerHTML = '<i class="ri-file-copy-line"></i>'; }, 2000);
}).catch(function() {
window.showToast('复制失败 / Failed', 'error');
});
} else {
var textarea = document.createElement('textarea');
textarea.value = code;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
window.showToast('代码已复制 / Copied!', 'success');
}
});
if (getComputedStyle(pre).position === 'static') {
pre.style.position = 'relative';
}
pre.appendChild(btn);
});
};