Hello Webpack Encore

Yo friends! It's Webpack time! Yeeeeeeah! Well, maybe not super "yeeeeeeah!" if you are the person responsible for installing and configuring Webpack... Cause, woh, yea, that can be tough! Unless... you're using Webpack Encore! More about that in a few minutes.

Why all the Webpack Buzz?

But first, I want you to know why you should care about Webpack... like super please-let-me-start-using-webpack-right-now-and-never-stop-using-it kind of care. Sure, technically, Webpack is just a tool to build & compile your JavaScript and CSS files. But, it will revolutionize the way you write JavaScript.

The reason is right on their homepage! In PHP, we organize our code into small files that work together. But then, traditionally, in JavaScript, we just smash all our code into one gigantic file. Or, if we do split them up, it's still a pain because we then need to remember to go add a new script tag to every page that needs it... and those script tags need to be in just the right order. If they're not, kaboom! And even if you do have a build system like Gulp, you still need to manage keeping all of the files listed there and in the right order. How can our code be so nicely organized in PHP, but such a disaster in JavaScript?

Webpack changes this. Suppose we have an index.js file but we want to organize a function into a different, bar.js file. Thanks to Webpack, you can "export" that function as a value from bar.js and then import and use it in index.js. Yes, we can organize our code into small pieces! Webpack's job is to read index.js, parse through all of the import statements it finds, and output one JavaScript file that has everything inside of it. Webpack is a huge over-achiever.

So let's get to it! To import or... Webpack the maximum amount of knowledge from this tutorial, download the course code from this page and code along with me. After you unzip the file, you'll find a start/ directory that has the same code I have here: a Symfony 4 app. Open up the README.md file for all the setup details.

The last step will be to open a terminal, move into the project and start a web server. I'm going to use the Symfony local web server, which you can get from https://symfony.com/download. Run it with:

symfony serve

Then, swing back over to your browser and open up https://localhost:8000 to see... The Space Bar! An app we've been working on throughout our Symfony series. And, we did write some JavaScript and CSS in that series... but we kept it super traditional: the JavaScript is pretty boring, and there are multiple files but each has its own script tag in my templates.

This is not the way I really code. So, let's do this correctly.

Installing WebpackEncoreBundle + Recipe

So even though both Webpack and Encore are Node libraries, if you're using Symfony, you'll install Encore via composer... well... sort of. Open a new terminal tab and run:

composer require encore

This downloads a small bundle called WebpackEncoreBundle. Actually, Encore itself can be used with any framework or any language! But, it works super well with Symfony, and this thin bundle is part of the reason.

This bundle also has a Flex recipe - oooooOOOOooo - which gives us some files to get started! If you want to use Webpack from outside of a Symfony app, you would just need these files in your app.

Back in the editor, check out package.json:

15 lines package.json
{
"devDependencies": {
"@symfony/webpack-encore": "^0.27.0",
"core-js": "^3.0.0",
"webpack-notifier": "^1.6.0"
},
"license": "UNLICENSED",
"private": true,
"scripts": {
"dev-server": "encore dev-server",
"dev": "encore dev",
"watch": "encore dev --watch",
"build": "encore production --progress"
}
}

This is the composer.json file of the Node world. It requires Encore itself plus two optional packages that we'll use:

15 lines package.json
{
"devDependencies": {
"@symfony/webpack-encore": "^0.27.0",
"core-js": "^3.0.0",
"webpack-notifier": "^1.6.0"
},
... lines 7 - 14
}

Installing Encore via Yarn

To actually download these, go back to your terminal and run:

yarn

Or... yarn install if you're less lazy than me - it's the same thing. Node has two package managers - "Yarn" and "npm" - I know, kinda weird - but you can install and use whichever you want. Anyways, this is downloading our 3 libraries and their dependencies into Node's version of the vendor/ directory: node_modules/.

And... done! Congrats! You now have a gigantic node_modules/ directory... because JavaScript has tons of dependencies. Oh, the recipe also updated our .gitignore file to ignore node_modules/:

31 lines .gitignore
... lines 1 - 22
###> symfony/webpack-encore-bundle ###
/node_modules/
/public/build/
... lines 26 - 27
###
... lines 29 - 31

Just like with Composer, there is no reason to commit this stuff. This also ignores public/build/, which is where Webpack will put our final, built files.

Hello webpack.config.js

In fact, I'll show you why. At the root of your app, the recipe added the most important file of all webpack.config.js:

