Reducing Vue application file size with Laravel Mix

Patrick Brouwers
Spartner
Published in
5 min readFeb 12, 2019
Photo by Drew Beamer on Unsplash

One of the major caveats of moving more and more logic over to the client-side is that the file size of your JavaScript file gets increasingly larger, especially if bundled into one file (app.js in default Laravel installation).

Slower load time

I’ve seen file sizes going into megabytes, even when minified and gzipped. This has negative effects on initial page load; users will have to wait until the entire file is loaded before the site is useable or — in worst cases — visible.

Especially on poor (mobile) connections this can be a problem. Not every user might wait until the full page is loaded and click away before anything is visible. This way you may be losing potential leads or sales.

Search engines

Google already took the time to load your website (on desktop) into account in their ranking algorithm, but since 2018 they also use the mobile load time. Having a slow loading site can thus have a big negative effect on your search engine ranking. Google research even indicates that the average time for a mobile page load is 15 seconds and pages loading between 1 and 3 seconds already have a bounce rate of 32%.

Extract vendor

Laravel Mix already has a great way of extracting common vendors into one JavaScript file. This also has the benefit of not needing to cache-bust the vendor.js file, if no changes have been made to the dependencies that have been extracted.

mix.extract(['vue', 'jquery']);

This will create a separate vendor.js file that will contain the Vue and jQuery dependencies. The files can now be included on the page as follows:

<script src="/js/manifest.js"></script>
<script src="/js/vendor.js"></script>
<script src="/js/app.js"></script>

Loading only what you need

One of the major issues of having one JavaScript bundle is that it includes a lot of JavaScript that you don’t even need on some pages.

One solution is to create several JavaScript entry points and compile them one by one with Laravel Mix. But this becomes a bit tedious and results into some potential code duplication. It also requires us to include the correct file on each page.

Deferred loading

The ultimate goal is to be able to use our JavaScript app in the exact same way as we did when having a single bundle, but with the difference that it will automatically load parts of the code when they are needed.

Async components

You might be registering your components as follows:

Vue.component('AsyncComponent', YourComponent)

This results in always loading this component in your Vue application, even when you do not need it.

Webpack has an awesome feature to create chunks of code. The key to this is to use async components. These components get loaded completely asynchronously whenever the component is present on the page you just loaded.

Preparation

The first step is to install @babel/plugin-syntax-dynamic-import and @babel/preset-env via npm (or yarn) if you are using ES6 imports. If you are using require(), this step is not needed.

npm install @babel/plugin-syntax-dynamic-import @babel/preset-env

Create (or edit) a .babelrc file and add the following:

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

The next step is to use a single app.js file and define it in your webpack.mix.js file.

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

Loading async component

In your resources/js/app.js you can now dynamically include your components, by using the () => import() Promise syntax. You can either register the components globally or locally.

To register the async components globally you can do the following with ES6 imports.

Vue.component('AsyncComponent', () => import('./components/AsyncComponent') )

Or the following with require syntax:

Vue.component('AsyncComponent', function (resolve) {
require(['./components/AsyncComponent'], resolve)
});

If you want to register the components locally, you can add them to the components array of the Vue instance.

import Vue from 'vue';new Vue({
el: '#app',
components: {
AsyncComponent: () => import('./components/AsyncComponent' /* webpackChunkName: "js/async-component" */)
},
});

By using /* webpackChunkName: “js/async-component” */ you can choose the name of the new JavaScript file. When no chunk name is defined it will name it 0.js, 1.js, …

Auto registering async components

If you like how Laravel auto-registers Vue components, but want to have them as async components, you can use the following snippet:

const files = require.context('./', true, /\.vue$/i, 'lazy').keys();

files.forEach(file => {
Vue.component(file.split('/').pop().split('.')[0], () => import(`${file}`));
});

If you want to have each chunk have the name of the component, you can add /*webpackChunkName: “[request]” */ to the import.

const files = require.context('./', true, /\.vue$/i, 'lazy').keys();

files.forEach(file => {
Vue.component(file.split('/').pop().split('.')[0], () => import(`${file}` /*webpackChunkName: “[request]” */));
});

Usage

Now whenever you use a component on a page, it will automatically load the chunk that is linked to that component. No extra code is needed to make this work. You won’t have to include the chunk file on your page.

<body>
<div id="app">
<async-component></async-component>
</div>
</body>

If your components use dependencies (vendors) that are not shared by other components, it will automatically create a separate chunk for those vendors.

This vendor file will be called: public/vendors~js/async-component.js.

Warning: you can’t currently combine mix.extract() and async components. According to Laravel Mix this will be fixed when Webpack 5 gets released. (Read more here in their release notes)

Read more about async components in the Vue documentation: https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components

Versioning chunks

If you want to version your chunks, you can customize the chunk name in your mix file:

mix.webpackConfig({
output: {
chunkFilename: 'js/[name].[contenthash].js',
}
});

This will add a hash at the end of the file. js/async-component.d12c57b94efe0e4fdebc.js

Code splitting approaches

One of the most obvious situations when code splitting might be useful, is when dealing with a site with multiple pages. Each of those pages can be a chunk that gets loaded when the user opens that specific page.

It can also be interesting for components that are conditionally shown on a page. Those JavaScript files will only be loaded if the page is rendered including those components and won’t when the component is not required.

--

--

Patrick Brouwers
Spartner

Loves building awesome things in Laravel, VueJS and TailwindCss. Working @ Spartner. Maintainer of Laravel Excel.