Enhance the Vue.js performance by preloading components

After discovering the benefits of lazy loading components, I decided to start implementing it in my project. However, I encountered some issues when trying to prefetch the lazy loaded components and vue-router routes. Upon inspecting with Chrome DevTools, I noticed that the lazy loaded chunks are only loaded when we navigate to the specific route or when a v-if statement evaluates to true, rendering the component.

I attempted to utilize the webpackPrefetch: true string in both the router configuration and component import statement, but unfortunately, it did not have any noticeable impact.

Project structure:
Master-Detail layout

router config:

import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);

var routes = [  
  {
    path: "/DetailPage",
    component: () => import(/* webpackChunkName: "Detail-chunk" */ "AppModules/views/MyModuleName/DetailPage.vue")
  },
  {
    path: "/MasterPage",
    component: () => import("AppModules/views/MyModuleName/MasterPage.vue")
  }
]

export const router = new Router({
  routes: routes,
  stringifyQuery(query) {
    // encrypt query string here
  }
});

export default router;

Master view:

<template> 
  <div @click="navigate">
    Some text
  </div>
</template>

<script>
export default {
  name: "MasterPage",
  methods: {
    navigate() {
      this.$router.push({
        path: "/DetailPage",
        query: {},
      });
    },
  },
};
</script>

Details page:

<template>
  <div>    
    <my-component v-if="showComponent" />
    <div @click="showComponent = true">Show Component</div>
  </div>
</template>

<script>
const MyComponent = () => import(/* webpackChunkName: "MyComponent-chunk" */ "AppCore/components/AppElements/Helpers/MyComponent");
export default {
  name: "DetailPage",
  components: {
    MyComponent,
  },
  data() {
    return {
      showComponent: false
    }
  }
};
</script>

vue.js.config file:

const path = require("path");

const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
  .BundleAnalyzerPlugin;

module.exports = {
  publicPath: "some-url",
  outputDir: "./some/path",
  chainWebpack: webapckConfig => {
    webapckConfig.plugin("html").tap(() => {
      return [
        {
          inject: true,
          filename: "index.html",
          template: "./public/index.html"
        }
      ];
    });
  },
  productionSourceMap: true,
  configureWebpack: {
    plugins: [
      new BundleAnalyzerPlugin({
        analyzerMode: "server",
        generateStatsFile: false,
        statsOptions: {
          excludeModules: "node_modules"
        }
      })
    ],
    output: {
      filename: "some file name",
      libraryTarget: "window"
    },
    module: {
      rules: [
        {
          test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
          use: [
            {
              loader: "url-loader",
              options: {
                limit: 50000,
                fallback: "file-loader",
                outputPath: "/assets/fonts",
                name: "[name].[ext]?hash=[hash]"
              }
            }
          ]
        }
      ]
    },
    resolve: {
      alias: {
        vue$: process.env.NODE_ENV == 'production' ? 'vue/dist/vue.min.js' : 'vue/dist/vue.js',
        AppCore: path.resolve(__dirname, "..", "..", "AppCoreLite"),
        AppModules: path.resolve(__dirname, "..", "..", "AppModulesLite")
      }
    }
  }
};

Despite splitting async routes and components into separate chunks, these chunks are not prefetched. When navigating to the master view, Detail-chunk.[hash].js is not seen in the network tab until the navigation method is triggered. Similarly, on the details page, MyComponent-chunk.[hash].js is only requested when showComponent becomes true upon clicking a button.

It's worth noting that there are claims stating that vue-cli v3 has prefetch functionality enabled by default and does not require the webpack magic string. Removing the webpackPrefetch comment did not yield any changes in behavior.

When running vue-cli-service inspect, I confirmed that the prefetch plugin is indeed present in the webpack config:

 /* config.plugin('preload') */
    new PreloadPlugin(
      {
        rel: 'preload',
        include: 'initial',
        fileBlacklist: [
          /\.map$/,
          /hot-update\.js$/
        ]
      }
    ),
    /* config.plugin('prefetch') */
    new PreloadPlugin(
      {
        rel: 'prefetch',
        include: 'asyncChunks'
      }
    ),

UPDATE: Despite attempting to remove the prefetch webpack plugin and utilizing the webpack magic comment, no difference was observed.

How can I successfully implement prefetch functionality in my project?

Answer №1

My solution involved the development of a straightforward prefetch component that initiates after a specified duration.

PrefetchComponent.vue

<script>
import LazyComp1 from "./LazyComp1.vue";
import LazyComp2 from "./LazyComp2.vue";
export default {
    components:{
        LazyComp1,
        LazyComp2,
    }
}
</script>

MainApp.vue

<template>
    <PrefechLoader v-if="loadPrefetch"></PrefechLoader>
