laravel-mix: Error with Vue lazy loading components: "Failed to resolve async component"

  • Laravel Mix Version: 4.0.15
  • Node Version: 11.13.0
  • NPM Version: 6.7.0
  • OS: Ubuntu 16.10

Description:

When using Vue async components, and building my application in development mode (run dev or run watch) I receive the following error. But after saving any file to trigger another build, everything works perfectly.

[Vue warn]: Failed to resolve async component: function () {
    return __webpack_require__.e(/*! import() | components */ "components").then(__webpack_require__.bind(null, /*! ./components/partials/navigation/Navigation.vue */ "./resources/js/app/components/partials/navigation/Navigation.vue"));
  }
Reason: TypeError: Cannot read property 'call' of undefined

All JS chunk files are loaded correctly as seen in network debugging.

The Navigation.vue component seems to be the first partial which is included in my base App component.

I’ve splitted all partial Vue components to components.js and router view files to routes.js (using webpackChunkName magic comment with the import function)

Just running run watch / run dev:

             Asset      Size      Chunks             Chunk Names
        /js/app.js  2.31 MiB     /js/app  [emitted]  /js/app
...
  js/components.js  1.11 MiB  components  [emitted]  components
      js/routes.js   516 KiB      routes  [emitted]  routes

Saving any file to trigger re-building:

                                     Asset      Size                     Chunks             Chunk Names
                                /js/app.js  2.32 MiB                    /js/app  [emitted]  /js/app
...
                          js/components.js   1.1 MiB                 components  [emitted]  components
                              js/routes.js   513 KiB                     routes  [emitted]  routes
           js/vendors~components~routes.js  19.5 KiB  vendors~components~routes  [emitted]  vendors~components~routes

What I’ve tried

  • Replace all webpack resolve aliases with relative paths
  • I stubmled across an issue where the the webpack [chunkhash] placeholder in the output.chunkFilename webpack option causes an issue
  • Modifying webpack optimization.splitChunks.chunks option

webpack.mix.js

const mix = require('laravel-mix');
const fs = require('fs');
const webpack = require('webpack');
const tailwind = require('tailwindcss');
const path = require('path');
const glob = require('glob');
const exec = require('child_process').exec;
const pwa = require('sw-precache-webpack-plugin');

mix.webpackConfig({
    output: {
        chunkFilename: 'js/[name].js',
    },
    plugins: [
        new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
    ],
});

mix.version();

// ...

mix.js('resources/js/app/app.js', 'public/js/app.js');

Loading Vue partial components

Vue.component('navigation', () => import(/* webpackChunkName: "components" */ './components/partials/navigation/Navigation.vue'));

Loading Vue routes

import auth from './auth';

let routes = [{
    path: '/',
    name: 'home',
    component: () => import(/* webpackChunkName: "routes" */ '../components/views/Index'),
}];

export default routes.concat(
    auth
);

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 21
  • Comments: 40 (2 by maintainers)

Commits related to this issue

Most upvoted comments

I found that it wasn’t working for me because a vendors file containing style-loader and css-loader was not being created when using async imports.

My workaround was to import a blank scss file into my app.js to force style-loader and css loader to be included in the bundle.

Not ideal but it seems to be working now

Update / Workaround (?)

I just noticed that all failing components contain <style> tags. After removing these everything works normally on dev and production builds.

I was having the same issue with Laravel Mix 4.0. All the failing components contain <style> tags. I was trying to hash my chunks, so my imports look like this:

const Notification = () => import(/* webpackChunkName: "notifications" */ './components/Notification.vue');

And the mix options like this:

mix.js(...)
.webpackConfig({
    output: { 
        chunkFilename: 'js/chunks/[name].js?id=[chunkhash]',
        publicPath: '/',
    }
}).version();

Downgrading to Laravel Mix 3.0 (and upgrading vue?) appears to have solved it:

"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"laravel-mix": "^3.0",
"resolve-url-loader": "^2.3.2",
"vue": "^2.6.10",
"vue-template-compiler": "^2.6.10",

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

