Frontend Development 12 min read

Deep Dive into Vue Router 4 Component Navigation Guards

This article explains the internal implementation of component‑level navigation guards in Vue Router 4, covering guard categories, the full navigation lifecycle, the execution mechanism, and detailed source‑code analysis with examples of extractComponentsGuards, beforeRouteUpdate, beforeRouteEnter, and beforeRouteLeave.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Deep Dive into Vue Router 4 Component Navigation Guards

Vue Router 4 – Component Guard Deep Dive

The article continues the Vue Router 4 source‑code exploration series , focusing on the component‑level navigation guards. It assumes familiarity with the previous parts that introduced global guards, route‑specific guards, and the overall router architecture.

What You Will Gain

Comprehensive understanding of the core source code behind navigation guards.

Knowledge of the design pattern used for guard implementation.

Insight into the execution flow of component‑level guards.

Navigation Guard Classification

Vue Router 4 defines three major categories of navigation guards:

Global guards – attached to the router instance and triggered on every navigation.

Route‑specific guards – defined in the route configuration and triggered when the matched route is entered.

Component guards – declared inside Vue components and triggered during component creation, update, or before unmount.

Complete Navigation Flow

The navigation process starts with the router’s history listener (e.g., router.push , router.replace , router.go ) and proceeds through a series of guard executions:

History listener triggers navigation.

Global beforeEach guard runs.

If the component is being updated, step 2 runs again followed by the component’s beforeRouteUpdate guard.

If the component is being replaced, step 2 runs again followed by the route’s beforeEnter guard.

Component beforeCreate invokes beforeRouteEnter before rendering.

The beforeResolve guard executes.

After navigation is confirmed, the global afterEach hook runs.

DOM updates are performed.

Before a component is destroyed, the beforeRouteLeave guard runs.

Steps 3, 5, and 9 specifically involve component‑level guards.

Execution Mechanism

All guards are processed using two helper functions:

guardToPromiseFn – wraps a guard callback into a Promise for chainable execution.

runGuardQueue – runs the array of promise‑wrapped guards sequentially.

Component Guard vs. Other Guards

Component guards differ because guardToPromiseFn first extracts the guard functions from the component via extractComponentsGuards . This extraction step is omitted for global and route‑specific guards.

extractComponentsGuards

This utility extracts guard functions from a list of matched route records. Its signature is:

export function extractComponentsGuards(
  matched: RouteRecordNormalized[],
  guardType: GuardType,
  to: RouteLocationNormalized,
  from: RouteLocationNormalizedLoaded
) { … }

The function iterates over each record, validates component types, normalises async components, and builds an array of promise‑wrapped guard functions. Key steps include:

Warning for empty components in development mode.

Conversion of import('./Component.vue') into a lazy‑loaded function.

Handling of components defined with defineAsyncComponent .

Skipping beforeRouteEnter ‑type guards when the component instance is not yet mounted.

Support for class‑style components via the __vccOpts property.

Creation of guard promises using guardToPromiseFn for both synchronous and asynchronous components.

Guard Implementations

beforeRouteUpdate

.then(() => {
  // check in components beforeRouteUpdate
  guards = extractComponentsGuards(
    updatingRecords,
    'beforeRouteUpdate',
    to,
    from
  )
  for (const record of updatingRecords) {
    record.updateGuards.forEach(guard => {
      guards.push(guardToPromiseFn(guard, to, from))
    })
  }
  guards.push(canceledNavigationCheck)
  return runGuardQueue(guards)
})

beforeRouteEnter

.then(() => {
  // clear existing enterCallbacks, these are added by extractComponentsGuards
  to.matched.forEach(record => (record.enterCallbacks = {}))
  guards = extractComponentsGuards(
    enteringRecords,
    'beforeRouteEnter',
    to,
    from
  )
  guards.push(canceledNavigationCheck)
  return runGuardQueue(guards)
})

beforeRouteLeave

function navigate(
  to: RouteLocationNormalized,
  from: RouteLocationNormalizedLoaded
): Promise
{
  let guards: Lazy
[]
  const [leavingRecords, updatingRecords, enteringRecords] =
    extractChangingRecords(to, from)
  // beforeRouteLeave runs first
  guards = extractComponentsGuards(
    leavingRecords.reverse(),
    'beforeRouteLeave',
    to,
    from
  )
  for (const record of leavingRecords) {
    record.leaveGuards.forEach(guard => {
      guards.push(guardToPromiseFn(guard, to, from))
    })
  }
  const canceledNavigationCheck = checkCanceledNavigationAndReject.bind(
    null,
    to,
    from
  )
  guards.push(canceledNavigationCheck)
  return (
    // run beforeRouteLeave guards
    runGuardQueue(guards)
    // ... further navigation steps
  )
}

After extracting and executing the component guards, the router proceeds with the remaining navigation logic.

Images

The article includes several diagrams illustrating the guard categories and the full navigation flow. They are referenced via <img> tags with the original URLs.

Overall, the piece provides a systematic, line‑by‑line walkthrough of how Vue Router 4 processes component‑level navigation guards, offering developers deep insight into the framework’s internal design.

frontendJavaScriptSource CodeVue RouterNavigation GuardComponent Guard
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.