sauladam's avatar

Laravel Mix 6.0 + Vue3 - Huge bundle size / how to use only vue runtime?

I'm using a Mix 6.0.0-beta.14 with Vue 3 (no Laravel involved here). I've got a very basic app.js file:

// app.js

import { createApp } from "vue"

createApp({
  render: (h) => {
    return h('div', {})
  }
}).mount('#app')

which I run through mix: mix.js('app.js', 'dist/') with npm run prod

This gives me a bundle size of 47.7 KiB (19 KiB gzipped). Fair enough.

Now I want to use this awesome single file component :

<!-- AwesomeComponent.vue -->

<template>
  <div>Hello there</div>
</template>

<script>
  export default {}
</script>

So i pull it in in my app.js:

// app.js

import { createApp } from "vue"
import AwesomeComponent from './AwesomeComponent.vue'

// ...

Since now there is some vue specific stuff involved, I need to tell mix to handle that:

mix.js('app.js', 'dist/').vue()

When I now npm run prod, I get a bundle size of 348 KiB (102 KiB gzipped)!

That's quite a lot. My understanding is that mix handles all the single file components and transpiles templates into render functions, no? So in production we would only need the Vue runtime, which is supposed to be around 33 KiB gzipped. But it looks like a full blown Vue bundle is pulled in.

What am I missing? How do I reduce the bundle size?

0 likes
5 replies
rodrigo.pedra's avatar
Level 56

Add the runtimeOnly option to mix:

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

mix.js('resources/js/app.js', 'public/js')
    .vue({runtimeOnly: true});

I tested with similar files you posted. Compiled bundled went from 348Kb to 47.9Kb.

Just for reference, here is my ./resources/js/app.js file:

import {createApp} from 'vue';
import AwesomeComponent from './AwesomeComponent.vue';

createApp({
    render: (h) => h(AwesomeComponent, {}),
}).mount('#app');

And here is my ./resources/js/AwesomeComponent.js file:

<template>
    <div>Hello there</div>
</template>

<script>
export default {
    name: 'AwesomeComponent'
};
</script>

Hope it helps

2 likes
sauladam's avatar

Duuuude.... How did I miss that?!

This option does not seem to be mentioned anywhere in the docs though, but maybe I'm just too dumb to use google.

Regardless - thank you so much, this did the trick!

1 like
rodrigo.pedra's avatar

You're welcome!

Actually it is not mentioned in docs, I guess it is because Laravel Mix 6 is still in beta and docs are about version 5.

But I looked at the source for the Laravel Mix's Vue component:

https://github.com/JeffreyWay/laravel-mix/blob/a557b55c796ceb7349fb3c8a875bc452602b7e90/src/components/Vue.js#L105-L107

And there it was:

// lines 105-107
return this.options.runtimeOnly
    ? 'vue/dist/vue.runtime.esm-bundler.js'
    : 'vue/dist/vue.esm-bundler.js';

I wish that would be the default, not the opposite. But it is much better than on previous versions where one had to tweak webpack config directly with mix.webpackConfig({...}) calls.

Have a nice day =)

1 like
sauladam's avatar

Agreed that it would probably be a sensible default. But leaving it off seems to be the safest option in terms of reducing friction and confusion since it works either way.

I wonder if there is an easy way to determine at compile time whether the template compiler is needed in the prod build or not. This would be a really powerful feature.

Anyway, thanks again and have an even nicer day! :) (Friday 13th in 2020, what could possibly go wrong, right?)

1 like
rodrigo.pedra's avatar

Didn't noticed that!

But as this is still 2020, maybe a Friday 13th works the opposite!? One can dream =)

Please or to participate in this conversation.