3.4 KiB
Architecture
Ownership boundary
Vue Router owns matching, lazy components, committed routes, redirects, guards, URL serialization, and browser history. Native Vue Router owns a separate visual ledger containing mounted route entries, scroll/focus state, cache status, and the current interactive transaction.
A forward drag calls router.resolve() and Vue Router's public loadRouteLocation(), then renders the location through <RouterView :route> without calling push(). The URL and application history remain unchanged until the gesture commits.
Each preview subtree receives a scoped routeLocationKey, so useRoute() returns preview params even though the global route is not committed. A normal router.push() or replace() runs only after the gesture chooses to commit. A guard failure cancels the transaction and removes the preview.
Transaction lifecycle
Transactions move through interactive, committing, settling, and cancellation states. They expose normalized progress and velocity plus fromKey, toKey, direction, presentation, and optional source geometry.
The runtime deliberately keeps two ledgers. The navigation stack mirrors committed push/replace/pop semantics and is the only source of predictive-back targets. The view cache owns mounted component instances independently, so a replaced tab can be reused without becoming an accidental back destination.
Pointer movement changes only a CSS progress variable. Built-in presentations restrict active-frame work to compositor-friendly properties. Release uses distance/velocity intent and a damped spring. Reduced-motion mode settles immediately.
Component gesture owners stop propagation before a containing navigator can claim the same pointer. Navigator gestures wait for horizontal intent, use pointer capture, respect form controls and data-native-gesture="ignore", and preserve normal vertical scrolling through touch-action: pan-y.
Route metadata
interface NativeRouteOptions {
navigator?: string
presentation?: 'push' | 'reveal' | 'slide' | 'fade' | 'modal' | 'sheet' | string
parent?: RouteLocationRaw | ((route) => RouteLocationRaw)
siblingGroup?: string
siblingOrder?: number
siblingHistory?: 'push' | 'replace'
cache?: boolean
gesture?: boolean | 'edge' | 'full'
}
parent supplies a predictive back target when a deep link starts without an in-memory predecessor. Sibling routes replace history by default and use the built-in slide presentation, which moves both pages one-to-one as adjacent surfaces. Direction comes from siblingOrder. Set siblingHistory: 'push' when browser back should visit prior sibling selections.
Custom presentations
nativeRouter.registerPresentation(definePresentation({
name: 'scale-fade',
axis: 'x',
layerStyle({ role, progress }) {
return role === 'to'
? { opacity: progress, transform: `scale(${0.92 + progress * 0.08})` }
: { opacity: 1 - progress * 0.4 }
},
}))
Applications can call beginInteractive(), updateInteractive(), and finishInteractive() to drive the same transaction engine from a bespoke recognizer.
Cache semantics
The active route and recent inactive routes remain mounted. The default limit is eight inactive views per runtime. Older entries keep their route descriptor but are unmounted and lazily restored when revisited. Application data that must survive eviction belongs in an application store.