Lazy load components as a general practice

Vue allows us to build out infinite blocks of components that can work together or build on top of another to provide UIs of increasing sophistication. You may have a page or view that loads dozens of components including fields, tabs, charts and so on, and the loading of such components is automatically taken care by Vue.

Consider this simple example that uses Vuetify and Vue - Todo view, which is the base view to show to-dos.

<template>
  <div>
    <Panel title="Todo">
      <template slot="content">
        <v-layout row wrap>
          <v-flex xs12>
            <v-alert :value="message" dismissible>{{ message }}</v-alert>
          </v-flex>
          <v-flex xs12 md6>
            <TodoList></TodoList>
          </v-flex>
          <v-flex xs12 md3>
            <TodoMainChart></TodoMainChart>
          </v-flex>
        </v-layout>
      </template>
    </Panel>
    <TabPanel></TabPanel>
  </div>
</template>

<script>
  import Panel from "../components/Panel";
  import TodoList from "../components/TodoList";
  import TodoMainChart from "../components/TodoMainChart";
  import TabPanel from "../components/TabPanel";

  export default {
    components: { Panel, TodoList, TodoMainChart, TabPanel }
  };
</script>

Todo not only has to load itself with data and scripts, but also load everything from Panel, TodoList, TodoMainChart, TabPanel, and all child components that go under these components.

While Vue applications are pretty fast / nimble and you will likely not notice the transition if your components and data are manageable, we leverage any opportunity whatsoever to better user experience.

One of the efficient ways of doing that is to asynchronously load components that may not be required when base component loads, and split the code so that unnecessary JS is not downloaded all at once. Ergo, lazy loading and code splitting.

For e.g. TodoMainChart and components in TabPanel may wait until later, while the initial components get loaded and user can start interacting with the view.

The async loading of components can be accomplished in two steps -

  1. Local registration of components.
    Instead of doing a global registration, we use local and use functions that return a promise.

    <!-- code -->
    
    <script>
     import Panel from "../components/Panel";
     import TodoList from "../components/TodoList";
     export default {
       components: {
         Panel,
         TodoList,
         AccountNew: () => import("./AccountNew"),
         TabPanel: () => import("./TabPanel")
       }
     };
    </script>
  2. Define component as a promise and resolve component definitions. We use this component to initiate load of the actual component scripts when called into action by the UI. The below will load component after 3 seconds - it can also be called at button click or made to react to user-initiated event like scrolling.

    Vue.component("TodoList", function(resolve, reject) {
     setTimeout(function() {
       resolve(require("./components/TodoListReal.vue"));
     }, 3000);
    });
    

What I do in my applications is to use local registration and go about my business as usual. I get back to more complex UI based on UI complexity in the “second pass” - either based on perceived complexity (from dev. team), or issues found during performance tests.

comments powered by Disqus