Skip to content

Commit dafbdb6

Browse files
committed
feat(event-manager): 添加滚动监听器管理和防抖优化
添加 scrollHandler 和 scrollTimer 私有属性用于管理滚动事件监听器, 实现 registerScrollListener 和 removeAllScrollListener 方法来正确注册 和移除滚动监听器,避免内存泄漏。使用防抖机制优化滚动事件处理性能。 fix(knowledge-chart): 优化知识网络图缩放逻辑避免图表重建 修改 updateChartZoom 方法接受新缩放参数,当图表未创建时只记录目标 缩放值。使用 graphRoam 动作进行相对缩放更新,避免销毁重建图表导致 力导向布局重新计算,提升用户体验。 feat(mobile-doc): 在移动端文档页面组件卸载时清理事件管理器 在 MobileDocPage 组件的 unmounted 生命周期钩子中调用 cancelPendingRender 和 removeAllScrollListener 方法, 确保页面切换时正确清理滚动监听器和待处理渲染任务。 refactor(pwa): 优化 PWA 缓存策略减少首屏加载资源 调整 workbox 配置,预缓存仅包含 index.html,避免首次访问下载全站 约10MB资源。为带哈希的静态资源配置 CacheFirst 策略,文档数据配 置 StaleWhileRevalidate 策略,优化缓存效率。
1 parent 83d433b commit dafbdb6

4 files changed

Lines changed: 62 additions & 13 deletions

File tree

src/pages/doc/DocPageEventManager.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class DocPageEventManager {
2121
private isMobile: boolean;
2222
private cancelLatexTask: (() => void) | null = null;
2323
private cancelHighlightTask: (() => void) | null = null;
24+
private scrollHandler: ((e: Event) => void) | null = null;
25+
private scrollTimer: ReturnType<typeof setTimeout> | undefined;
2426

2527
constructor(docPageInstance: InstanceType<typeof DocPage | typeof MobileDocPage>, isMobile: boolean = false) {
2628
this.docPageInstance = docPageInstance;
@@ -197,16 +199,17 @@ class DocPageEventManager {
197199
* @memberof DocPageEventManager
198200
*/
199201
public registerScrollListener() {
200-
let timer: NodeJS.Timeout;
201-
document.addEventListener("scroll", (e) => {
202+
this.removeAllScrollListener();
203+
this.scrollHandler = () => {
202204
// 限流更新阅读位置
203-
clearTimeout(timer);
204-
timer = setTimeout(() => {
205+
clearTimeout(this.scrollTimer);
206+
this.scrollTimer = setTimeout(() => {
205207
DocService.setDocReadRecrod(this.docPageInstance.doc, window.scrollY);
206208
}, 1000);
207209
// 滚动的同时将link-popover隐藏掉
208210
this.getRef('linkPopover') && (this.getRef('linkPopover') as InstanceType<typeof LinkPopover>).hide();
209-
});
211+
};
212+
document.addEventListener("scroll", this.scrollHandler);
210213
}
211214

212215

@@ -259,7 +262,11 @@ class DocPageEventManager {
259262
* @memberof DocPageEventManager
260263
*/
261264
public removeAllScrollListener(){
262-
document.onscroll = null
265+
if (this.scrollHandler) {
266+
document.removeEventListener("scroll", this.scrollHandler)
267+
this.scrollHandler = null
268+
}
269+
clearTimeout(this.scrollTimer)
263270
}
264271

265272

src/pages/doc/knowledge/KnowledgeNetworkChart.vue

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,7 @@ export default defineComponent({
104104
this.init();
105105
},
106106
zoom(newVal) {
107-
this.graphZoom = newVal;
108-
this.updateChartZoom();
107+
this.updateChartZoom(newVal);
109108
}
110109
},
111110
computed: {
@@ -449,10 +448,25 @@ export default defineComponent({
449448
});
450449
},
451450
452-
updateChartZoom() {
453-
// 重新初始化图表以应用新的缩放级别
454-
this.$nextTick(() => {
455-
this.init(false);
451+
updateChartZoom(newZoom: number) {
452+
// 图表未创建时只记录目标缩放,等init时生效
453+
if (!this.chart) {
454+
this.graphZoom = newZoom;
455+
return;
456+
}
457+
// 用相对缩放动作更新,避免销毁重建图表导致力导向布局重跑
458+
// this.graphZoom 由 graphRoam 事件回调同步
459+
const factor = newZoom / this.graphZoom;
460+
if (!isFinite(factor) || factor <= 0 || factor === 1) {
461+
return;
462+
}
463+
const chartDom = document.getElementById(this.id);
464+
this.chart.dispatchAction({
465+
type: 'graphRoam',
466+
seriesIndex: 0,
467+
zoom: factor,
468+
originX: (chartDom?.clientWidth ?? 0) / 2,
469+
originY: (chartDom?.clientHeight ?? 0) / 2,
456470
});
457471
},
458472

src/pages/doc/mobile/MobileDocPage.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ export default defineComponent({
142142
this.init(this.doc, headingId, kw)
143143
this.eventManager = new DocPageEventManager(this, true)
144144
this.eventManager!.registerScrollListener();
145+
},
146+
unmounted() {
147+
this.eventManager!.cancelPendingRender();
148+
this.eventManager!.removeAllScrollListener();
145149
}
146150
})
147151
</script>

vite.config.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,31 @@ export default defineConfig({
6262
registerType: 'autoUpdate',
6363
includeAssets: ['favicon.ico'],
6464
workbox: {
65-
globPatterns: ['**/index.html', '**/*.{js,css,json}'],
65+
// 只预缓存应用入口,避免首次访问就在后台下载全站约10MB资源
66+
// (含mermaid/echarts/cytoscape等懒加载chunk和300+个文档JSON)
67+
globPatterns: ['**/index.html'],
68+
runtimeCaching: [
69+
{
70+
// 带内容哈希的静态资源不会变更,缓存优先
71+
urlPattern: /\/resource\/.*\.(?:js|css)$/,
72+
handler: 'CacheFirst',
73+
options: {
74+
cacheName: 'hashed-assets',
75+
expiration: { maxEntries: 300, maxAgeSeconds: 30 * 24 * 60 * 60 },
76+
cacheableResponse: { statuses: [0, 200] },
77+
},
78+
},
79+
{
80+
// 文档与统计数据按需缓存,先返回缓存同时后台刷新
81+
urlPattern: /\.json$/,
82+
handler: 'StaleWhileRevalidate',
83+
options: {
84+
cacheName: 'doc-data',
85+
expiration: { maxEntries: 500 },
86+
cacheableResponse: { statuses: [0, 200] },
87+
},
88+
},
89+
],
6690
},
6791
manifest: {
6892
name: '知识体系',

0 commit comments

Comments
 (0)