VueT2 Light_1200x303

Learn how code-splitting works in Nuxt, why you should care and how you can implement it in your Vue apps.

Introduction / TLDR πŸ€“

Recently, someone who is familiar with building standard Vue applications asked me how to deal with code-splitting with Nuxt. I couldn't give him a proper answer so when I got back home, I made myself a good β˜•οΈcup of coffee and started to dive into the subject.

Unfortunately, 😱 I was quite surprised to notice that there weren't a lot of resources about this. And actually, πŸ˜… the reason is simply because you don't have to do anything in order to implement code-splitting in Nuxt.

Yes, Nuxt takes care of code-splitting your application. Nuxt (with the help of webpack) will automatically create a JavaScript file for each page. It will also take care of the project dependencies even if you may have to do some extra work sometimes (more on this in part three). In other words, each route will fetch its JavaScript file, with just the code (and components) that is required to make that route function.

Nonetheless, when reading all the resources I could put my hands on, I learned so many things. πŸ‘©πŸ½‍πŸŽ“ This article aims at summarizing everything I learned to help you understand what's going on behind the curtain, so next time someone asks you how to deal with code-splitting in Nuxt, you will have an answer to give. 🎭

1. First, What Is Code-Splitting, and Why Should You Care about it?

Code-splitting is a term you'll keep hearing all through your life as a developer. If you are not familiar with this practice, πŸ‘©πŸΌ‍🏫 let me define it for you.

In a nutshell, it means splitting your code (the bundle you ship to the browser πŸ—ƒ) into different smaller bundles (also known as chunks πŸ“‚). In other words, it is a technique we use to split our JavaScript code into multiple files.

loaded Chrome chunk under Network tab

πŸ’πŸΌ‍♀‍You can take a look at the loaded chunk in your Chrome console with the Network tab.

Why bother doing this? πŸ€” The purpose is to make the application faster and improve its initial loading speed (especially on mobile devices with slow networks). Since the user doesn't have to download all the code in one hit, he will be able to interact with the page sooner. 🏎

Believe me, I have worked with a standard Vue.js application in which I quickly ended up with hundreds of components and pages. If we hadn't followed this practice from the start, the performance would have been hurt πŸ€• over time.

Another great thing with code-splitting is that when you change one line in your code, the browser does not have to reload your WHOLE bundle. Instead, it can invalidate and reload only the chunks 🎯 that have been modified.

2. Code-Splitting vs. Lazy-Loading

Something worth mentioning is the difference between code-splitting and lazy-loading, as you will often see both of these terms in the same context. I could not tell the difference between these two practices, so to avoid any confusion, let's define them.

First of all, both aim to achieve the same objective, which is speeding up your application.

Code-splitting is a process that involves splitting your code into different smaller files. Instead of getting a single big JavaScript bundle, you will divide it into several files (chunks).

On the other hand, when we refer to lazy-loading, we mean to defer a load of something to only when it is needed. One typical example you may have to deal with is to lazy-load images. Instead of fetching all of them when the page is rendered, we load them at logical breakpoints. Like when they appear near or inside the viewport.

Note: You can use the excellent v-lazy-image package: https://github.com/alexjoverm/v-lazy-image to lazy-load your images.

3. What Should I Know about Code-Splitting with a Nuxt.js Application?

🀹🏼‍♀‍The Process

As we said earlier, Nuxt, through its webpack configuration, takes care of code-splitting your application. Nonetheless, there are a few things you should know, as you can customize this default behavior.

Here is what is going on behind the curtain when you run nuxt build πŸ—:

  1. First of all, it generates a static version of your app

  2. Then, it splits your JS code into multiple files based on its default directory-based routing system (creating code-split points for each route)

  3. For each page's bundle, it loads only the components, dependencies, and so on you need for each page

  4. Another thing to keep in mind is that it prefetches the pages' bundles that are linked to the loaded page through <nuxt-link /> when it appears inside the viewport

Note ⚠️: Be careful, the code generated in development mode is not optimized for production. Some optimizations are disabled, as they would make your workflow less performant (by re-compiling unneeded stuff). To get the right picture of what will precisely happen on your production website, you should always analyze the behavior of your production build with the nuxt build and nuxt start commands.