After trying all the recommendations (chunknames, requiring empty scss in app.js, etc.) from this thread and in every case ending unsuccessfully, I finally came to a very interesting conclusion I guess.

Let’s assume in our app.js file we are trying to dynamically import two Vue components (both containing styles) as follows:

// app.js
Vue.component('FirstComponent', () => import('path/to/first/FirstComponent'))
Vue.component('SecondComponent', () => import('path/to/first/SecondComponent'))

As we all know, the problem is, that in laravel-mix v4 and v5 these components won’t load and we will get that ugly “failed to resolve” error.

After debugging for hours, I have tested a scenario, when I tried to load the FirstComponent by using the classical import and loading the SecondComponent dynamically. And guess what, the Second Component loaded dynamically without any problems.

// app.js
import FirstComponent from 'path/to/first/FirstComponent'
Vue.component('FirstComponent', FirstComponent)
Vue.component('SecondComponent', () => import('path/to/first/SecondComponent'))

So I came up with an idea of creating a dummy Vue component with some dummy styles, that will be imported at the beginning of app.js. Because this dummy component contains styles and is imported statically, Webpack will trigger the SCSS processing for the dynamically loaded modules as well. This way my real components could be loaded dynamically.

// LaravelMixHack.vue
<template>
    <div>

    </div>
</template>

<script>

export default {
    name: 'LaravelMixHack',
}
</script>

<style scoped lang="scss">
div#nonExistingSCSSIdHack {
    display: none;
}
</style>

The app.js will look as follows:

// app.js
import LaravelMixHack from 'path/to/hack/LaravelMixHack'

Vue.component('FirstComponent', () => import('path/to/first/FirstComponent'))
Vue.component('SecondComponent', () => import('path/to/first/SecondComponent'))

Hope this will save someone from debugging all day.

The thread is polluted on non-related or non useful information.

@redisotschek : Have you read the chris-rodgers solution? https://github.com/JeffreyWay/laravel-mix/issues/2064#issuecomment-511364566

Steps:

  1. Create a empty file with the name fake.scss
  2. Add in your app.js the following line: require(‘fake.scss’);

This will force to load the css-loader, so your Vue styles will be compiled and load.

@romanzipp : I think that is a good idea to close this issue, since we identified the issue and we discovered a workaround.

I am using "laravel-mix": "^5.0.1", and using dynamic import like below:

const TemplateEditor = () => import (
    /* webpackChunkName: "TemplateEditor" */
    './components/TemplateEditor.vue'
);

I was getting this issue only when running npm run prod (I did not observe this when running npm run dev). Also I am not using any <style> in my component.

I solved this by following @juliusvdijk advice, i.e., by adding following lines in webpack.mix.js


mix.webpackConfig({
  output: {
    chunkFilename: "js/chunks/[id].chunk.[chunkhash].js"
  }
});

Adding the following to my webpack config solved the issue.

    output: {
        chunkFilename: "js/chunks/[id].chunk.js?id=[chunkhash]",
    },

thanks @BrandonSurowiec but downgrading laravel mix causes build to fail and introduces other package dependencies with security warnings. I was able to solve my problem by removing laravel-mix from my project.

The problem is in the scss splitting in Vue and using mix.scss() both. Laravel mix when having both creates a css file with manifest js file content in it. which is definitely a bug. which the community mentions a bug from Webpack and will be resolved in webpack 5. But If you use only code splitting in Vue and have the default app.scss file imported to main Vue component like this(not in scope), so each other component will get the styling properly

// resources/js/components/app.vue
<template>
 <!-- Main Vue Component  -->
</template>

<script>
// Main Script
</script>

<style lang="scss">
  @import '~@/sass/app.scss';
</style>

and the webpack.mix.js file will have no mix.scss function to run only a single app.js file. here is my file.

// webpack.mix.js
const mix = require('laravel-mix')
mix.babelConfig({
  plugins: ['@babel/plugin-syntax-dynamic-import'] // important to install -D
})

