Combining Multiple Files with Laravel Mix

Ben Carey picture Ben Carey · Mar 6, 2017 · Viewed 20.6k times · Source

I am currently in the process of diving into Laravel Mix and so far, whilst I fully understand what Laravel Mix is and how it works, I am trying to understand a little more about the common practices and 'How-Tos'...

For instance, consider this file structure:

/resources/assets/js/app.js (all global functions)
/resources/assets/js/index/index.js (functions specific to index.js)
/resources/assets/js/about/about.js (functions specific to about.js)
/resources/assets/js/contact/contact.js (functions specific to contact.js)

Now, ideally, I would like the following combined and minified in the following way:

/public/js/index/index-some_hash.js (including app.js)
/public/js/about/about-some_hash.js (including app.js)
/public/js/contact/contact-some_hash.js (including app.js)

As far as I understand, the way to achieve this is something like the following:

// Index
mix.js([
    'resources/assets/js/app.js',
    'resources/assets/js/index/index.js'
], 'public/js/index/index.js').version();

// About
mix.js([
    'resources/assets/js/app.js',
    'resources/assets/js/about/about.js'
], 'public/js/about/about.js').version();

// Contact
mix.js([
    'resources/assets/js/app.js',
    'resources/assets/js/contact/contact.js'
], 'public/js/contact/contact.js').version();

My Question

Quite simply, I would like to know if the above is the correct method for doing what I am trying to do? Are there better ways, or more common ways of achieving this?

If the above structure is wrong and there are other ways my files should be combined then please share your knowledge. However, unless there is a very good reason, I would like to avoid the following:

  • Serving two separate files for each page i.e. app.min.js and index.min.js. This requires two lookups per page, ideally it should be as few as possible
  • Serving the same file to ALL pages on my site. Serving code to a page that is not going to use it is a waste of resource, regardless of caching...

One Idea...

I noticed a line of code in one of the JS files; require('./bootstrap');. Call me old fashioned but I have never seen this in JavaScript (I assume it is from node.js). That said, obviously it is just loading the bootstrap.js file as a dependency into the specific file. So, with this in mind, would the following solution be better:

about.js

require('./app'); // Include global functions

// Do some magic here specifically for the 'about' page...

webpack.mix.js:

mix.js(['resources/assets/js/*/*.js'); // For all pages

If this is a better solution then how do I include files using SASS as well? Are there ways the above can be improved at all?

Answer

Rwd picture Rwd · Jul 16, 2017

I'd say there are 3 main things you have to consider here (they are not all equal):

  • Size - The amount of space your resources are going to take up and the file sizes that are being downloaded.
  • Number of requests - How many files are being loaded in to the page.
  • Browser Cache - Files will be pulled from the cache rather than the server.

In your particular situation it would depend on how big your app.js file is on it's own, how big your page specific files would be without the code the code from app.js, how many page specific files you have and if you're using some of the same resources in your different file e.g. requiring the same package(s) in the different files.


One File

If your page specific files are fairly small then I would just included them in your main app.js file:

require('./index/index')
require('./about/about')
//etc

webpack.mix.js:

.js('resources/assets/js/app.js', 'public/js')

This would mean that you're only storing one compiled file on your server, only one request is being made for your javascript and it should be cached for every subsequent page load across the site.


A main file and a page specific file

If you have a large number of page specific files or your page specific files aren't that small then I would suggest compiling your app.js independently of your page specific files. Don't forget to compare the results of this approach with the One file approach as the One File approach might still be more efficient.

webpack.min.js

.js('resources/assets/js/app.js', 'public/js')
.js('resources/assets/js/index/index.js', 'public/js/index')
.js('resources/assets/js/about/about.js', 'public/js/about')

This will mean that the main bulk of your code (app.js) will still be getting cached and that only one request is being made for page specific code (which should then be cache for each subsequent load of that page).

Please note that if you are requiring packages in both your app.js file and page specific file they will not be shared across the 2 so it will increase the overall size for those requests. Packages that can be added to the window object (e.g. jQuery) should only be included in your app.js.

One way to get around this would be to use Vendor Extraction (Laravel Mix docs):

.extract(['jquery'])

This will produce two extra files that you'll need to include in your html:

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

One main file and a few page specific file

If you have one or two page specific files that are fairly big but the rest aren't you could compile the majority of your page specific files in to app.js and have the larger ones compile to their own files.


All page specific files

I would only go down this route if all your page specific file are quite large, your app.js file wasn't that big and the code/logic in your page specific files couldn't be refactored so that it could be shared across different files.

This way would similar to the example in your question, however, instead of having:

webpack.min.js

mix.js([
    'resources/assets/js/app.js',
    'resources/assets/js/index/index.js'
], 'public/js/index/index.js').version();

you would have:

resources/assets/js/index/index.js

require('../app.js')

//rest of file

webpack.min.js

mix.js('resources/assets/js/index/index.js', 'public/js/index/index.js').version();

I would never reach for this approach straightaway though and there are very few situations where it would be the most efficient.


Summary

I would always reach for the One File approach and then look at optimising later (unless something really suck out during development) as premature optimisation can lead to code smell and harder maintainability.

This might be a good article for you as well https://medium.com/@asyncmax/the-right-way-to-bundle-your-assets-for-faster-sites-over-http-2-437c37efe3ff