At Foster Made, we use Laravel Mix to compile front-end assets on most of our projects because it’s easy to configure and flexible. It puts a nice wrapper around Webpack and Babel so we don't have to think about them. But if you want to use some newer Javascript features and maintain browser support, it helps to look a little deeper.
Oftentimes our clients don’t need to support older browsers like IE11 and in those cases the mix.js()
method (see docs) does everything we need to safely use ES2015 and ES2017. It uses Webpack to add support for modules and modern browsers already support the newer JS features natively (see the ECMAScript Browser Compatibility Table). But what if we want to use an ES2018 feature and support Edge? Or what if we need to support IE11? Then we need to setup Mix to tell Babel to add polyfills. Babel doesn't add polyfills by default.
Babel compiles all new JS syntax into vanilla JS. But out-of-the-box Babel does not add support for other features such as built-ins like Promise
and methods like Array.prototype.includes
. Babel needs to be configured to add polyfills to fill in those gaps. Whether or not you need to enable them depends on the browsers you need to support and the features you want to use. If you need to support IE11, just do it. If you want to use Promise.prototype.finally
and support Edge 17, do it.
One thing to note is that Babel only adds polyfills for ECMAScript features. There are other features, like fetch, that are defined by a web specification. You will need to add other polyfills to cover them. There are also some features like Service Workers that can’t be polyfilled into older browsers.
Okay, so how do we enable polyfills with Mix?
NOTE: this setup is for Mix version 4.x which uses Babel 7.
Laravel Mix has a few methods for processing Javascript: mix.scripts()
, mix.babel()
and mix.js()
.
For our purposes mix.js()
is the one you want. It uses Webpack and Babel for module and ES2015+ syntax support. See the Mix docs for more info.
The mix.js()
method uses the Babel preset: @babel/preset-env
. A preset is a set of plugins. Each plugin tells Babel what transforms to perform on the code. Without them, Babel doesn't do anything, it just parses code and returns it: const babel = code => code;
.
With this preset, ES2015+ syntax is compiled to vanilla JS. Now let’s add the polyfills.
Add the package:
npm install @babel/polyfill --save
Whether or not you need to import it into your entry JS file depends on which value you use for the useBuiltIns
option (see below).
Add a .babelrc
file in the root of your project:
{
"presets": [
[
"@babel/preset-env",
{
"debug": true,
"modules": false,
"forceAllTransforms": true,
"useBuiltIns": "usage",
"targets": "last 1 version, > 1%"
}
]
]
}
Mix will use this in its Babel config. The modules
and forceAllTransforms
properties are from the Mix default Babel configuration. The useBuiltIns
option determines if polyfills are added and how.
The @babel/preset-env
is a smart preset that performs transforms based on your target environments. The recommended way to set which environments are supported is by using a .browserslistrc
file, but Mix does not support the use of browserslist
. Instead, use the targets
option. The value of the option needs to be a browserslist-compatible query. If you want to see which browsers are included, run this command in your terminal:
npx browserslist "last 1 version, > 1%"
This query includes a bunch of browsers, including “ie 11”.
The useBuiltIns
option can be set to either "usage" | "entry" | false
.
With the entry
entry value, you will need to import the polyfill at the top of your entry JS file:
import "@babel/polyfill";
If you use usage
, you don't have to import it.
If the option is set to entry
, all the polyfills will be included based on the target environments, no matter which features are used in your code.
The usage
value is experimental and it may or may not work depending on your project. The advantage is that it only includes polyfills based on which feature are used in your code.
I found the Babel documentation in this area very confusing. This repo is very helpful for explaining the useBuiltIns
option and its ramifications.
The debug
option will give you a list of which plugins and polyfills were added and why, for example:
es7.array.includes { "android":"67", "ie":"11" }
It also lists which transforms Babel is performing and other helpful information.
There are a lot of exciting new JS features that you can use today. Browsers are always adding support for new features, so consult the Browser Compatibility Table often. And of course, there is no substitute for testing your project in all the browsers you need to support. Now go write some async
functions!
Posted in #Technologies under *Javascript, *Laravel