vue-stack-tabs 1.4.1 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,15 +25,14 @@ import {
25
25
  omitStackTabsReservedQuery,
26
26
  toSafeTabUrl,
27
27
  decodeSafeTabUrl,
28
- cloneLocationQuery,
29
- clonePage
28
+ cloneLocationQuery
30
29
  } from '../utils/urlParser'
31
30
  import { TabEventType, useTabEmitter } from './useTabEventBus'
32
31
  import { runNavigationTransaction } from './tabPanel/navigationTransaction'
33
32
  import { useI18n } from 'vue-i18n-lite'
34
33
 
35
34
  import { resolveStackTabsRuntimeContext, type StackTabsRuntimeContext } from './stackTabsContext'
36
- import { createTabPanelEviction } from './tabPanel/evict'
35
+ import { getTabStore } from './tabPanel/tabStore'
37
36
  import { createTabPanelScroll } from './tabPanel/scroll'
38
37
  import { createTabPanelSession } from './tabPanel/session'
39
38
  import { createTabPanelRefresh } from './tabPanel/refresh'
@@ -58,27 +57,50 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
58
57
  const runtimeContext = providedRuntimeContext ?? resolveStackTabsRuntimeContext()
59
58
  const emitter = useTabEmitter(runtimeContext)
60
59
  const { t } = useI18n()
60
+ const { tabs, defaultTabs, caches, refreshKey, iframeRefreshKeys, isInitialized } = runtimeContext
61
+ const store = getTabStore(runtimeContext)
61
62
  const {
62
- tabs,
63
- defaultTabs,
64
- caches,
65
- components,
66
- tabIdsToEvict,
67
- refreshKey,
68
- iframeRefreshKeys,
69
- isInitialized,
70
- cacheIdsToEvict
71
- } = runtimeContext
72
- const {
63
+ getTabs,
64
+ getTab,
65
+ getActiveTab,
66
+ findTabByTopPagePath,
67
+ hasTab,
68
+ canAddTab,
69
+ getPages,
70
+ getTopPage,
71
+ getPageSnapshot,
72
+ addTab,
73
+ addTabs,
74
+ activateTab,
75
+ moveTab,
76
+ removeTab: removeTabFromStore,
77
+ removeTabs: removeTabsFromStore,
78
+ appendPage,
79
+ popPages,
80
+ replacePages,
81
+ clearPages,
82
+ updatePage,
83
+ consumeBackParams,
73
84
  markCacheForEviction,
74
- markTabPagesForEviction,
75
85
  markTabPagesForEvictionOnly,
76
86
  addCache,
77
87
  removeCache,
78
88
  evictMarkedCaches,
79
89
  evictPageCache,
80
- replacePageCaches
81
- } = createTabPanelEviction(runtimeContext)
90
+ replacePageCaches,
91
+ isTabMarkedForEviction,
92
+ isCacheMarkedForEviction,
93
+ unmarkCacheForEviction,
94
+ clearTabEvictionMarks,
95
+ getComponent,
96
+ setComponent,
97
+ deleteComponent,
98
+ snapshotComponents,
99
+ restoreComponents,
100
+ snapshotCaches,
101
+ restoreCaches,
102
+ reset: resetStore
103
+ } = store
82
104
  const { restoreScroller, saveScroller, removeScroller, addPageScroller } =
83
105
  createTabPanelScroll(runtimeContext)
84
106
  const {
@@ -89,7 +111,7 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
89
111
  getSessionKey
90
112
  } = createTabPanelSession(runtimeContext)
91
113
 
92
- const size = (): number => tabs.value.length
114
+ const size = (): number => getTabs().length
93
115
 