var Encore = require('@symfony/webpack-encore');
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
// only needed for CDN's or sub-directory deploy
//.setManifestKeyPrefix('build/')
/*
* ENTRY CONFIG
*
* Add 1 entry for each "page" of your app
* (including one that's included on every page - e.g. "app")
*
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if you JavaScript imports CSS.
*/
.addEntry('app', './assets/js/app.js')
//.addEntry('page1', './assets/js/page1.js')
//.addEntry('page2', './assets/js/page2.js')
// When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
.splitEntryChunks()
// will require an extra script tag for runtime.js
// but, you probably want this, unless you're building a single-page app
.enableSingleRuntimeChunk()
/*
* FEATURE CONFIG
*
* Enable & configure other features below. For a full
* list of features, see:
* https://symfony.com/doc/current/frontend.html#adding-more-features
*/
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
// enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
// enables @babel/preset-env polyfills
.configureBabel(() => {}, {
useBuiltIns: 'usage',
corejs: 3
})
// enables Sass/SCSS support
//.enableSassLoader()
// uncomment if you use TypeScript
//.enableTypeScriptLoader()
// uncomment to get integrity="..." attributes on your script & link tags
// requires WebpackEncoreBundle 1.4 or higher
//.enableIntegrityHashes()
// uncomment if you're having problems with a jQuery plugin
//.autoProvidejQuery()
// uncomment if you use API Platform Admin (composer req api-admin)
//.enableReactPreset()
//.addEntry('admin', './assets/js/admin.js')
;
module.exports = Encore.getWebpackConfig();

This is the configuration file that Encore reads. Actually, if you use Webpack by itself, you would have this exact same file! Encore is basically a configuration generator: you tell it how you want Webpack to behave and then, all the way at the bottom, say:

Please give me the standard Webpack config that will give me that behavior.

Encore makes things easy, but it's still true Webpack under-the-hood.

Most of the stuff in this file is for configuring some optional features that we'll talk about along the way - so ignore it all for now. The three super important things that we need to talk about are output path, public path and this addEntry() thing:

var Encore = require('@symfony/webpack-encore');
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
... lines 8 - 10
/*
* ENTRY CONFIG
*
* Add 1 entry for each "page" of your app
* (including one that's included on every page - e.g. "app")
*
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if you JavaScript imports CSS.
*/
.addEntry('app', './assets/js/app.js')
//.addEntry('page1', './assets/js/page1.js')
//.addEntry('page2', './assets/js/page2.js')
... lines 23 - 65
;
... lines 67 - 68

Let's do that next, build our first Webpack'ed files and include them on the page.