</template>
<script>
export default {
    components: {
        PrefechLoader: () => import("./PrefetchComponent");
    },
    data() {
        return {
            loadPrefetch: false
        }
    },
    mounted() {
        setTimeout(() => {
            this.loadPrefetch = true;
        }, 1000);
    }
}
</script>

Answer №2

due to issues with vue-router-prefetch, I manually implemented route prefetching.

Vue 3 Sample - loading async components for all routes on page load

const router = createRouter({
    history: createWebHistory(),
    routes: [{
        path: '/',
        component: HomeView
    }, {
        path: '/about',
        component: () => import('./views/AboutView.vue')
    }]
});

async function preloadAsyncRoutes() {
    // iterate through all routes and prefetch async components if any
    for (const route of router.getRoutes()) {
        if (!route.components) continue;

        // loop through components in case named views are used
        for (const componentOrImporter of Object.values(route.components)) {
            if (typeof componentOrImporter === 'function') {
                try {
                    // prefetch the component and wait for it to finish before moving to the next one
                    await componentOrImporter();
                } catch (err) {
                    // handle failed requests gracefully
                }
            }
        }
    }
}

window.addEventListener('load', preloadAsyncRoutes);

Answer №3

Lazy loaded elements are designed to be activated only upon the user's interaction with the route. If you desire to load an element prior to this, simply refrain from employing lazy loading.

vue-router will preload components into memory and switch out the tag's content dynamically, even when utilizing non-lazy loaded components.

Answer №4

To fulfill your requirements, consider incorporating the vue-router-prefetch package. A functional demonstration is available for reference.

Important: The demo showcases that only page 2 is prefetched by the QuickLink component, which has been imported from the vue-router-prefetch.

Code:

import Vue from "vue";
import Router from "vue-router";
import RoutePrefetch from "vue-router-prefetch";

Vue.use(Router);

Vue.use(RoutePrefetch, {
  componentName: "QuickLink"
});

const SiteNav = {
  template: `<div>
    <ul>
      <li>
        <router-link to="/page/1">page 1</router-link>
      </li>
      <li>
        <quick-link to="/page/2">page 2</quick-link>
      </li>
      <li>
        <router-link to="/page/3">page 3</router-link>
      </li>
    </ul>
  </div>`
};
const createPage = (id) => async() => {
  console.log(`fetching page ${id}`);
  return {
    template: `<div>
          <h1>page {id}</h1>
          <SiteNav />
        </div>`,
    components: {
      SiteNav
    }
  };
};

const routers = new Router({
  mode: "history",
  routes: [{
    path: "/",
    component: {
      template: `<div>
          <h1>hi</h1>
          <SiteNav />
        </div>`,
      components: {
        SiteNav
      }
    }
  }]
});

for (let i = 1; i <= 3; i++) {
  routers.addRoutes([{
    path: `/page/${i + 1}`,
    component: createPage(i + 1)
  }]);
}

export default routers;

Answer №5

I am currently developing a mobile application and I need to dynamically load some components while displaying the splash screen.

Although @Thomas's suggestion of using a Prefetch component is helpful, it does not load the component in the shadow DOM and does not pass Vetur validation (as each component must have its template).

Below is the code I have implemented:

Main.vue

<template>
    <loader />
</template>
<script>
    import Loader from './Loader'
    const Prefetch = () => import('./Prefetch')
    export default {
        name: 'Main',
        components: {
            Loader,
            Prefetch
        }
    }
</script>

Prefetch.vue

<template>
    <div id="prefetch">
        <lazy-comp-a />
        <lazy-comp-b />
    </div>
</template>
<script>
import Vue from 'vue'

import LazyCompA from './LazyCompA'
import LazyCompB from './LazyCompB'

Vue.use(LazyCompA)
Vue.use(LazyCompB)
export default {
    components: {
        LazyCompA,
        LazyCompB
    }
}
</script>

<style lang="scss" scoped>
#prefetch {
    display: none !important;
}
</style>

The loader component is loaded and rendered first, allowing the Prefetch component to dynamically load content afterwards.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Maximizing Efficiency: Chaining Several Actions Using the JavaScript Ternary Operator

When the condition is true, how can I chain two operations together? a = 96 c = 0 a > 50 ? c += 1 && console.log('passed') : console.log('try more') I attempted chaining with && and it successfully worked in react, b ...

How can you toggle the visibility of a text based on the selection of a jQuery radio button?

Is it possible to dynamically enable or disable a field based on the selection of a radio button? Here are the rules: The field should only be active when the "Inactive" radio button is selected Check out the code snippet below for reference: Radio B ...

Struggling to get getInitialProps working in dynamic routes with Next.js?

