After months of community work, I'm am thrilled to be able to talk about the release of Webpack Encore 0.21.0. This represents a huge step forward to stay with the latest versions of industry-standard tools and a bunch of great features along the way.
So, what changes and improvements happened? Let's find out!
WebpackEncoreBundle & encore_entry_script_tags()
Besides the Webpack 4 upgrade itself (described below) the most noticeable change
is that a new bundle has been created: WebpackEncoreBundle. If you've previously
installed the symfony/webpack-encore-pack
, you can remove it and install the
bundle instead:
1 2
composer require symfony/webpack-encore-bundle
composer remove symfony/webpack-encore-pack
Notice there is no --dev
flag for symfony/webpack-encore-bundle
. That's
on purpose: it contains a new feature that makes it much easier to include the
script
and link
tags for an entry:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
{# templates/.../checkout.html.twig #}
{% block javascripts %}
{{ parent() }}
- <script src="{{ asset('build/checkout.js') }}"></script>
+ {{ encore_entry_script_tags('checkout') }}
{% endblock %}
{% block stylesheets %}
{{ parent() }}
- <link rel="stylesheet" href="{{ asset('build/checkout.css') }}">
+ {{ encore_entry_link_tags('checkout') }}
{% endblock %}
Why have we added these functions? Because using these functions gives you versioning and CDN support for free, as well as enabling the new "Split Chunks" feature described below.
Webpack 4, Babel 7 and Major Upgrades
The main focus of this release was to upgrade Encore to use the latest versions
of Webpack & Babel. Webpack 4 contains a long list of improvements - like faster
build times and a new way to "split" your built files into more optimized pieces
(more on that later). Babel 7 is also a huge release - it contains optimizations and
support (via @babel/preset-env
) for browserslist
(more on that later).
Several other libraries were also upgraded - see the CHANGELOG for full details. If you're using Encore's features exclusively, you shouldn't have any problem with these major version upgrades. If you've added custom features - be sure to check the changes for each library.
splitChunks() & encore_entry_script_tags()
One of the biggest innovations in Webpack 4 is SplitChunksPlugin. This solves
the problem of duplicated code between multiple compiled entry files. Before Webpack 4,
this was solved in Encore via the createSharedEntry()
method. This method
will not go away soon, but Webpack now has a better option.
Encore 0.21.0 adds a new splitEntryChunks() method. When this feature is enabled,
Webpack's SplitChunksPlugin will use an optimization algorithm to automatically "split"
the final JavaScript and CSS files into multiple pieces. For example, an entry called
"app" will normally output app.js
and app.css
files. But with
splitEntryChunks()
, it may output app.js
and vendor~app.js
(which would
contain code shared by other entry files) as well as app.css
and vendor~app.css
.
But this now means you may need multiple script
and link
tags in your
template! To solve this problem, Encore outputs a new entrypoints.json
file
that contains all the JavaScript and CSS files needed for each "entry". The new
WebpackEncoreBundle Twig functions know how to read from this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
{# templates/.../checkout.html.twig #}
{% block javascripts %}
{{ parent() }}
{#
May now render multiple script tags:
<script src="/build/runtime.js"></script>
<script src="/build/checkout.js"></script>
<script src="/build/vendor~checkout.js"></script>
#}
{{ encore_entry_script_tags('checkout') }}
{% endblock %}
{% block stylesheets %}
{{ parent() }}
{#
May now render multiple link tags:
<script src="/build/checkout.css"></script>
<script src="/build/vendor~checkout.css"></script>
#}
{{ encore_entry_link_tags('checkout') }}
{% endblock %}
That's it! Webpack will intelligently "split" your entry files and the correct
script
and link
tags will always be rendered. The manifest.json
file
is still output by Encore, but is only used if you need to refer to a static asset
(like an image) from your Twig template via the asset()
function.
New runtime.js File
This version of Encore also introduces an optional new output file called runtime.js
.
You should explicitly enable or disable it by calling either enableSingleRuntimeChunk()
or disableSingleRuntimeChunk()
. If you enable it, a new runtime.js
file will
be output and needs to be included in your page (but encore_entry_script_tags()
will handle this automatically)
What's the point of runtime.js
? Suppose you include multiple entry JavaScript
files on the same page - like an app.js
in your layout and also checkout.js
when you're on a checkout page. Then:
- With
enableSingleRuntimeChunk()
: if the same module (e.g.jquery
) is required by both entry files, they will require the same object. - With
disableSingleRuntimeChunk()
: if the same module (e.g.jquery
) is required by both entry files, each will receive a different object.
Using enableSingleRuntimeChunk()
will give you the same behavior as Webpack 3,
but you can choose whichever setting works better for your app.
New copyFiles() Feature
One of the most-asked-for features in Encore is the ability to copy static files
into your build directory. Until now, we recommended using the copy-webpack-plugin.
This plugin is great, but if you used version hashes in your filenames, the manifest.json
file would not contain the correct key for this copied file (the key would be the versioned/hashed
filename). In practice, this made it impossible to version your copied files with filename hashes.
To fix this, Encore now has a copyFiles() method! It's simple: use it to copy
files from one directory (e.g. assets/images
) into your "build" directory.
If versioning is enabled, all files are given version hash names and are included
in manifest.json
so you can fetch the correct filename via the asset()
function
in Twig.
Async Code Splitting out-of-the Box
Did you know that Webpack allows you to require modules asynchronously, only when you need them? The feature is called Code Splitting. It's not new, but in the latest version of Encore, you can use it without any extra configuration:
1 2 3 4 5 6 7
$('.js-open-video').on('click', function() {
// async load the VideoPlayer module
import('./components/VideoPlayer').then(({ default: VideoPlayer }) => {
// once it loads, use it!
const player = new VideoPlayer('some-element');
});
});
browserslist Support
There are several tools in Encore that need to know what versions of browsers your site needs to support. For example, both Babel and autoprefixer (from PostCSS) will compile your code differently if you need to support older browsers versus only newer browsers.
Until now, there was no one place to configure this. But, thanks to upgrades
to @babel/preset-env
, you can now set your browsers via a browserslist
key in package.json
.
For more details, see the browserslist documentation for Encore.
Support for Uglifying ES6 Code
Before this release, Uglify (the library that minifies your JavaScript for production), was unable to process ES6 code. This meant that you were forced to transpile all your JavaScript to ES5 code, even if your site didn't need to support older browsers.
Because of this, Uglify was replaced with terser - a fork of Uglify that processes
ES6 code. You shouldn't notice any difference in your app unless you use the
configureUglifyJsPlugin()
method (use configureTerserPlugin()
now).
Ready to Upgrade?
Because this is a big release, upgrading requires a few steps:
- Install the bundle via
composer require symfony/webpack-encore-bundle
- Remove the webpack-encore-pack via
composer remove symfony/webpack-encore-pack
- Update your version constraint in
package.json
for@symfony/webpack-encore
to^0.21.0
- Run
yarn upgrade
- Replace your manual
script
andlink
tags with the newencore_entry_script_tags()
andencore_entry_link_tags()
functions.
And, over time, replace createdSharedEntry()
with splitEntryChunks()
.
If you have any issues, please report them on the symfony/webpack-encore repository.
Enjoy!
Amazing release! I'll try to upgrade my apps as soon as possible. Thanks Ryan and thanks to the Webpack Encore community too!
Thank you for your work (and the community) on those two repositories ! It will really help decrease the boilerplate on our current and next projects for assets management :D
This is awesome! Thanks a lot :-)
I noticed a little typo: manifest.json is sometime written manfiest.json
Great work! I am already integrating this into my project. I was wondering, how can we now add the 'async' and/or 'defer' attributes to the script element now that the element is generated?
Great work, thank Ryan for that release
Yeeha! Thanks, Ryan! :D
GREAT question! This is a feature that we didn't finish in time... kind of on purpose. Basically, I wanted to get community feedback on requirements in order to handle this in the best way possible. You can track that here https://github.com/symfony/webpack-encore/issues/429 and here https://github.com/symfony/webpack-encore-bundle/issues/10
Until then, you'll need to use the encore_entry_js_files() and encore_entry_css_files() helpers instead - which read the entrypoints.json file and return an array of the script or link tags you need. So, a bit more work until we get the feature implemented correctly, but still possible.
Cheers!
Very nice! thanks !
Thanks! We Weese waiting copyFiles with impatience! Amazing work with Encore 👏👍
Cool, thanks everyone! Question: (How) does that work with HTTP 2? Is there a way to push these assets to the browser?
Thanks! I've been waiting for this!
I'm rubbing my hands, now to practice, thank you very much.
Great question! that's still a TODO. Basically, we deferred adding some of those features so we could implement them correctly. See https://github.com/symfony/webpack-encore/issues/429 and https://github.com/symfony/webpack-encore-bundle/issues/10
But you can also use the encore_entry_js_files() and encore_entry_css_files() helpers if you need to control the script and link tags manually until then :).
Cheers!
Gr8 work!
Good job :)