Skip to content

Latest commit

 

History

History
96 lines (73 loc) · 3.9 KB

lazy-loading.md

File metadata and controls

96 lines (73 loc) · 3.9 KB

Lazy Loading Routes

When building apps with a bundler, the JavaScript bundle can become quite large, and thus affect the page load time. It would be more efficient if we can split each route's components into a separate chunk, and only load them when the route is visited.

Combining Vue's async component feature and webpack's code splitting feature, it's trivially easy to lazy-load route components.

First, an async component can be defined as a factory function that returns a Promise (which should resolve to the component itself):

const Foo = () => Promise.resolve({ /* component definition */ })

Second, in webpack 2, we can use the dynamic import syntax to indicate a code-split point:

import('./Foo.vue') // returns a Promise

Note: if you are using Babel, you will need to add the syntax-dynamic-import plugin so that Babel can properly parse the syntax.

Combining the two, this is how to define an async component that will be automatically code-split by webpack:

const Foo = () => import('./Foo.vue')

Nothing needs to change in the route config, just use Foo as usual:

const router = new VueRouter({
  routes: [
    { path: '/foo', component: Foo }
  ]
})

Grouping Components in the Same Chunk

Sometimes we may want to group all the components nested under the same route into the same async chunk. To achieve that we need to use named chunks by providing a chunk name using a special comment syntax (requires webpack > 2.4):

const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')

webpack will group any async module with the same chunk name into the same async chunk.

Handling Loading State

Async component factories can also return an object, specifying alternative components to use when loading or if an error occurs. This is a great way to improve user experience on slow or intermittent connections.

Unfortunately, routes can currently only resolve to a single component in Vue Router. So to make this work, you'll need a helper function to create an intermediary component, like this:

function lazyLoadView (AsyncView) {
  const AsyncHandler = () => ({
    component: AsyncView,
    // A component to use while the component is loading.
    loading: require('./Loading.vue').default,
    // A fallback component in case the timeout is exceeded
    // when loading the component.
    error: require('./Timeout.vue').default,
    // Delay before showing the loading component.
    // Default: 200 (milliseconds).
    delay: 400,
    // Time before giving up trying to load the component.
    // Default: Infinity (milliseconds).
    timeout: 10000
  })

  return Promise.resolve({
    functional: true,
    render (h, { data, children }) {
      // Transparently pass any props or children
      // to the view component.
      return h(AsyncHandler, data, children)
    }
  })
}

Then you can pass the import() to lazyLoadView for smarter, lazy-loaded view components:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: () => lazyLoadView(import('./Foo.vue'))
    }
  ]
})

WARNING: Components loaded with this strategy will not have access to in-component guards, such as beforeRouteEnter, beforeRouteUpdate, and beforeRouteLeave. If you need to use these, you must either use route-level guards instead or lazy-load the component directly, without handling loading state.