node -v
): 8.5.0npm -v
): 5.4.2Does anyone know how to get Vue.js async components to work? Getting the following error:
```error in ./resources/assets/js/app.js
Syntax Error: Unexpected token (10:25)
8 | el: '#app',
9 | components: {
10 | 'example': () => import('./components/Example')
| ^
11 | }
12 | });```
components: {
example: require('./components/Example')
}
@ruchern That's the normal way of pulling it in, I'm trying to do it asynchronously instead.
https://stackoverflow.com/questions/45993715/how-to-add-dynamic-async-lazy-components-in-vuejs
Laravel mix should support this by default.
@ankurk91 Why is that not a Vuejs
problem instead but a Laravel Mix
one?
@ruchern
It is specific to webpack (tooling), after all it is babel who will transform the code and will pass to uglify-js. So it is upto end user to configure and enable modern feature.
@pix2D
鈿狅笍 Updated for Laravel Mix v3+
npm install --save-dev @babel/plugin-syntax-dynamic-import
.babelrc
in your project root, and specify the plugin{
"plugins": ["@babel/plugin-syntax-dynamic-import"]
}
// resources/js/routes.js
{
path: 'post/:id(\\d+)',
// notice the dynamic import
component: () => import('./components/post/show.vue'),
name: 'post.show',
meta: {
title: 'Show post',
}
}
npm run dev
PS
If you want to load components via computed property, read this
@ankurk91 Thank you, that works perfectly. Any idea how to make the files go to public/js instead of public?
@pix2D
See my last point. (not tested).
ref #1055, #936
@ankurk91 Thanks, that works great.
Anyone know how to get this working when programmatically mounting components afterwards?
@emielmolenaar
can you share an example on how you want it ?
@ankurk91 ,
Using dynamic import - all works, chunks loading. Thanks for your input!
I guess @emielmolenaar faces same issue i have..
There's an issue when i use dynamic computed component name using (
return () => import(
./${this.componentFile});
Can u assume why this generates 9 chunks? And when component conditionally loads - browser loads only one chunk of that 9.
Where to dig?
@ankurk91 We have implemented vue.js together with async components in a somewhat older app. The only legacy library that is left is datatables.net, which parses HTML tables by received JSON stuff from the server. The server generates some HTML for action buttons, which may contain stuff like
<a is="SomeComponent"></a>
We then use some hacks to mount the components after the DOM has changed:
function mountVueComponents(table) {
let elements = Array.prototype.slice.call(table[0].querySelectorAll('td div[is]'), 0);
if (elements.length > 0) {
elements.forEach(function (element) {
try {
/**
* Hacky way to mount Vue components
*/
let component = Vue.component(element.getAttribute('is'));
let args = {
propsData : {},
parent : window.vm
};
for (let i = 0; i < element.attributes.length; i++) {
let attrib = element.attributes[i];
if (attrib.specified && attrib.name != 'is') {
args.propsData[_.camelCase(attrib.name)] = attrib.value;
}
}
new component(args).$mount(element);
} catch(error) {
console.log(error);
}
});
}
}
Until we have moved away from datatables.net, when have to work this way... This function works for now, but not with components that are loaded asynchronous. Any hint in the right direction? Thanks!
@emielmolenaar
I have not tried on-demand imports or import callback yet but i found some stuff, here are links-
http://2ality.com/2017/01/import-operator.html
https://github.com/tc39/proposal-dynamic-import
https://webpack.js.org/guides/code-splitting/#dynamic-imports
You should be able do something like this -
import('./myComponent.js')
.then(({default: theDefault}) => {
console.log(theDefault);
// do mount operation here
});
Any plans in the future to have the babel stuff included and pre-configured, or is this not a common enough thing that users ask for which means we should just configure it manually when we need it?
@raniesantos
PR welcome
@ankurk91
Sorry, but I have a problem about the import. It generates chunk js files correctly, but when it tries to download the js file, it uses relative paths instead of absolute paths. Therefore, it requests the wrong url and an error occurs.
For example, I have 0.js
in my public
folder. But when I visit dynamic page /ABC/DEF
, it just requests /ABC/0.js
and results in an error...
Thank you in advance for your help!
@turtlegood
Never faced this issue. Cant say anything now.
@ankurk91 Could you please paste the code of how you use it? I think maybe it is because of some code which you did not paste and I did wrong.
@ankurk91 My case:
.webpackConfig({
plugins: [
new webpack.ProvidePlugin({
jQuery: 'jquery',
$: 'jquery',
jquery: 'jquery',
'window.jQuery': 'jquery',
}),
new webpack.ProvidePlugin({
Promise: 'es6-promise-promise'
})
],
})
.js('resources/assets/js/app.js', 'public/gen')
.extract([
'jquery',
'vue',
'vue-router',
'./resources/assets/js/libs/materialize.js',
......
])
.version()
.sass('resources/assets/sass/app.scss', 'public/gen')
.styles([
'resources/assets/sass/libs/materialize.css',
......
], 'public/gen/vendor.css')
Thank you very much in advance!
@turtlegood Please look at https://github.com/JeffreyWay/laravel-mix/issues/863#issuecomment-324332719 or https://github.com/JeffreyWay/laravel-mix/issues/863#issuecomment-366662394 Which provides a fix for the problem mentioned in the linked issue
@turtlegood
I have updated my code in above comment as well.
publicPath: '/',
was missing there.
@spaceemotion @ankurk91 Thanks!!!
Is it just me or after implementing this - the npm run watch
stopped to work properly(doesn't seem to reflect the code changes)... Now you have to just run npm run dev
for full change...
Edited :
Another disadvantage that I've noticed after implementing this is that my global components are no longer working - need to import them in the vue file....
This doesn't seem to be Laravel Mix issue. Same happens with fresh Vue cli generated codebase. Interesting is that it happens only with first async component. Compiles, runs and works perfectly tho.
Even after setting up syntax-dynamic-import
this still gives me an error of:
Unexpected token import
I fail to see what is still going wrong especially when I already have a stage-2
preset set up, so I know that my .babelrc
file is working perfectly fine.
Figured it out
Transpiling and parsing actually went fine it was the eslint configuration which was not set up for experimental features.
To support features like dynamic imports this babel-eslint
parser needs to be set up:
https://github.com/babel/babel-eslint
Simply a matter of installing it:
babel-eslint@8 --save-dev
And updating your .eslintrc
file:
"parser": "babel-eslint"
Hopefully this might help someone.
Hi guys, thanks for the nice feature of code splitting. Modules are now splitting. But the issue is with cache busting. The aproach with chunkFilename: 'js/[name].[chunkhash].js',
generates new files each time.
@lazychaser Do you have the latest version of mix? In this commit The md5 chunk hash plugin has been added, so this should no longer be an issue (at least it's not for me).
I wanted files without hash at all, but still with cache busting, like in mix manifest. Dealed with it using following config option: chunkFilename: 'js/[name].js?[chunkhash]'
@lazychaser I had the same issue with newly generated files which just pile up. My solution so far is to delete the folder before compiling again for production, using clean-webpack-plugin.
const CleanWebpackPlugin = require('clean-webpack-plugin')
if (mix.inProduction())
{
webpackConfig.plugins.push(new CleanWebpackPlugin([
'public/css',
'public/js',
]));
}
There are a lot more config options for this plugin, so check out https://github.com/johnagan/clean-webpack-plugin.
@dimitri-koenig we actually got the same plugin with a similar config in our project. Would be nice to see it in mix natively (feature request?)
@spaceemotion
Yeah, something like
if (mix.inProduction())
{
mix.cleanup();
}
would be nice cause you don't need to do this in dev mode, do you? It's the same with mix.version() which you don't need in dev mode, only in production.
Solved the pile-up of files very simple in package.json without any plugins:
"scripts": {
"dev": "yarn 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": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "yarn 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": "yarn run production",
"production": "rm -fv public/assets/chunks/* && rm -fv public/assets/js/* && rm -fv public/assets/css/* && cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js && git add public/assets"
},
Note the git add public/assets
in the prod command, I regularly missed chunked files in git (which breaks your app) because I did not pay enough attention while committing :smile:
@neorganic
Is it just me or after implementing this - the npm run watch stopped to work properly(doesn't seem to reflect the code changes)... Now you have to just run npm run dev for full change...
I also have this problem and found out that the problem arises when I implement this:
mix.webpackConfig({
output: {
publicPath: '/',
chunkFilename: 'js/[name].[chunkhash].js',
},
});
Anyone has an idea how to solve this? My current workaround for that is to delete the generated chunk files in the public folder using this in my webpack.mix.js:
const glob = require('glob')
const del = require('del')
if (process.env.NODE_ENV === 'production') {
del.sync('public/js/async')
} else {
glob('public/*([0-9]).js', {}, (err, files) => {
del.sync(files)
})
}
and this webpack config:
if (process.env.NODE_ENV === 'production') {
mix.webpackConfig(webpack => ({
output: {
chunkFilename: 'js/async/[name].[chunkhash].js',
}
}))
}
so when I use the watcher the chunk files are saved to the public directory and in production when I don't need the watcher the files are saved in my preferred directory.
Just here to say that @stephan-v solution worked for me - I do not know what happened after I upgraded to Mojave - had to re-install xcode, git, node - a real mess. On npm run watch my code compiled fine however, going to my dynamic component page gave me:
[Vue warn]: Failed to resolve async component: function () {
return __webpack_require__(".[LOCATION] lazy recursive ^\\.\\/field\\-.*$")("./field-" + _name);
}
Reason: Error: Loading chunk 0 failed.
And the page did not load. After following the steps by @stephan-v it now compiles and upon browsing to that page it finds 0.js and loads
Most helpful comment
@ruchern
It is specific to webpack (tooling), after all it is babel who will transform the code and will pass to uglify-js. So it is upto end user to configure and enable modern feature.
@pix2D
Here is the complete tutorial -
鈿狅笍 Updated for Laravel Mix v3+
.babelrc
in your project root, and specify the pluginPS
If you want to load components via computed property, read this