Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue js Prefetch components

I recently learnt about lazy loading components and started using it. Now I am trying to prefetch the lazy loaded components as well as vue-router routes. But using the chrome devtools I found that lazy loaded chunks are only loaded when we actually navigate to the lazy loaded route (in case of a vue-router route) or when the v-if evaluates to true and the component is rendered (in case of a lazy loaded component).

I have also tried using the webpackPrefetch: true magic string in the router as well as component import statement but doing that does not seem to make any difference.

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")
      }
    }
  }
};

Both the async route and component do get split into separate chunks but these chunks are not prefetched.
When I navigate to the master view, I dont see Detail-chunk.[hash].js in the network tab. It gets requested only when the navigate method in the master page is executed (this the correct lazy load behaviour without prefetch).
Now when I am on the details page, MyComponent-chunk.[hash].js is only requested when the showComponent becomes true (on click of a button)
I've also read at a few places that vue-cli v3 does has prefetch functionality enabled by default and webpack magic string is not needed. I also tried that by removing the webpackPrefetch comment but it made no difference.

I did vue-cli-service inspect and found that 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: I tried removing the prefetch webpack plugin using config.plugins.delete('prefetch'); and then using the webpack magic comment: /* webpackPrefetch: true */ but it made no difference.

How do I implement prefetch functionality?

like image 520
ravi kumar Avatar asked Nov 16 '22 06:11

ravi kumar


1 Answers

I solved this by creating a simple prefetch component that loads after a custom amount of time.

Prefetch.vue

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

App.vue

<template>
    <Prefech v-if="loadPrefetch"></Prefech>
</template>
<script>
export default {
    components: {
        Prefech: () => import("./Prefetch");
    },
    data() {
        return {
            loadPrefetch: false
        }
    },
    mounted() {
        setTimeout(() => {
            this.loadPrefetch = true;
        }, 1000);
    }
}
</script>
like image 194
Thomas Avatar answered Jan 03 '23 19:01

Thomas