Of course, if you need to lazy-load a third-party package (e.g., Moment.js) only when something specific happens (πŸ™„ and you don't need it for your whole app), you will have to use webpack's dynamic import function by yourself.

export default {
  methods: {
    async doSomething() {
      const moment = await import("moment");
      // ...
    }
  }
};

Also, as explained in point four, <nuxt-link/> will prefetch the page that it's linked to, when it appears inside the viewport. If you want to disable behavior, set the prefetchLinks to false:

// nuxt.config.js
export default {
  router: {
    prefetchLinks: false
  }
};

I recommend leaveing it set to true, as it should not impact the initial loading of the page since the prefetching is done during πŸ•° idle time. A great rule of thumb is to stay with the default unless you know what you are doing. More on this in the official documentation. πŸ‘ˆπŸ½

🦹🏼‍♀‍ Things Changed as of Nuxt 2

⏩ This is a quick summary of the official release article of Nuxt 2.

  • Nuxt no longer splits layout chunks by default. They will all be loaded alongside the main entry-point. If you want to enable layout splitting, set build.splitChunks.layouts to true.

  • For production builds, the file names are no longer used as a part of chunk names (/_nuxt/pages/foo/bar.[hash].js becomes [hash.js]). The main reason for this change was to avoid an accidental leak of your project internals. You can force enable names back using build.optimization.splitChunks.name set to true.

  • Runtime (manifest) chunk is not split by default by webpack to reduce async requests and is moved into the main chunk. You can enable runtime splitting by setting build.optimization.runtimeChunk to true.

4. BONUS 🌟: I Have a Standard Vue.js Application. How can I Implement Code-Splitting?

Well, don't worry, it's not that hard. πŸ˜‡

Long story short, you will have to rely on webpack's dynamic import function to divide each group of components in their own build file. It will basically use Promise to load them asynchronously.

<template>
  <div class="c-app">
    <div class="c-app__content">
      Obi-Wan Kenobi...Obi-Wan? Now thats a name I haven't heard in a long time...a long time. I think my uncle knew him. He said he was dead. Oh, he's not dead, not...not yet. You know him! Well of course, of course I know him. He's me! I haven't gone by the name Obi-Wan since oh, before you were born. Then the droid does belong to you. Don't seem to remember ever owning a droid. Very interesting... I think we better get indoors. The Sandpeople are easily startled but they will soon be back and in greater numbers. Threepio! Where am I? I must have taken a bad step... Can you stand? We've got to get out of here before the Sandpeople return. I don't think I can make it. You go on, Master Luke. There's no sense in you risking yourself on my account. I'm done for. No, you're not. What kind of talk is that? Quickly, son...they're on the move.
    </div>

    <MyPopup v-if="visible" class="c-app__popup"></MyPopup>
  </div>
</template>
<script>
// Webpack dynamic's import πŸ‘‡
const MyPopup = () => import(/* webpackChunkName: "popup" */ "./MyPopup.vue");

export default {
  data() {
    return {
      visible: false
    };
  },
  components: {
    MyPopup
  }
};
</script>

In this ☝🏼 example, the code of the popup component will only be downloaded when visible is true. The comment you see inside the import function (webpackChunkName: "popup") is called a Magic comment, and its purpose is to customize the name of the chunk.

I have listed the best two articles I could find on the subject right here πŸ‘‡πŸΌ:

Conclusion

Once again, it's just useless to say how excellent Nuxt is and how much it takes off your plate. #SuperNuxt 🦸🏻‍β™‚‍

With code-splitting enabled out-of-the-box, it saves you a lot of time while improving your SEO (as Google penalizes slow-loading sites).

Feel free to tell me in the comments if you have something to add to this article, or you can reach out to me on Twitter @RifkiNada. 🐦


Nada Rifki
About the Author

Nada Rifki

Nada is a JavaScript developer who likes to play with UI components to create interfaces with great UX. She specialises in Vue.js and loves sharing anything and everything that could help her fellow front-end web developers. Nada also dabbles in digital marketing, dance and Chinese.

Related Posts

Comments

Comments are disabled in preview mode.