# Vue-Element-Admin Layout Component Hierarchy: How Navbar, Sidebar, AppMain, and TagsView Share State

> Discover how vue-element-admin layout components like Navbar Sidebar AppMain and TagsView share state exclusively via Vuex. Learn about mapState mapGetters and actions to manage visibility tags and cache.

- Repository: [花裤衩/vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
- Tags: internals
- Published: 2026-02-27

---

**The layout components in vue-element-admin communicate exclusively through Vuex store modules, using `mapState` and `mapGetters` to read shared state and dispatching actions to mutate sidebar visibility, visited tags, and cached views.**

The vue-element-admin template organizes its interface through a hierarchical layout system where **Navbar**, **Sidebar**, **AppMain**, and **TagsView** operate as independent visual components. Rather than passing props or emitting events through the component tree, this **layout component hierarchy** relies on a centralized state management pattern that ensures perfect synchronization across all UI sections.

## Centralized State Architecture

All shared data lives in dedicated Vuex modules within `src/store/modules/`. The **layout component hierarchy** consumes three primary modules:

- **`app`**: Controls sidebar collapse state (`sidebar.opened`), device type detection (`device`), and UI size settings
- **`tagsView`**: Maintains `visitedViews` arrays and `cachedViews` for `<keep-alive>` persistence
- **`settings`**: Stores UI configuration flags including `tagsView` visibility, `fixedHeader`, and `showSettings`

Source references: [`src/store/modules/app.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/store/modules/app.js) defines `toggleSideBar` and `closeSideBar` actions, while [`src/store/modules/tagsView.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/store/modules/tagsView.js) exports `addView`, `delView`, and `delAllViews` for tag management.

## Layout Wrapper Coordination

The root layout component at [`src/layout/index.vue`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/layout/index.vue) serves as the orchestration layer. It maps global state to local computed properties using `mapState`:

```javascript
computed: {
  ...mapState({
    sidebar: state => state.app.sidebar,
    device: state => state.app.device,
    showSettings: state => state.settings.showSettings,
    needTagsView: state => state.settings.tagsView,
    fixedHeader: state => state.settings.fixedHeader
  })
}

```

When users click the mobile overlay to close the sidebar, the wrapper dispatches the `app/closeSideBar` action without props drilling, ensuring the Sidebar component reacts immediately to the state change.

## Navbar State Management

The [`src/layout/components/Navbar.vue`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/layout/components/Navbar.vue) component demonstrates the read-write pattern. It accesses sidebar status and user avatar through `mapGetters`:

```javascript
computed: {
  ...mapGetters(['sidebar', 'avatar', 'device'])
}

```

When the hamburger icon triggers a toggle, Navbar dispatches the centralized action rather than emitting events upward:

```javascript
methods: {
  toggleSideBar() {
    this.$store.dispatch('app/toggleSideBar')
  },
  async logout() {
    await this.$store.dispatch('user/logout')
    // ...
  }
}

```

This ensures that Sidebar and any other components watching `sidebar` state update simultaneously.

## Sidebar Reactive Rendering

The [`src/layout/components/Sidebar/index.vue`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/layout/components/Sidebar/index.vue) remains a pure consumer of Vuex state. It retrieves accessible routes and sidebar collapse status via `mapGetters`:

```javascript
computed: {
  ...mapGetters(['permission_routes', 'sidebar'])
}

```

The component renders the menu based on `permission_routes` and applies CSS classes according to `sidebar.opened`. It never mutates the store directly; it relies on Navbar or the ResizeHandler mixin to dispatch `app/closeSideBar` or `app/toggleSideBar` when the viewport changes.

## TagsView Synchronization

The [`src/layout/components/TagsView/index.vue`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/layout/components/TagsView/index.vue) coordinates with the router to maintain visited tags. It reads from the `tagsView` module:

```javascript
computed: {
  visitedViews() { 
    return this.$store.state.tagsView.visitedViews 
  },
  routes() { 
    return this.$store.state.permission.routes 
  }
}

```

When the route changes, the component dispatches `tagsView/addView`:

```javascript
watch: {
  $route() {
    this.addTags()
    this.moveToCurrentTag()
  }
},
methods: {
  addTags() {
    if (this.$route.name) {
      this.$store.dispatch('tagsView/addView', this.$route)
    }
  }
}

```

All tag mutations—adding, deleting, refreshing, or closing all—flow through Vuex actions such as `tagsView/delView` and `tagsView/delAllViews`, ensuring AppMain's `<keep-alive>` stays synchronized via the `cachedViews` array.

## AppMain and Keep-Alive Integration

The [`src/layout/components/AppMain.vue`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/layout/components/AppMain.vue) connects the tag system to Vue's component caching. It accesses `cachedViews` from the store:

```javascript
computed: {
  cachedViews() { 
    return this.$store.state.tagsView.cachedViews 
  }
}

```

This computed property feeds the `<keep-alive>` include attribute:

```html
<keep-alive :include="cachedViews">
  <router-view :key="key" />
</keep-alive>

```

When TagsView commits a mutation adding a route to `cachedViews` (typically when `meta.noCache` is false), AppMain automatically includes that component in the keep-alive cache without direct component-to-component communication.

## Global Resize Handling

The [`src/layout/mixin/ResizeHandler.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/layout/mixin/ResizeHandler.js) provides cross-cutting responsive behavior. It dispatches Vuex actions when viewport dimensions change:

```javascript
methods: {
  $_resizeHandler() {
    if (!document.hidden) {
      const isMobile = this.$_isMobile()
      this.$store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
      
      if (isMobile) {
        this.$store.dispatch('app/closeSideBar', { withoutAnimation: true })
      }
    }
  }
}

```

This ensures the **layout component hierarchy** remains synchronized when users resize their browser or switch devices, automatically closing the sidebar on mobile without requiring individual components to handle resize logic.

## Summary

- **Vuex as single source of truth**: All layout state resides in `app`, `tagsView`, `settings`, and `permission` modules, eliminating prop drilling.
- **Read via getters**: Components use `mapState` and `mapGetters` to access sidebar status, visited tags, cached views, and device type.
- **Write via actions**: Mutations occur exclusively through dispatched actions like `app/toggleSideBar`, `tagsView/addView`, and `tagsView/delView`.
- **Automatic synchronization**: Changes propagate reactively to all dependent components, keeping Sidebar, Navbar, TagsView, and AppMain perfectly aligned without direct references.
- **Cross-component coordination**: Actions in one component (e.g., Navbar toggling the sidebar) instantly affect the entire layout hierarchy through shared Vuex state.

## Frequently Asked Questions

### How does the Sidebar know when to collapse without receiving props from Navbar?

The Sidebar component in [`src/layout/components/Sidebar/index.vue`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/layout/components/Sidebar/index.vue) reads the collapse state directly from Vuex using `mapGetters(['sidebar'])`. When Navbar dispatches `app/toggleSideBar`, the Vuex mutation updates the state, and Sidebar's computed property reacts automatically. This eliminates the need for prop passing or event emitting between siblings.

### Why does TagsView use `$store.state` directly instead of mapGetters?

While `mapGetters` would work, TagsView in [`src/layout/components/TagsView/index.vue`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/layout/components/TagsView/index.vue) accesses `visitedViews` and `routes` via direct state references (`this.$store.state.tagsView.visitedViews`) to illustrate explicit dependency on the raw state structure. Both approaches are valid Vuex patterns; the project uses `mapGetters` in components like Navbar and Sidebar for brevity, while TagsView demonstrates direct store access for clarity in tracking visited routes.

### How does AppMain keep router views cached without communicating with TagsView?

AppMain in [`src/layout/components/AppMain.vue`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/layout/components/AppMain.vue) binds the `<keep-alive>` include attribute to `this.$store.state.tagsView.cachedViews`. When TagsView dispatches `tagsView/addView` for a route with `meta.noCache !== true`, the Vuex module commits the route name to `cachedViews`. AppMain's computed property detects this change and updates the keep-alive include list, caching the component instance without direct component interaction.

### What triggers the sidebar to close automatically on mobile devices?

The [`src/layout/mixin/ResizeHandler.js`](https://github.com/PanJiaChen/vue-element-admin/blob/main/src/layout/mixin/ResizeHandler.js) mixin dispatches `app/toggleDevice` and `app/closeSideBar` when the viewport width falls below a mobile threshold (typically 992px). This global handler ensures the sidebar closes without animation on mobile devices, maintaining responsive behavior across the entire layout component hierarchy. The Sidebar component reacts to this state change through its `sidebar` computed getter, applying CSS classes to hide or show the menu.