94
116
  /**
95
117
  * 初始化:根据 defaultTabs 创建标签与初始页面,并尝试从 sessionStorage 恢复上次激活的标签
@@ -139,89 +161,44 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
139
161
  }
140
162
 
141
163
  defaultTabs.value = [...defaultTabs.value, ...newDefaults]
142
- tabs.value = [...tabs.value, ...newTabs]
164
+ addTabs(newTabs)
143
165
 
144
166
  // SSR 环境无 sessionStorage,直接跳过恢复。
145
167
  const storedTabJson =
146
168
  typeof window !== 'undefined' ? sessionStorage.getItem(getSessionKey()) : null
147
169
  const restoredTab = restoreTabFromSession(storedTabJson)
148
- if (restoredTab && !tabs.value.some((t) => t.id === restoredTab.id)) {
170
+ if (restoredTab && !hasTab(restoredTab.id)) {
149
171
  if (restoredTab.url) restoredTab.url = toSafeTabUrl(restoredTab.url)
150
172
  if (restoredTab.iframe && restoredTab.url && restoredTab.iframeRefreshMode === undefined) {
151
173
  restoredTab.iframeRefreshMode = isCrossOriginUrl(restoredTab.url) ? 'reload' : 'postMessage'
152
174
  }
153
175
 
154
176
  if (restoredTab.pages && typeof restoredTab.pages.list === 'function') {
155
- const pages = restoredTab.pages.list()
156
- pages.forEach((page) => {
177
+ addTab(restoredTab)
178
+ for (const page of getPages(restoredTab.id)) {
157
179
  if (page && page.id) addCache(page.id)
158
- })
180
+ }
181
+ } else {
182
+ addTab(restoredTab)
159
183
  }
160
-
161
- tabs.value = [...tabs.value, restoredTab]
162
184
  }
163
185
  isInitialized.value = true
164
186
  }
165
187
 
166
- const hasTab = (id: string) => tabs.value.some((t) => t.id === id)
167
- const canAddTab = () =>
168
- runtimeContext.maxTabCount.value <= 0 ||
169
- (runtimeContext.maxTabCount.value > 0 && runtimeContext.maxTabCount.value > tabs.value.length)
170
-
171
- const addTab = (tab: ITabItem): void => {
172
- tabs.value = [...tabs.value, tab]
173
- }
174
-
175
- const getTab = (id: string) => tabs.value.find((t) => t.id === id) ?? null
176
-
177
- /**
178
- * 唯一的标签激活状态写入口。即使标签对象保持不变,也替换列表引用,
179
- * 让 TabHeader 与 iframe 视图在同一个响应式提交中观察到一致状态。
180
- */
181
- const setActiveTab = (id: string): ITabItem | null => {
182
- let activeTab: ITabItem | null = null
183
- tabs.value = tabs.value.map((tab) => {
184
- tab.active = tab.id === id
185
- if (tab.active) activeTab = tab
186
- return tab
187
- })
188
- return activeTab
189
- }
190
-
191
188
  const { resolvePageComponent } = createPageComponentFactory({
192
189
  runtimeContext,
193
- getTab,
190
+ consumeBackParams,
191
+ getComponent,
192
+ setComponent,
194
193
  addCache,
195
194
  evictMarkedCaches,
195
+ clearTabEvictionMarks,
196
196
  addPageScroller,
197
197
  saveScroller,
198
198
  restoreScroller,
199
199
  removeScroller
200
200
  })
201
201
 
202
- /**
203
- * 将指定标签移动到目标索引。标签对象本身不变,因此其页面栈和缓存关联保持不受影响。
204
- * @returns 是否实际调整了顺序
205
- */
206
- const moveTab = (id: string, targetIndex: number): boolean => {
207
- const sourceIndex = tabs.value.findIndex((tab) => tab.id === id)
208
- if (
209
- sourceIndex < 0 ||
210
- targetIndex < 0 ||
211
- targetIndex >= tabs.value.length ||
212
- sourceIndex === targetIndex
213
- ) {
214
- return false
215
- }
216
-
217
- const reorderedTabs = tabs.value.slice()
218
- const [tab] = reorderedTabs.splice(sourceIndex, 1)
219
- if (!tab) return false
220
- reorderedTabs.splice(targetIndex, 0, tab)
221
- tabs.value = reorderedTabs
222
- return true
223
- }
224
-
225
202
  /** 把已有标签的对外字段提取为 ITabBase(用于让路由归属回该标签) */
