Skip to content

Add support to avoid scroll position calculation on router push #2393

Open
@index23

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    • Status

      🧑‍💻 In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions