3

TLDR

How do you properly lazy load Vue components within a Laravel application, using Vue routing in addition to Laravel's own web routes?

Full story:

I have a Laravel application that is using Vue. Now that the application has grown considerably in size, I would like to lazy load the Vue components.

To do this, I have set up these files:

  • app.js
  • router.js
  • Test.vue
  • app.blade.php
  • home.blade.php
  • news.blade.php

If I import like: import Test from './components/Test';, then everything works fine, but the component isn't lazy loaded.

If I import like

const Test = resolve => {
    require.ensure(['./components/Test'], () => {
        resolve(require('./components/Test'));
    });
}

then, the component is lazy loaded. I can tell this because I am logging to the console. However, this seems to break all other JS and the CSS.

I can also see the new JS file created in the Network tab. It creates a 0.js file.

Based on this other question, I have also tried:

function loadView(view) {
    return () => import(/* webpackChunkName: "[request]" */ `./components/${view}.vue`)
}

Everything works fine here as well, but the component isn't lazy loaded.

app.js

import Vue from 'vue';
import router from './router.js';

new Vue({
    el: '#app',
    router
})

router.js

import Vue from 'vue';
import Router from 'vue-router';
// import Test from './components/Test'; // Everything works, but doesn't lazy load

// Lazy loads and logs to the console, but breaks all of the other JS and the CSS
const Test = resolve => {
    require.ensure(['./components/Test'], () => {
        resolve(require('./components/Test'));
    });
}

// Also tried this way, which works, but does not lazy load
// function loadView(view) {
//     return () => import(/* webpackChunkName: "[request]" */ `./components/${view}.vue`)
// }

Vue.use(Router);

export default new Router({
    routes: [
        {
            path: '/',
            components: {
                test: Test
                // test: loadView('Test') // 
            }
        },
        {
            path: '/news',
            components: {}
        }
    ],
    mode: 'history',
});

Test.vue

<template>
    <div>
        <h1>This is a test</h1>
    </div>
</template>

<script>
    export default {
    }
    console.log('hey, this is from test.vue');
</script>

<style lang="scss" scoped>
</style>

app.blade.php

  • Includes <div id="app"></div> within body

home.blade.php

  • Includes <router-view name="test"></router-view>

news.blade.php

  • Also includes <router-view name="test"></router-view>, just to test.

Update 1:

Based on this question: Lazy Loading Components not working with Vue Router, I have updated the loadView function, but am still not able to load the CSS.

function loadView(view) {
    return () => import(`./components/${view}.vue`)
}

Update 2:

Based on this question: vuejs lazy loading components without the router, I tried to import the component and set it to a constant:

const Test = () => import(
  /* webpackChunkName: "./js/Test" */ './components/Test'
)

The lazy loading works perfectly, but the CSS still doesn't load.

Update 3:

When using import Test from './components/Test';, I notice that the app.css file is compiled successfully to 560 KiB.

However, when using const Test = () => import(/* webpackChunkName: "./js/Test" */ './components/Test');, this same file fails to compile, and instead remains at 0 bytes. 🤔

1 Answer 1

2

The problem here was with Webpack and not the Vue router. The routing was working fine, but as noted in Update 3, the app.css would compile to 0 bytes.

I noticed that this was a common problem that others had as well. The best solution that I found comes from a comment by Daljeet Singh, aka Ilamp, here: https://github.com/JeffreyWay/laravel-mix/issues/1914#issuecomment-628791041

To save you a click, here is what he did:

Install laravel-mix-merge-manifest

npm install laravel-mix-merge-manifest --save-dev

webpack.css.mix.js

const mix = require('laravel-mix');
require('laravel-mix-merge-manifest');

mix.sass('resources/sass/app.scss', 'public/css')
  .version();

if (!mix.inProduction()) {
  mix.sourceMaps();
}

mix.mergeManifest();

webpack.js.mix.js

const mix = require('laravel-mix');
require('laravel-mix-merge-manifest');

mix.react('resources/js/app.js', 'public/js')
  .extract([])
  .setPublicPath('public')
  .version();

if (!mix.inProduction()) {
  mix.sourceMaps();
}

mix.mergeManifest();

Update your package.json's scripts section to:

    "dev": "npm run development-js && npm run development-css",
    "development-js": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --env.mixfile=webpack.js.mix",
    "development-css": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --env.mixfile=webpack.css.mix",
    "prod": "npm run production-js && npm run production-css",
    "production-js": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --env.mixfile=webpack.js.mix",
    "production-css": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --env.mixfile=webpack.css.mix",
    "watch": "npm run development-js -- --watch & npm run development-css -- --watch",
    "watch-poll": "npm run watch -- --watch-poll",

Happy coding 🤓

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.