mix.config.webpackConfig.output = {
  chunkFilename: 'js/[name].bundle.js',
  publicPath: '/'
}

mix
  .js('resources/js/app.js', 'public/js')
  .extract(['vue'])
  .webpackConfig({
    resolve: {
      alias: {
        '@': path.resolve('resources/') // just to use relative path properly
      }
    }
  })

Hope this solves everyone’s question

Just upgraded to the latest version of Mix - 4.0.14. I’m loading some Vue components as above: i.e.

Vue.component('products-grid', () => import('./components/Products/Grid/App.vue'));

Initially Mix compiled fine, but the site displayed a console error stating the dynamic component couldn’t be loaded.

After removing 3 lines of scoped styles from './components/Products/Grid/App.vue the site worked fine and all components load dynamically.

Failing the above, I’d recommend you downgrade to Mix 3 and wait for a newer version of Mix that uses Webpack 5. See https://laravel-mix.com/docs/4.0/upgrade#notes Dynamic import

I also run into this problem when importing CSS for example:

import 'simplemde/dist/simplemde.min.css';

Everything is working fine in watch-mode btw.

I am experiencing the same issue. After upgrading to the latest version of mix and upgrading babel to 7+.

Changing the output does not solve the issue for me.

mix config

.webpackConfig({
    devServer: {
      https: true,
      port: 8080
    },
    output: {
      chunkFilename: "js/chunks/[id].chunk.[chunkhash].js"
    },
    plugins: [
      new CleanWebpackPlugin({
        cleanOnceBeforeBuildPatterns: ['js/*']
      })
    ],
    resolve: {
      alias: {
        '~': path.resolve(__dirname, 'resources/assets/js/'),
        '~store': path.resolve(__dirname, 'resources/assets/js/store'),
        '~helper': path.resolve(__dirname, 'resources/assets/js/helper'),
        '~auth': path.resolve(__dirname, 'resources/assets/js/auth'),
        '~service': path.resolve(__dirname, 'resources/assets/js/service'),
        '~repositories': path.resolve(__dirname, 'resources/assets/js/repositories'),
        '~components': path.resolve(__dirname, 'resources/assets/js/components'),
        '~containers': path.resolve(__dirname, 'resources/assets/js/containers'),
        '~pages': path.resolve(__dirname, 'resources/assets/js/pages'),
        '~placeholders': path.resolve(__dirname, 'resources/assets/js/placeholders')
      }
    }

babel.rc

{
  "presets": [
    "@vue/babel-preset-jsx",
    "@babel/preset-env"
  ],
  "plugins": [
    "@babel/plugin-syntax-dynamic-import"
  ]
}

The error:

vue.common.dev.js?4650:630 [Vue warn]: Failed to resolve async component: function OrganisationCreateForm() {
  return Promise.all(/*! import() | OrganisationCreateForm */[__webpack_require__.e("vendors~OrganisationCreateForm~OrganisationInvitation~SidebarOrganization~UserProfileImage"), __webpack_require__.e("GenericEdit~OrganisationCreateForm~PageCompanyModalCreate~PageCompanyModalEdit~PageCompanyModalShow~~3730cde4"), __webpack_require__.e("OrganisationCreateForm")]).then(__webpack_require__.bind(null, /*! ./Organisation/OrganisationCreateForm.vue */ "./resources/assets/js/containers/Organisation/OrganisationCreateForm.vue"));
}
Reason: TypeError: Cannot read property 'call' of undefined

I don’t have style tags in the vue components. So it’s not related to it.

Using js/chunks/[id].chunk.[chunkhash].js works.

mix.webpackConfig({
  resolve: {
    alias: {
      Api: path.resolve(__dirname, "resources/js/api/"),
      Components: path.resolve(__dirname, "resources/js/components/"),
      Constants: path.resolve(__dirname, "resources/js/constants/"),
      Container: path.resolve(__dirname, "resources/js/container/"),
      Views: path.resolve(__dirname, "resources/js/views/"),
      Helpers: path.resolve(__dirname, "resources/js/helpers/"),
      Themes: path.resolve(__dirname, "resources/js/themes/")
    }
  },
  output: {
    chunkFilename: "js/chunks/[id].chunk.[chunkhash].js"
  }
});

Source: https://github.com/webpack/webpack/issues/959#issuecomment-524293798

I am already using relative paths.

This is my configuration: Tested on: Mac Os with Node v10.11.0 Ubuntu with Node v8.16.0

This is my package.json

{
    "private": true,
    "scripts": {
        "dev": "npm run development",
        "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch": "npm run development -- --watch",
        "watch-poll": "npm run watch -- --watch-poll",
        "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
        "prod": "npm run production",
        "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
    },
    "devDependencies": {
        "axios": "^0.19.0",
        "cross-env": "^5.1",
        "laravel-echo": "^1.5.3",
        "laravel-mix": "^4.0.16",
        "pusher-js": "^4.4.0",
        "resolve-url-loader": "^2.3.1",
        "sass": "^1.21.0",
        "sass-loader": "^7.1.0",
        "ts-loader": "^6.0.2",
        "typescript": "^3.5.1",
        "uuid": "^3.3.2",
        "validator": "^10.11.0",
        "vue": "^2.6.10",
        "vue-property-decorator": "^8.1.1",
        "vue-template-compiler": "^2.6.10",
        "webpack-clean-obsolete-chunks": "^0.4.0"
    }
}

webpack.mix.js

let mix = require("laravel-mix");

mix.webpackConfig({
    output: {
        publicPath: '/',
        chunkFilename: 'js/[name].[chunkhash].js',
    },
    plugins: [
        new (require('webpack-clean-obsolete-chunks'))()
    ],
    module: {
        rules:
        [
            {
                test: /\.tsx?$/,
                loader: "ts-loader",
                options: { appendTsSuffixTo: [/\.vue$/] },
                exclude: /node_modules/
            }
        ]
    },
    resolve: {
        extensions: ["*", ".js", ".jsx", ".vue", ".ts", ".tsx"]
    }
});

mix.babelConfig({
    plugins: [
        '@babel/plugin-syntax-dynamic-import'
    ]
});

mix
  .js("resources/js/app.js", "public/js")
  .sass("resources/sass/app.scss", "public/css");


if (!mix.inProduction())
{
    console.log('📃 Source maps produced!');
    mix.sourceMaps();
}

app.js

window.Vue = require('vue');

/**
 * Test component
 */
Vue.component('bad', () => import(/* webpackChunkName: "bad" */'./components/bad.vue'));
Vue.component('god', () => import(/* webpackChunkName: "good" */'./components/good.vue'));

/**
 * Inject Vue
 */
window.app = new Vue({
    el: '#app'
});

bad.vue

<template>

    <div class="whatever">
        Component
    </div>
</template>

<script>
    export default {
        name: "bad"
    }
</script>

<style>
    .whatever {
        font-weight: bold;
    }
</style>

good.vue

<template>

    <div class="whatever">
        Component
    </div>
</template>

<script>
    export default {
        name: "good"
    }
</script>

Expected result:

  • Both chunks are correctly generated.
  • Both components are correctly loaded and rendered.

Current result:

  • Both chunks are correctly generated.
  • Only the “good” component is loaded and rendered.
  • The following error is received:
[Vue warn]: Failed to resolve async component: function () {
  return __webpack_require__.e(/*! import() */ 6).then(__webpack_require__.bind(null, /*! ./components/bad.vue */ "./resources/js/components/bad.vue"));
}
Reason: TypeError: modules[moduleId] is undefined vue.common.dev.js:630

It seems that the only difference is that “bad.vue” has styles.

I think that maybe the issue is with vue-loader. I will research a bit more about this issue.

@rferons Downgrade to Laravel Mix 3. My post above fixed it for me.

It’s been a while but, I am having this same exact problem. removing styles does fix the problem for me, however removing the styles from all my components is not a feasible solution to this problem. Will continue to dig, but curious if anyone has found any other solution.