I am encountering an issue. The return value from the getInitialProps function is not being passed to the parent component. However, when I console.log the values inside the getInitialProps function, they appear to be correct. Here is the code snippet: i ...

Post a message utilizing javascript

Can a client-side tweet be generated using JavaScript, a text box, and a submit button? This involves entering the tweet text into the textbox, clicking the button, and then tweeting it with an authenticated account all within the client's browser. ...

The SelectedIndexChanged event fails to trigger following an onchange event

I am currently working on validating a dropdown list on the client side based on its selected index/value. My goal is to create a function that triggers an alert when the selected index is 0, or alternatively execute the SelectedIndexChandged method in the ...

AngularJS expression utilizing unique special character

There are certain special characters (such as '-') in some angular expressions: <tr data-ng-repeat="asset in assets"> <td>{{asset.id}}</td> <td>{{asset.display-name}}</td> <td>{{asset.dns-name}}</td&g ...

Guide to retrieving SQL data using Node.js with mysql2

I've decided to convert my code from Python to Node.js, and I'm encountering a problem when trying to retrieve data from SQL. In Python, it's simple to get the data, return it, and then use it in the code. Here is the Python code: def sql_g ...

What could be causing the issue with my dynamic sitemap.xml file not functioning properly?

I have encountered an issue with creating a dynamic sitemap in my Next.js app. Despite following the instructions in the Next.js documentation and placing a sitemap.js file inside my app directory, I am seeing a 404 error when trying to access http://local ...

Leveraging the power of HTML5 alongside Angularjs and Node/Express within the MEAN.js boilerplate framework

I've decided to kickstart my app using the mean.js () boilerplate because of its built-in authentication/authorization features. However, I've hit a roadblock when it comes to incorporating HTML5 into my project. In my Angular app, I've en ...

Tips on saving information received from an API using Vue and axios into a variable that can be easily utilized in another tool or library such as chart.js

I'm struggling to save information from an API into a variable using Vue and axios. However, when I attempt to display the variable with document.write(this.info), it only shows undefined. How can I ensure that the variable contains data retrieved fro ...

Ways to detect when the window printing process has been completed

Within my application, I attempted to generate a voucher page for the user using the following code: var htm ="<div>Voucher Details</div>"; $('#divprint').html(htm); window.setTimeout('window.print()',2000); The &apo ...

VueJs: Finding the corresponding value of a data object

Is there a way to extract data from an object in Vue? This is the format of my data: datasets: [{ text:"Cars", value: "[1,2,3]" }, { text:"Trains", value: "[1,4,10] } ] When I receive information from route props like this: this.selectedText= this ...

Webstorm showcases files with similar content in a distinct manner

In Webstorm, everything is color-coded based on whether it is a function, object, etc. I am working with JavaScript. I have noticed that in two different files, the same line of code looks differently: var Comment = React.createClass({ In file 1: "var" ...

Is there a way to retrieve the data selected in my modal window when a button is clicked, depending on the v-for value?

As someone who is new to Vue and utilizing Bootstrap modals to showcase product information, I have grid containers that contain a product image, description, and two buttons. One of the buttons, labeled More details >>, is intended to trigger a moda ...

Utilizing jQuery TableSorter Scroller: How to Achieve Scrolling with Keydown Focus

Apologies for any language barriers. I am currently utilizing tablesorter 2.0 along with scroller functionality. On my page, the table scrolling function works when I interact with the table directly. However, I want the scrolling to happen automatically ...

Adding Node Modules during the setup of an ElectronJS application

Hey there! I'm currently working on an ElectronJS application designed for developers. One of the key features is checking for the presence of NodeJS on the user's computer. If it's not detected, the app will automatically download and insta ...

The loading on the Express endpoint does not cease, despite configuring the response status (Encountering problems with Multer and Express)

Currently, I am in the process of developing a filter that evaluates a file's signature obtained through a file's buffer provided by Multer. While Multer supplies the MIME type, additional validation methods are required to confirm if the file ma ...

Is there a better method to accomplish this task in a more effective manner?

Is there a more efficient way to achieve this code with fewer lines of code? I'm looking for a solution that avoids repetition and makes it easier to manage, especially since I plan on creating multiple instances of this. Performance is a key consider ...

Sort columns in a MUI datatable

I am facing an issue with sorting in a column that represents an object. Although I can display the desired value, the sorting functionality does not seem to work for that particular column. Here is an example to provide better clarity: const [data, set ...

Tips for integrating @mdx-js/loader with your create-react-app project

Following the guidelines found at: The steps I took were: $ npx create-react-app react-app $ cd react-app $ npm install @mdx-js/loader Then, according to the instructions, I created the src/content.mdx file like this: # Hello, world! This is **markdown* ...