How the TagsView Component Manages Tab State and Right-Click Operations in Vue-Element-Admin
The TagsView component in vue-element-admin centralizes tab state in a dedicated Vuex store module while handling right-click operations through targeted mutations that filter visitedViews and cachedViews arrays based on affix metadata and selection state.
The TagsView component provides the multi-tab navigation interface in the vue-element-admin dashboard framework. It synchronizes browser route changes with a persistent tab bar, supporting advanced interactions like "Close Others" and "Close All" through a tightly-coupled Vuex architecture that maintains two distinct state arrays.
Vuex Store Architecture
The state management relies on two synchronized arrays in src/store/modules/tagsView.js:
- visitedViews: Tracks all tabs currently displayed in the tab bar
- cachedViews: Stores component names for
<keep-alive>caching to preserve component state
State Initialization and Affix Tags
When the component mounts, initTags() in src/layout/components/TagsView/index.vue extracts routes marked with meta.affix (permanent tabs that cannot be closed) and populates the store:
// src/layout/components/TagsView/index.vue
const affixTags = this.affixTags = this.filterAffixTags(this.routes)
for (const tag of affixTags) {
if (tag.name) {
this.$store.dispatch('tagsView/addVisitedView', tag)
}
}
Affix tags are protected across all deletion operations, ensuring critical navigation elements like the dashboard homepage remain accessible regardless of bulk close actions.
Dynamic Route Watching
The component watches $route changes to automatically register new tabs. The addTags() method dispatches tagsView/addView, which triggers both addVisitedView and addCachedView actions:
// src/store/modules/tagsView.js
addView({ dispatch }, view) {
dispatch('addVisitedView', view)
dispatch('addCachedView', view)
}
The corresponding mutations ADD_VISITED_VIEW and ADD_CACHED_VIEW prevent duplicates by checking existing paths before pushing new view objects into the state arrays.
Right-Click Context Menu Operations
The component renders a hidden <ul class="contextmenu"> element that becomes visible via openMenu(tag, $event) when users right-click a tab. This method sets selectedTag and calculates positioning based on container bounds to prevent overflow.
Close Others Implementation
Selecting "Close Others" triggers closeOthersTags(), which first navigates to the selected tag, then dispatches tagsView/delOthersViews:
// src/layout/components/TagsView/index.vue
closeOthersTags() {
this.$router.push(this.selectedTag)
this.$store.dispatch('tagsView/delOthersViews', this.selectedTag)
.then(() => { this.moveToCurrentTag() })
}
The mutation DEL_OTHERS_VISITED_VIEWS filters the array to retain only the selected view and any affix tags:
// src/store/modules/tagsView.js
DEL_OTHERS_VISITED_VIEWS: (state, view) => {
state.visitedViews = state.visitedViews.filter(v => {
return v.meta.affix || v.path === view.path
})
}
A corresponding DEL_OTHERS_CACHED_VIEWS mutation cleans the component cache. After completion, moveToCurrentTag() scrolls the active tab into view using the ScrollPane child component.
Close All Implementation
The "Close All" operation via closeAllTags() dispatches tagsView/delAllViews, which executes DEL_ALL_VISITED_VIEWS and DEL_ALL_CACHED_VIEWS. These mutations preserve only affix tags:
// src/store/modules/tagsView.js
DEL_ALL_VISITED_VIEWS: (state) => {
const affixTags = state.visitedViews.filter(tag => tag.meta.affix)
state.visitedViews = affixTags
}
If the current active view is not an affix tag, toLastView() navigates to the last remaining tab or the home route to prevent 404 errors.
Single Tab Operations
Close: closeSelectedTag() dispatches tagsView/delView, calling both DEL_VISITED_VIEW and DEL_CACHED_VIEW mutations. If closing the active tab, it automatically navigates to the previous view in the history.
Refresh: refreshSelectedTag() removes the component from cachedViews via tagsView/delCachedView, then redirects to a special /redirect route containing the original path. This destroys and recreates the component instance without a full page reload.
State Persistence and Scroll Behavior
Tab state persists for the duration of the SPA session through the Vuex store. Unlike localStorage-based solutions, this approach maintains strict alignment between the tab bar and component cache. The ScrollPane.vue helper component handles horizontal scrolling, ensuring the active tag remains visible after navigation or bulk close operations through moveToCurrentTag().
Summary
- Vuex Centralization: All tab state lives in
src/store/modules/tagsView.jswith separatevisitedViewsandcachedViewsarrays - Affix Protection: Routes with
meta.affixsurvive all bulk deletion operations (Close Others/Close All) - Right-Click Flow: Context menu sets
selectedTag, then dispatches specific store actions that filter arrays while preserving affix entries - Auto-Navigation:
toLastView()prevents orphaned routes by redirecting to remaining tabs when the active tab closes - Component Cache: The
cachedViewsarray directly feeds Vue's<keep-alive>include prop for stateful component preservation
Frequently Asked Questions
How does the TagsView component persist tabs across browser navigation?
The component watches $route changes in src/layout/components/TagsView/index.vue and dispatches Vuex actions to add views to the store. Since the tagsView module maintains visitedViews in memory, tabs persist as long as the SPA remains loaded. For persistence across page reloads, integrate a Vuex persistence plugin like vuex-persistedstate to sync the store with localStorage.
Why are some tabs protected when using Close All or Close Others?
Tabs generated from routes with meta.affix: true (like the dashboard homepage) are filtered and preserved in the DEL_ALL_VISITED_VIEWS and DEL_OTHERS_VISITED_VIEWS mutations. The filterAffixTags() method identifies these during initialization in src/layout/components/TagsView/index.vue, ensuring critical navigation anchors remain available.
How does the Refresh option reload a component without refreshing the entire page?
refreshSelectedTag() dispatches tagsView/delCachedView to remove the component name from cachedViews, clearing the <keep-alive> cache. It then redirects to a special /redirect route containing the original path, causing Vue Router to destroy and recreate the component instance while preserving the URL state.
Where is the horizontal scroll behavior implemented when many tabs are open?
The scroll logic resides in src/layout/components/TagsView/ScrollPane.vue. The TagsView component calls moveToCurrentTag() after navigation or bulk close operations, which queries the DOM for the active <router-link> element and invokes ScrollPane methods to calculate and apply horizontal scroll offsets.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →