优化事件监听机制避免泄漏
This commit is contained in:
parent
3146fc99cd
commit
8656f037bd
@ -2,7 +2,7 @@
|
||||
import { SITE_NAME, NAV_LINKS } from "@/consts.ts";
|
||||
import Search from "astro-pagefind/components/Search";
|
||||
import ThemeToggle from "@/components/ThemeToggle.astro";
|
||||
|
||||
import "@/styles/header.css";
|
||||
// 获取当前路径
|
||||
const currentPath = Astro.url.pathname;
|
||||
|
||||
|
@ -95,6 +95,49 @@ const {
|
||||
<script is:inline>
|
||||
// 立即执行主题初始化,采用"无闪烁"加载方式
|
||||
(function () {
|
||||
// 存储事件监听器,便于清理
|
||||
const listeners = [];
|
||||
|
||||
// 添加事件监听器并记录,方便后续统一清理
|
||||
function addListener(element, eventType, handler, options) {
|
||||
if (!element) return null;
|
||||
|
||||
element.addEventListener(eventType, handler, options);
|
||||
listeners.push({ element, eventType, handler, options });
|
||||
return handler;
|
||||
}
|
||||
|
||||
// 清理函数 - 移除所有事件监听器
|
||||
function cleanup() {
|
||||
listeners.forEach(({ element, eventType, handler, options }) => {
|
||||
try {
|
||||
element.removeEventListener(eventType, handler, options);
|
||||
} catch (err) {
|
||||
// 忽略错误
|
||||
}
|
||||
});
|
||||
|
||||
// 清空数组
|
||||
listeners.length = 0;
|
||||
}
|
||||
|
||||
// 注册清理函数 - 确保在页面转换前清理事件
|
||||
function registerCleanup() {
|
||||
const cleanupEvents = [
|
||||
"astro:before-preparation",
|
||||
"astro:before-swap",
|
||||
"swup:willReplaceContent"
|
||||
];
|
||||
|
||||
// 为每个事件注册一次性清理函数
|
||||
cleanupEvents.forEach((eventName) => {
|
||||
document.addEventListener(eventName, cleanup, { once: true });
|
||||
});
|
||||
|
||||
// 页面卸载时清理
|
||||
window.addEventListener("beforeunload", cleanup, { once: true });
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取系统首选主题
|
||||
const getSystemTheme = () => {
|
||||
@ -121,6 +164,8 @@ const {
|
||||
|
||||
// 立即设置文档主题,在DOM渲染前应用,避免闪烁
|
||||
document.documentElement.dataset.theme = theme;
|
||||
// 确保同步classList,提高兼容性
|
||||
document.documentElement.classList.toggle('dark', theme === 'dark');
|
||||
|
||||
// 监听系统主题变化(只有当主题设为跟随系统时才响应)
|
||||
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
@ -130,14 +175,37 @@ const {
|
||||
if (!localStorage.getItem("theme")) {
|
||||
const newTheme = e.matches ? "dark" : "light";
|
||||
document.documentElement.dataset.theme = newTheme;
|
||||
document.documentElement.classList.toggle('dark', e.matches);
|
||||
}
|
||||
};
|
||||
|
||||
// 添加系统主题变化监听
|
||||
mediaQuery.addEventListener("change", handleMediaChange);
|
||||
addListener(mediaQuery, "change", handleMediaChange);
|
||||
|
||||
// 注册清理函数
|
||||
registerCleanup();
|
||||
|
||||
// 监听页面转换事件,确保在页面转换后重新初始化
|
||||
function onPageTransition() {
|
||||
// 重新初始化主题
|
||||
if (storedTheme) {
|
||||
document.documentElement.dataset.theme = storedTheme;
|
||||
document.documentElement.classList.toggle('dark', storedTheme === 'dark');
|
||||
} else {
|
||||
const systemTheme = getSystemTheme();
|
||||
document.documentElement.dataset.theme = systemTheme;
|
||||
document.documentElement.classList.toggle('dark', systemTheme === 'dark');
|
||||
}
|
||||
}
|
||||
|
||||
// 设置页面转换事件监听
|
||||
document.addEventListener("astro:page-load", onPageTransition);
|
||||
document.addEventListener("astro:after-swap", onPageTransition);
|
||||
|
||||
} catch (error) {
|
||||
// 出错时应用默认浅色主题,确保页面正常显示
|
||||
document.documentElement.dataset.theme = "light";
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
@ -156,7 +224,7 @@ const {
|
||||
/>
|
||||
|
||||
<!-- 预获取脚本 -->
|
||||
<script>
|
||||
<script is:inline>
|
||||
// 在DOM加载完成后执行
|
||||
document.addEventListener("astro:page-load", () => {
|
||||
// 获取所有视口预获取链接
|
||||
|
@ -914,36 +914,5 @@ const {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// 全局暴露主题切换函数和配置,方便调试和高级用法
|
||||
window.__themeToggle = {
|
||||
modes: TRANSITION_MODES,
|
||||
getMode: getThemeTransitionMode,
|
||||
setMode: saveThemeTransitionMode,
|
||||
// 添加新的帮助方法:切换动画模式
|
||||
toggleMode: function() {
|
||||
const currentMode = getThemeTransitionMode();
|
||||
const modes = Object.values(TRANSITION_MODES);
|
||||
const currentIndex = modes.indexOf(currentMode);
|
||||
const nextIndex = (currentIndex + 1) % modes.length;
|
||||
const nextMode = modes[nextIndex];
|
||||
saveThemeTransitionMode(nextMode);
|
||||
return nextMode;
|
||||
},
|
||||
// 描述当前模式
|
||||
describeModeEffect: function() {
|
||||
const mode = getThemeTransitionMode();
|
||||
const currentTheme = document.documentElement.dataset.theme || 'light';
|
||||
const nextTheme = currentTheme === 'light' ? 'dark' : 'light';
|
||||
|
||||
// 确定将使用的动画类型
|
||||
const animationType = determineAnimationType(mode, currentTheme, nextTheme);
|
||||
|
||||
if (animationType === TRANSITION_MODES.EXPAND) {
|
||||
return `当前模式: ${mode}, ${currentTheme}→${nextTheme} 将使用扩散效果`;
|
||||
} else {
|
||||
return `当前模式: ${mode}, ${currentTheme}→${nextTheme} 将使用收缩效果`;
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
</script>
|
@ -4,6 +4,7 @@ import { getSpecialPath } from "@/content.config";
|
||||
import Layout from "@/components/Layout.astro";
|
||||
import Breadcrumb from "@/components/Breadcrumb.astro";
|
||||
import { ARTICLE_EXPIRY_CONFIG } from "@/consts";
|
||||
import "@/styles/content-styles.css";
|
||||
|
||||
// 添加这一行,告诉Astro预渲染这个页面
|
||||
export const prerender = true;
|
||||
|
@ -324,21 +324,18 @@ function getArticleUrl(articleId: string) {
|
||||
<script is:inline>
|
||||
// 使用"进入→绑定→退出完全清理"模式
|
||||
(function () {
|
||||
// 脚本实例ID,用于跟踪当前实例
|
||||
const SCRIPT_INSTANCE_ID = Date.now() + '-' + Math.random().toString(36).substring(2, 9);
|
||||
// 统一管理所有事件监听器
|
||||
const listeners = [];
|
||||
|
||||
// 标记脚本是否已销毁
|
||||
let isDestroyed = false;
|
||||
|
||||
// 统一管理所有事件监听器
|
||||
const listeners = [];
|
||||
|
||||
// 添加事件监听器并记录,方便后续统一清理
|
||||
function addListener(element, eventType, handler, options) {
|
||||
if (!element) return null;
|
||||
if (!element || isDestroyed) return null;
|
||||
|
||||
element.addEventListener(eventType, handler, options);
|
||||
listeners.push({ element, eventType, handler });
|
||||
listeners.push({ element, eventType, handler, options });
|
||||
|
||||
return handler;
|
||||
}
|
||||
@ -351,9 +348,9 @@ function getArticleUrl(articleId: string) {
|
||||
isDestroyed = true;
|
||||
|
||||
// 移除所有监听器
|
||||
listeners.forEach(({ element, eventType, handler }) => {
|
||||
listeners.forEach(({ element, eventType, handler, options }) => {
|
||||
try {
|
||||
element.removeEventListener(eventType, handler);
|
||||
element.removeEventListener(eventType, handler, options);
|
||||
} catch (err) {
|
||||
// 忽略错误
|
||||
}
|
||||
@ -362,15 +359,8 @@ function getArticleUrl(articleId: string) {
|
||||
// 清空数组
|
||||
listeners.length = 0;
|
||||
|
||||
// 移除页面转换监听器
|
||||
document.removeEventListener("astro:after-swap", onPageTransition);
|
||||
document.removeEventListener("astro:page-load", onPageTransition);
|
||||
document.removeEventListener("swup:contentReplaced", onPageTransition);
|
||||
|
||||
// 从全局范围中移除自身引用
|
||||
if (window.__filteredPageInstances) {
|
||||
window.__filteredPageInstances = window.__filteredPageInstances.filter(id => id !== SCRIPT_INSTANCE_ID);
|
||||
}
|
||||
// 移除页面可见性监听
|
||||
document.removeEventListener('visibilitychange', onPageVisibilityChange);
|
||||
}
|
||||
|
||||
// 跟踪页面离开,确保完全清理
|
||||
@ -403,7 +393,7 @@ function getArticleUrl(articleId: string) {
|
||||
endDate: urlParams.get('endDate') || ''
|
||||
};
|
||||
|
||||
// 储存所有文章数据(全局缓存)
|
||||
// 储存所有文章数据
|
||||
let allArticles = [];
|
||||
let isArticlesLoaded = false;
|
||||
|
||||
@ -1362,54 +1352,71 @@ function getArticleUrl(articleId: string) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果仍在筛选页面但脚本已销毁,重新初始化
|
||||
// 如果当前页面是筛选页面,重新初始化
|
||||
if (isDestroyed) {
|
||||
isDestroyed = false;
|
||||
init();
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果仍在筛选页面且脚本未销毁,可能是路由切换,重新初始化
|
||||
// 重新初始化
|
||||
init();
|
||||
}
|
||||
|
||||
// 初始化函数
|
||||
function init() {
|
||||
// 设置功能
|
||||
// 如果已销毁,立即退出
|
||||
if (isDestroyed) return;
|
||||
|
||||
// 首先清理可能存在的旧监听器
|
||||
listeners.forEach(({ element, eventType, handler, options }) => {
|
||||
try {
|
||||
element.removeEventListener(eventType, handler, options);
|
||||
} catch (err) {
|
||||
// 忽略错误
|
||||
}
|
||||
});
|
||||
listeners.length = 0;
|
||||
|
||||
// 设置筛选功能
|
||||
setupArticlesFilter();
|
||||
|
||||
// 注册清理函数
|
||||
const cleanupEvents = [
|
||||
"astro:before-preparation",
|
||||
"astro:before-swap",
|
||||
"swup:willReplaceContent",
|
||||
"beforeunload"
|
||||
];
|
||||
|
||||
// 为每个事件类型注册一次性清理
|
||||
cleanupEvents.forEach(eventType => {
|
||||
const target = eventType === "beforeunload" ? window : document;
|
||||
target.addEventListener(eventType, () => {
|
||||
cleanup();
|
||||
}, { once: true });
|
||||
});
|
||||
registerCleanup();
|
||||
|
||||
// 添加页面可见性变化监听
|
||||
document.removeEventListener('visibilitychange', onPageVisibilityChange);
|
||||
document.addEventListener('visibilitychange', onPageVisibilityChange);
|
||||
}
|
||||
|
||||
// 页面转换时检查
|
||||
// 注册清理函数 - 确保在页面转换前清理事件
|
||||
function registerCleanup() {
|
||||
const cleanupEvents = [
|
||||
"astro:before-preparation",
|
||||
"astro:before-swap",
|
||||
"swup:willReplaceContent"
|
||||
];
|
||||
|
||||
// 为每个事件注册一次性清理函数
|
||||
cleanupEvents.forEach((eventName) => {
|
||||
document.addEventListener(eventName, cleanup, { once: true });
|
||||
});
|
||||
|
||||
// 页面卸载时清理
|
||||
window.addEventListener("beforeunload", cleanup, { once: true });
|
||||
}
|
||||
|
||||
// 监听页面转换
|
||||
document.removeEventListener("astro:after-swap", onPageTransition);
|
||||
document.removeEventListener("astro:page-load", onPageTransition);
|
||||
document.removeEventListener("swup:contentReplaced", onPageTransition);
|
||||
|
||||
document.addEventListener("astro:after-swap", onPageTransition);
|
||||
document.addEventListener("astro:page-load", onPageTransition);
|
||||
document.addEventListener("swup:contentReplaced", onPageTransition);
|
||||
|
||||
// 初始化
|
||||
// 在页面加载后初始化
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", init, {
|
||||
once: true,
|
||||
});
|
||||
document.addEventListener("DOMContentLoaded", init, { once: true });
|
||||
} else {
|
||||
// 使用setTimeout确保处于事件队列末尾,避免可能的事件冲突
|
||||
setTimeout(init, 0);
|
||||
}
|
||||
})();
|
||||
|
@ -1,3 +1,5 @@
|
||||
@import "./table-styles.css";
|
||||
|
||||
/* 增强列表样式 */
|
||||
.prose ul {
|
||||
list-style-type: disc;
|
||||
|
@ -1,7 +1,4 @@
|
||||
@import "tailwindcss";
|
||||
@import "./content-styles.css";
|
||||
@import "./table-styles.css";
|
||||
@import "./header.css";
|
||||
|
||||
/* 定义深色模式选择器 */
|
||||
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
||||
|
Loading…
Reference in New Issue
Block a user