226
203
  const toTabBase = (tab: ITabItem): ITabBase => ({
227
204
  id: tab.id,
@@ -245,8 +222,8 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
245
222
  }
246
223
 
247
224
  const path = normalizePathForCache(route)
248
- const activeTab = tabs.value.find((tab) => tab.active)
249
- const activePage = activeTab?.pages.peek()
225
+ const activeTab = getActiveTab()
226
+ const activePage = activeTab ? getTopPage(activeTab.id) : null
250
227
  if (activeTab && activePage?.path === path) {
251
228
  return toTabBase(activeTab)
252
229
  }
@@ -254,7 +231,7 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
254
231
  // 当前激活标签与路由不一致:首屏 URL 无 __tab,或 session 恢复了上次激活的其它标签
255
232
  //(演示应用在 iframe 中重载后 hash 为空即属此类)。此时接管栈顶 path 与当前路由一致的
256
233
  // 标签(默认标签或已有标签),避免同一个页面既留下已有标签、又生成一个「未命名」的兜底标签。
257
- const pathMatchedTab = tabs.value.find((tab) => tab.pages.peek()?.path === path)
234
+ const pathMatchedTab = findTabByTopPagePath(path)
258
235
  if (pathMatchedTab) return toTabBase(pathMatchedTab)
259
236
 
260
237
  return {
@@ -292,13 +269,14 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
292
269
  active: true,
293
270
  pages
294
271
  } as unknown as ITabItem
295
- setActiveTab('')
272
+ activateTab('')
296
273
  addTab(targetTab)
297
274
  emitter.emit(TabEventType.TAB_ACTIVE, { id: tabInfo.id!, isRoute: false })
298
275
  } else {
299
- setActiveTab(targetTab.id)
300
- if (targetTab.pages.isEmpty() || targetTab.pages.peek()!.id !== page.id) {
301
- targetTab.pages.push(page)
276
+ activateTab(targetTab.id)
277
+ const topPage = getTopPage(targetTab.id)
278
+ if (!topPage || topPage.id !== page.id) {
279
+ appendPage(targetTab.id, page)
302
280
  }
303
281
  }
304
282
  return targetTab
@@ -318,7 +296,7 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
318
296
  | { empty: true }
319
297
  | { recovered: DefineComponent; cacheName: string } => {
320
298
  const tabInfo = parseTabInfoFromRoute(route)
321
- if (tabIdsToEvict.has(tabInfo.id!)) return { empty: true }
299
+ if (isTabMarkedForEviction(tabInfo.id!)) return { empty: true }
322
300
 
323
301
  const path = normalizePathForCache(route)
324
302
 
@@ -326,14 +304,14 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
326
304
  let cacheName: string
327
305
  let page: ITabPage | null = null
328
306
 
329
- if (targetTab && !targetTab.pages.isEmpty()) {
330
- const top = targetTab.pages.peek()
307
+ if (targetTab && getTopPage(targetTab.id)) {
308
+ const top = getTopPage(targetTab.id)!
331
309
  if (!top) throw new Error('[vue-stack-tabs] Stack is not empty but peek() returned undefined')
332
310
 
333
311
  if (top.path === path) {
334
312
  cacheName = top.id
335
313
  } else {
336
- const pagesList = targetTab.pages.readonlyList()
314
+ const pagesList = getPages(targetTab.id)
337
315
  let foundIndex = -1
338
316
  for (let i = pagesList.length - 2; i >= 0; i--) {
339
317
  const pastPage = pagesList[i]
@@ -350,13 +328,11 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
350
328
  )
351
329
  }
352
330
  const stepsToPop = pagesList.length - 1 - foundIndex
353
- for (let i = 0; i < stepsToPop; i++) {
354
- const popped = targetTab.pages.pop()
355
- // 仅标记待驱逐,由 onActivated 或下一拍 evictMarkedCaches 统一清理,
356
- // 避免 watcher 同步执行与守卫拒绝路由之间产生不一致。
357
- if (popped) markCacheForEviction(popped.id)
358
- }
359
- const newTop = targetTab.pages.peek()
331
+ const poppedPages = popPages(targetTab.id, stepsToPop)
332
+ // 仅标记待驱逐,由 onActivated 或下一拍 evictMarkedCaches 统一清理,
333
+ // 避免 watcher 同步执行与守卫拒绝路由之间产生不一致。
334
+ for (const popped of poppedPages) markCacheForEviction(popped.id)
335
+ const newTop = getTopPage(targetTab.id)
360
336
  cacheName = newTop ? newTop.id : createPageId()
361
337
 
362
338
  if (!newTop) {
@@ -387,16 +363,16 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
387
363
  }
388
364
  }
389
365
 
390
- if (cacheIdsToEvict.has(cacheName)) {
391
- cacheIdsToEvict.delete(cacheName)
392
- const cached = components.get(cacheName)
366
+ if (isCacheMarkedForEviction(cacheName)) {
367
+ unmarkCacheForEviction(cacheName)
368
+ const cached = getComponent(cacheName)
393
369
  if (cached) return { recovered: cached, cacheName }
394
370
  }
395
371
 
396
372
  if (page) {
397
373
  targetTab = findOrCreateTargetTab(tabInfo, route, cacheName, page)
398
374
  } else {
399
- setActiveTab(targetTab!.id)
375
+ activateTab(targetTab!.id)
400
376
  }
401
377
 
402
378
  // 首屏 URL 往往没有 __tab(用户直接打开默认路由):把解析出的标签编码写回地址,
@@ -417,8 +393,8 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
417
393
  const activeCacheKey = ref<string>('')
418
394
  /** 只随当前活动页面刷新变化,避免一个 Tab 刷新导致其他 Tab 的缓存 key 失效。 */
419
395
  const activePageRefreshVersion = computed(() => {
420
- const activeTab = tabs.value.find((tab) => tab.active)
421
- return activeTab?.pages.peek()?.refreshVersion ?? 0
396
+ const activeTab = getActiveTab()
397
+ return activeTab ? (getTopPage(activeTab.id)?.refreshVersion ?? 0) : 0
422
398
  })
423
399
 
424
400
  let lastRouteKey = ''
@@ -455,74 +431,45 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
455
431
  const currentTab = getTab(tab.id!)
456
432
  if (!currentTab) return undefined
457
433
 
458
- const pageSnapshot = currentTab.pages.list().map((p) => clonePage(p))
459
- const cacheSnapshot = [...caches.value]
460
- const componentSnapshot = new Map(components)
434
+ const pageSnapshot = getPageSnapshot(currentTab.id)
435
+ const cacheSnapshot = snapshotCaches()
436
+ const componentSnapshot = snapshotComponents()
461
437
 
462
438
  markTabPagesForEvictionOnly(currentTab)
463
439
  evictMarkedCaches()
464
- currentTab.pages.clear()
440
+ clearPages(currentTab.id)
465
441
 
466
442
  return () => {
467
- currentTab.pages.clear()
468
- for (const page of pageSnapshot) currentTab.pages.push(clonePage(page))
469
- caches.value = [...cacheSnapshot]
470
- components.clear()
471
- for (const [id, component] of componentSnapshot) components.set(id, component)
443
+ replacePages(currentTab.id, pageSnapshot)
444
+ restoreCaches(cacheSnapshot)
445
+ restoreComponents(componentSnapshot)
472
446
  }
473
447
  }
474
448
 
475
449
  const removeTab = (id: string): string => {
476
- let activeTabId = ''
477
- const currentTab = getTab(id)
478
- const tabList = tabs.value
479
- const i = tabList.findIndex((t) => t?.id === id)
480
- if (i < 0) return ''
481
-
482
- const tab = tabList[i]!
483
- if (!tab.closable) return id
484
-
485
- markTabPagesForEviction(tab)
486
- tab.pages.clear()
487
- if (tabList.length > 1 && tab.active) {
488
- activeTabId = (i === 0 ? tabList[1] : tabList[i - 1])?.id ?? ''
489
- }
490
- tabs.value = [...tabList.slice(0, i), ...tabList.slice(i + 1)]
450
+ const result = removeTabFromStore(id)
451
+ if (result.status === 'not-found') return ''
452
+ if (result.status === 'not-closable') return id
491
453
 
492
- if (activeTabId) {
493
- emitter.emit(TabEventType.TAB_ACTIVE, { id: activeTabId })
494
- }
495
- if (!currentTab?.active) {
496
- evictMarkedCaches()
497
- tabIdsToEvict.clear()
454
+ if (result.nextActiveId) {
455
+ emitter.emit(TabEventType.TAB_ACTIVE, { id: result.nextActiveId })
498
456
  }
499
457
 
500
- return activeTabId
458
+ return result.nextActiveId
501
459
  }
502
460
 
503
461
  /** 批量关闭满足条件的可关闭标签,并统一标记其页面缓存待驱逐。 */
504
- const removeTabs = (shouldRemove: (tab: ITabItem, index: number) => boolean) => {
505
- const removed: ITabItem[] = []
506
- const remaining = tabs.value.filter((tab, index) => {
507
- if (!tab.closable || !shouldRemove(tab, index)) return true
508
- markTabPagesForEviction(tab)
509
- tab.pages.clear()
510
- removed.push(tab)
511
- return false
512
- })
513
- tabs.value = remaining
514
- return removed
515
- }
462
+ const removeTabs = (shouldRemove: (tab: ITabItem, index: number) => boolean) =>
463
+ removeTabsFromStore(shouldRemove)
516
464
 
517
465
  const removeAllTabs = () => {
518
466
  removeTabs(() => true)
519
- const hasActive = tabs.value.some((t) => t.active)
520
- if (hasActive) {
467
+ if (getActiveTab()) {
521
468
  evictMarkedCaches()
522
- tabIdsToEvict.clear()
469
+ clearTabEvictionMarks()
523
470
  return
524
471
  }
525
- const last = tabs.value.at(-1)
472
+ const last = getTabs().at(-1)
526
473
  if (last) emitter.emit(TabEventType.TAB_ACTIVE, { id: last.id })
527
474
  }
528
475
 
@@ -533,42 +480,44 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
533
480
  emitter.emit(TabEventType.TAB_ACTIVE, { id })
534
481
  } else {
535
482
  evictMarkedCaches()
536
- tabIdsToEvict.clear()
483
+ clearTabEvictionMarks()
537
484
  }
538
485
  }
539
486
 
540
487
  const removeLeftTabs = (id: string) => {
541
- const pivot = tabs.value.findIndex((t) => t?.id === id)
488
+ const pivot = getTabs().findIndex((t) => t?.id === id)
542
489
  if (pivot <= 0) return
543
490
 
544
491
  removeTabs((_, index) => index < pivot)
545
- if (tabs.value.some((t) => t.active)) {
492
+ if (getActiveTab()) {
546
493
  evictMarkedCaches()
547
- tabIdsToEvict.clear()
494
+ clearTabEvictionMarks()
548
495
  return
549
496
  }
550
- const first = tabs.value[0]
497
+ const first = getTabs()[0]
551
498
  if (first) emitter.emit(TabEventType.TAB_ACTIVE, { id: first.id })
552
499
  }
553
500
 
554
501
  const removeRightTabs = (id: string) => {
555
- const pivot = tabs.value.findIndex((t) => t?.id === id)
556
- if (pivot < 0 || pivot >= tabs.value.length - 1) return
502
+ const tabList = getTabs()
503
+ const pivot = tabList.findIndex((t) => t?.id === id)
504
+ if (pivot < 0 || pivot >= tabList.length - 1) return
557
505
 
558
506
  removeTabs((_, index) => index > pivot)
559
- if (tabs.value.some((t) => t.active)) {
507
+ if (getActiveTab()) {
560
508
  evictMarkedCaches()
561
- tabIdsToEvict.clear()
509
+ clearTabEvictionMarks()
562
510
  return
563
511
  }
564
- const last = tabs.value.at(-1)
512
+ const last = getTabs().at(-1)
565
513
  if (last) emitter.emit(TabEventType.TAB_ACTIVE, { id: last.id })
566
514
  }
567
515
 
568
516
  const { refreshTab, refreshAllTabs } = createTabPanelRefresh({
569
- tabs,
517
+ getTabs,
518
+ getTopPage,
519
+ updatePage,
570
520
  iframeRefreshKeys,
571
- getTab,
572
521
  createPageId,
573
522
  addCache,
574
523
  evictPageCache,
@@ -576,8 +525,6 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
576
525
  refreshIframeByPostMessage: (id) => emitter.emit(TabEventType.REFRESH_IFRAME_POSTMESSAGE, id)
577
526
  })
578
527
 
579
- const getComponent = (id: string) => components.get(id)
580
-
581
528
  /**
582
529
  * 激活指定标签,更新 active 状态并可选跳转路由
583
530
  * @param id - 标签 id
@@ -589,9 +536,9 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
589
536
  if (!target) return Promise.resolve()
590
537
  if (target.active) return Promise.resolve()
591
538
 
592
- const top = target.pages.peek()
539
+ const top = getTopPage(id)
593
540
  const activateTarget = () => {
594
- const activeTab = setActiveTab(id)
541
+ const activeTab = activateTab(id)
595
542
  if (activeTab) saveActiveTabToSession(activeTab)
596
543
  }
597
544
 
@@ -616,7 +563,7 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
616
563
 
617
564
  return runNavigationTransaction({
618
565
  apply: () => {
619
- const prevActiveId = tabs.value.find((tab) => tab.active)?.id
566
+ const prevActiveId = getActiveTab()?.id
620
567
  // SSR 环境无 sessionStorage;快照为 null 时 rollback 视为「无变化」。
621
568
  const sessionSnapshot =
622
569
  typeof window !== 'undefined' ? window.sessionStorage.getItem(getSessionKey()) : null
@@ -625,7 +572,7 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
625
572
  },
626
573
  navigate: () => router.push({ path: top.path, query }),
627
574
  rollback: (snapshot) => {
628
- setActiveTab(snapshot.prevActiveId ?? '')
575
+ activateTab(snapshot.prevActiveId ?? '')
629
576
  restoreActiveTabSession(snapshot.sessionSnapshot)
630
577
  },
631
578
  isFailureResult: isNavigationFailure,
@@ -636,13 +583,9 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
636
583
  const reset = () => removeAllTabs()
637
584
 
638
585
  const destroy = () => {
639
- tabs.value = []
586
+ resetStore()
640
587
  defaultTabs.value = []
641
- cacheIdsToEvict.clear()
642
- tabIdsToEvict.clear()
643
588
  iframeRefreshKeys.value = {}
644
- caches.value = []
645
- components.clear()
646
589
  runtimeContext.scrollPositionsByPageId.clear()
647
590
  runtimeContext.isInitialized.value = false
648
591
  runtimeContext.refreshKey.value = 0
@@ -669,8 +612,17 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
669
612
  addCache,
670
613
  removeCache,
671
614
  getTab,
615
+ getActiveTab,
616
+ getTopPage,
617
+ getPages,
618
+ getPageSnapshot,
672
619
  moveTab,
673
620
  addTab,
621
+ appendPage,
622
+ popPages,
623
+ replacePages,
624
+ clearPages,
625
+ updatePage,
674
626
  removeTab,
675
627
  removeAllTabs,
676
628
  removeLeftTabs,
@@ -679,12 +631,13 @@ export default (providedRuntimeContext?: StackTabsRuntimeContext) => {
679
631
  hasTab,
680
632
  renewTab,
681
633
  addPage,
682
- addComponent: (id: string, comp: DefineComponent) => components.set(id, comp),
634
+ addComponent: setComponent,
683
635
  getComponent,
684
- removeComponent: (id: string) => components.delete(id),
636
+ removeComponent: deleteComponent,
685
637
  markCacheForEviction,
686
638
  evictMarkedCaches,
687
639
  evictPageCache,
640
+ clearTabEvictionMarks,
688
641
  refreshTab,
689
642
  refreshAllTabs,
690
643
  iframeRefreshKeys,