Description
What problem is this solving
Description
Vue Router currently calculates the scroll position on every router.push navigation by invoking the computeScrollPosition function, which accesses window.pageXOffset and window.pageYOffset. While this behavior is useful for maintaining scroll position, it triggers layout recalculations that can negatively impact performance, especially on low-powered devices.
Given that JavaScript applications built with Vue.js can run across a variety of platforms — from mobile phones to high-performance computers, embedded devices, and Smart TVs — it's essential to account for environments where layout-intensive operations can hinder the user experience.
Motivation
The layout calculation triggered by accessing scroll-related properties is particularly expensive on embedded devices and Smart TVs, where hardware constraints may limit performance. Reducing unnecessary layout operations is critical for ensuring smooth navigation and an optimal user experience on these platforms.
Applications on such devices couldn't use scroll behaviour at all and such feature is redudant for these platforms.
Currently, Vue Router does not offer a way to skip or control these scroll position calculations, resulting in performance overhead even when maintaining scroll position is unnecessary.
Proposed solution
We propose extending the Vue Router API with an option to disable scroll position calculations on navigation. This could be configured either globally or on a per-route basis, providing developers with more control over performance-critical scenarios.
const router = createRouter({
history: createWebHistory(),
routes,
scrollPositioning: {
calculateScrollPosition: false, // Disable scroll position calculation
},
});
This feature would ensure that developers working on performance-sensitive applications — especially for embedded systems or Smart TVs — can reduce the performance impact associated with layout recalculations.
Another alternative solution could be to add third parameter to push
function to avoid scroll position computation, like it has the buildState
function.
function push(to, data, calculateScrollPosition) {
// Add to current entry the information of where we are going
// as well as saving the current position
const currentState = assign({},
// use current history state to gracefully handle a wrong call to
// history.replaceState
// https://github.com/vuejs/vue-router-next/issues/366
historyState.value, history.state, {
forward: to,
scroll: calculateScrollPosition ? computeScrollPosition() : {
left:0,
top: 0
},
});
if ((process.env.NODE_ENV !== 'production') && !history.state) {
warn(`history.state seems to have been manually replaced without preserving the necessary values. Make sure to preserve existing history state if you are manually calling history.replaceState:\n\n` +
`history.replaceState(history.state, '', url)\n\n` +
`You can find more information at https://next.router.vuejs.org/guide/migration/#usage-of-history-state.`);
}
changeLocation(currentState.current, currentState, true);
const state = assign({}, buildState(currentLocation.value, to, null), { position: currentState.position + 1 }, data);
changeLocation(to, state, false);
currentLocation.value = to;
}
Describe alternatives you've considered
A workaround could be manually overriding the window.pageXOffset
and window.pageYOffset
properties to skip layout calculations. However, this approach, modifying window object, could be error prone in the context of entire applications.
Metadata
Assignees
Labels
Type
Projects
Status
🧑💻 In progress
Activity