Leave a comment!

  • 2019-05-13 Diego Aguiar

    Alright! Anyways, it should work, probably there is something obvious missing (I couldn't find it by looking at your config), I would double check uploads_base_url parameter and the filesystem service

  • 2019-05-11 José Luís Riego Montero

    Really, really no changes.
    I think, this is not important for the course purpose.
    I'll take the course about uploads. 👨‍🎓️
    Thank You!

    liip_imagine:
    # # valid drivers options include "gd" or "gmagick" or "imagick"
    # driver: "gd"
    #

    loaders:
    flysystem_loader:
    flysystem:
    filesystem_service: oneup_flysystem.uploads_filesystem_filesystem

    # default loader to use for all filter sets
    data_loader: flysystem_loader

    resolvers:
    flysystem_resolver:
    flysystem:
    filesystem_service: oneup_flysystem.cached_uploads_filesystem_filesystem
    cache_prefix: media/cache
    root_url: '%uploads_base_url%'

    # default cache resolver for saving thumbnails
    cache: flysystem_resolver

    filter_sets:
    #
    # # an example thumbnail transformation definition
    # # https://symfony.com/doc/cur...
    squared_thumbnail_small:
    filters:
    thumbnail:
    size: [200, 200]
    mode: outbound
    allow_upscale: true

    squared_thumbnail_medium:
    filters:
    thumbnail:
    size: [500, 500]
    mode: outbound
    allow_upscale: true

    #
    # # set your image quality defaults
    # jpeg_quality: 85
    # png_compression_level: 8
    #
    # # setup the filter steps to apply for this transformation
    # filters:
    #
    # # auto rotate the image using EXIF metadata
    # auto_rotate: ~
    #
    # # strip the image of all metadata
    # strip: ~
    #
    # # scale and square the image to the given dimensions
    # thumbnail:
    # size: [253, 253]
    # mode: outbound
    # allow_upscale: true
    #
    # # create border by placing image on larger black background
    # background:
    # size: [256, 256]
    # position: center
    # color: '#fff'

  • 2019-05-10 Diego Aguiar

    Ah ok, can you show me your Liip config?

  • 2019-05-09 José Luís Riego Montero

    thanks Diego!
    Yes, the page show images, the ice cream, the meteor shower etc... but not the article image.
    I suspect there are a problem with the liip bundle configuration.

  • 2019-05-09 Diego Aguiar

    Hey José Luís Riego Montero

    The images are not being displayed on your browser or the actual files are not there? I just double checked and the images are inside "start/public/images/" directory

    Cheers!

  • 2019-05-09 José Luís Riego Montero

    ummm well, god morning...
    I try to rum(Imean run, of course) your(our) course code on docker and the images are missing.
    Not all the images, only the bd images.
    Whats can I do?

  • 2019-05-07 Krzysztof Krakowiak

    Web Components (with VueJS) give me finally proper separation between Templates and JavaScript, I can easy create reusable components without need to dump any JavaScript into script tag.

    They are also a great solution for progressive enhancement strategy, for example what in my symfony forms - if I need to add extra functionality from JavaScript world them I am warping forms in my custom made beautiful-form-component:


    <beautiful-form-component id="my-form">
    <form>
    //here I have all html for form
    </form>
    </beautiful-form-component>

    And then this content between my custom tags is processed as a slot in my VueJS Component, I can easily access it and do with it wherever I want, in this case in example I am adding date-picker to my inputs. Id in above example will be passed as a props to my component. So data can be passed to such component through props or slots.

    here I have repo with really simple example (Encore, vue-custom-element) https://github.com/aniolekx...

    Unfortunately somewhere between encore and babel, there is an issue, which is breaking slots under chrome (I already reported it: https://github.com/karol-f/..., but it works fine under Firefox and IE (IE needs poly-fill, not included in demo)

    I am trying to resolve it too, maybe I will find the root cause of it.

    In my Vue components I am using lots of different libraries (for DataTable, Date Pickers, etc) and all this JS code is wrapped in to Web Components. It is really easier to reuse and maintain such code.

    It is possible to create web components without any extra library like VueJS or React, but I am using Web Components way to mount my VueJS Components.

    Disclaimer: I am absolutely not a pro in a front end world and I have archived all that in past 2 months, but I believe that this is the way :D

  • 2019-05-07 weaverryan

    Hey Krzysztof Krakowiak!

    Hmm, interesting. I'll admit that I don't really have much experience or an opinion yet. What are you using them for? I mean, what problem are they solving for you - or why were you attracted to use them? I'd love to know!

    Cheers!

  • 2019-05-03 Krzysztof Krakowiak

    Ryan, thanks for another great tutorial, but could you add something about Web Components to it? The truth is that I am already using web components with VueJs (through vue-custom-element), but I would like to see some input from your regarding that. Again - Thanks for your hard work!

  • 2019-04-24 Vladimir Sadicov

    hey Vinz Stoned Orgies

    Sometimes it happens, probably you don't have symfony/flex package, or it's outdated in that case you should use full package name symfony/webpack-encore-bundle.

    But stop do you use our course code?

    Cheers!

  • 2019-04-24 Vinz Stoned Orgies

    Hi, my Composer says it can't find any encore package... using "composer require encore".

  • 2019-04-19 weaverryan

    Hey Marcel dos Santos!

    I'm definitely covering the highlights in this video. The biggest one is that we've gone from Webpack 3 to Webpack 4, which is quite a large change. It eliminated the "commons chunk" stuff, and replaced with with the "
    split chunks" (which has a similar name, but is totally different). If you *really* want to see the changes, the last tutorial covered Webpack Encore 0.19. The new tutorial uses 0.27 - you can see the differences in the CHANGELOG: https://github.com/symfony/... - the biggest changes were in 0.21.0.

    Cheers!

  • 2019-04-18 weaverryan

    Hey nibsirahsieu!

    Not *really*. Webpack can be installed globally, but it's not recommended (https://webpack.js.org/guid.... For that reason, if I remember correctly, we actually *don't* support this with Encore. It causes problems with dependency versions between your global node_modules and your local one. So... avoid it ;).

    Cheers!

  • 2019-04-17 nibsirahsieu

    Hi Ryan, is it possible to install webpack encore globally? As you know, nodes dependencies is quite big :)

  • 2019-04-16 Marcel dos Santos

    Nice. Is there a changelog or something like that where I can see the changes, improvements and new features about Symfony Encore since the last course (Webpack Encore: A Party for your Assets)?

  • 2019-04-16 weaverryan

    Hey Marcel dos Santos!

    Great question! You should... thought you will probably speed through a lot of parts ;). There's a quite a lot of new stuff that I want to make sure people are aware of - like the new "split chunks" functionality, browserslist to control browsers you support, super easy async imports(), copyFiles() and Babel polyfill support. If you want, you can skip chapters that you think you know - but definitely check out this other stuff if it's new to you :).

    Cheers!

  • 2019-04-16 Marcel dos Santos

    Hi Ryan, thanks for another great course!

    I already did the course 'Webpack Encore: A Party for your Assets' some weeks ago.
    Should I do this course too? There will be any difference between both courses?

    Bye!