Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

HMR: See Changes without Reloading

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

If you use Webpack's dev-server, then as soon as it detects a change, our page automatically reloads. Cool... but not cool enough! Find your terminal, hit Control + C to stop Encore, then re-run the dev server with a --hot option.

Tip

If you're using Webpack Encore 1.0 or higher, you do not need to pass the --hot flag: it's already, automatically, enabled.

yarn dev-server --hot

That stands for hot module replacement. Once that finishes... I'll move over and reload the page one time just to be safe.

Let's do the same trick: add two exclamation points, then quick! Go back to the browser! Woh! It's there... but I didn't even see it reload! And no matter how fast you are, you'll see the update but you'll never see a page refresh: it's just not happening anymore! How cool is that? Webpack is able to dynamically update the JavaScript without reloading. It even keeps any Vue data the same.

This hot module replacement thing doesn't work with all JavaScript, but it does work well with Vue and React.

Disabling CSS Extraction

But... there's one tiny problem. Don't worry it's not a big deal. Back in our editor, at the bottom, let's make a CSS change: I'm pretty sure a designer just told me that the hover background should be pink.

This time, back on the browser, hmm: the style did not update. But if we refresh, it is there. HMR isn't working for styles.

This is easy to fix by disabling a feature in Encore. Let me show you. Open up webpack.config.js and go all the way to the bottom so we can use an if statement. Here, say if not Encore.isProduction() - a nice flag to see if we're building our assets for production or not - then Encore.disableCssExtraction().

... lines 1 - 91
if (!Encore.isProduction()) {
Encore.disableCssExtraction();
}
... lines 95 - 97

CSS extraction means that any CSS that Webpack finds should be extracted into an external CSS file. That's why a products.css file is being output. When you disable CSS extraction, it tells Webpack not to do this anymore. Instead, it embeds the CSS into the JavaScript. Then, when the JavaScript loads, it adds the CSS as style tags on the page.

I'll explain that a bit more in a minute... but let's see how our page changes first. Because we updated our Webpack config, at your terminal, stop Encore and restart it:

yarn dev-server --hot

Now, refresh the page. Did you see that? It looks the same, but it was unstyled for just a second. If you view the HTML source, there are no CSS link tags anymore. The CSS is being added by our JavaScript files. And because it takes a moment for those to load, our page looks ugly for an instant.

Two important things about this. First, disabling CSS extraction should only ever be done in dev mode: you always want real CSS files on production. And second, the only reason we're going to all the trouble of disabling CSS extraction at all, is because hot module replacement only works when it's disabled. Hopefully, someday, that won't be the case... and we won't need to do this.

The end result is pretty sweet though. Our hover links are still pink but now let's change them to green. And... yes! You could see it change from pink to green almost instantly. If we remove the extra background entirely... that time it was faster than me!

I'm going to do the rest of the tutorial using the dev-server with hot module replacement because I love it! But it did require some work - like using http:// or getting the ssl certificate setup and disabling CSS extraction.

Your situation may require even more work. If it does, consider using yarn watch instead. The dev-server is supposed to make your life easier. If it doesn't leave it behind.

Next, let's really start organizing our app - and making it more realistic - by splitting our code into several new components.

Leave a comment!

8
Login or Register to join the conversation
Pablo Avatar
Pablo Avatar Pablo | posted 2 months ago | edited

If you run an external development server with a local domain and webpack-encore keeps outputting "http://localhost:8080" in your header.

Try this:

yarn dev-server --host 0.0.0.0 --hot --disable-host-check --public 'http://[replace-with-your-domain]:8080'

Also works with yarn inside a docker container, but you must expose the container port (-p 8080:8080) and open the firewall.

1 Reply

Hey Pablo,

Thanks for this tip! Yeah, using Symfony built-in web server may be simpler, but for more complex setups you need to do more work.

Cheers!

Reply
edin Avatar edin | posted 9 months ago | edited | HIGHLIGHTED

This HMR stuff is great, except when it's not :)

To elaborate: I had a lot of sass warnings when compiling assets like:

Deprecation Using / for division outside of calc() is deprecated and will be removed in Dart Sass 2.0.0.* 

These warnings were also visible on the website in a full screen modal. Only clicking on a X button top right made it go away.
Luckily you can suppress these sass warnings by adding some options to the sassLoader in webpack.config.js like this:

    .enableSassLoader((options) => {
        options.sassOptions = {
            quietDeps: true,
        };
    })

Just for full info: I did update everything before starting this course with composer update and also some packages in package.json (but that should not be related to the sass warnings)

1 Reply

Hey Edin,

Thank you for sharing these tips with others! Yeah, HMR is a cool thing... though might cause some problems, so use it at your own risk :)

Cheers!

Reply
Abelardo Avatar
Abelardo Avatar Abelardo | posted 10 months ago | edited

Hi there!
You wrote: If you use Webpack's dev-server, then as soon as it detects a change, our page automatically reloads. Cool... but not cool enough

But at my webpack configuration it doesn't work. I run yarn watch (yarn dev-server doesn't generate build files).

When I modify a .vue file, I have to hit the browser because it doesn't automatically reload the webpage.

I added these lines inside my Webpack config file but that doesn't work either:

Encore.configureWatchOptions(function(watchOptions) {
  // enable polling and check for changes every 250ms
  // polling is useful when running Encore inside a Virtual Machine
  watchOptions.poll = 250;
});

What's wrong? 🤔

Reply

Hey @Abelardo!

Ok, there are two similar, but not-identical "modes" for Webpack:

A) "watch" mode - this is what you get with yarn watch. This generates physical files into your public/build directory and it automatically rebuilds those files whenever you change one of then. But it doesn't automatically update what you see in the browser: you need to refresh so the browser downloads the new files.

B) "dev server" mode - this is what you get with yarn dev-server. THIS one, if HMR is enabled and can be leveraged by the JS you're writing (which in Vue, it should work) is the one where your browser automatically updates without refreshing. But, the dev-server is a bit strange. As you noticed, it does not generate any build files. That is expected. Instead, Symfony will output your script/link tags to point to something like https://localhost:8080/build/app.js and that localhost:8080 (or whatever the address is, I can't remember the default) is actually a mini "web server" that is run by dev-server that returns your assets. So, no physically files, but your files WILL be downloaded. So if you want HMR, use the "dev server" mode, but don't expect to see any physical files ;).

Cheers!

Reply
Lloyd L. Avatar
Lloyd L. Avatar Lloyd L. | posted 2 years ago

Significant changes since webpack-encore 1.0.0 have happened.

* https, is now defined within webpack.config.js
* New in version 1.0.0:Before Encore 1.0, you needed to pass a --hot flag at the command line to enable HMR. You also needed to disable CSS extraction to enable HMR for CSS. That is no longer needed.

see: https://symfony.com/doc/cur...

''' package.json versioning.
"devDependencies": {
"@symfony/stimulus-bridge": "^2.0.0",
"@symfony/webpack-encore": "^1.0.0",
"@vue/compiler-sfc": "^3.0.5",
"core-js": "^3.0.0",
"regenerator-runtime": "^0.13.2",
"sass": "^1.32.7",
"sass-loader": "^11.0.0",
"stimulus": "^2.0.0",
"vue": "^3.0.2",
"vue-loader": "^16.1.0",
"webpack-notifier": "^1.6.0"
},

Reply

Hey @Lloyd!

Ah yes! Thanks for mentioning those! I was aware (I wrote those changes!) but I didn’t think of how they would impact this spot if you’re using the latest version. We’ll add a note to help others :).

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This course is also built to work with Vue 3!

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@symfony/webpack-encore": "^0.30.0", // 0.30.2
        "axios": "^0.19.2", // 0.19.2
        "bootstrap": "^4.4.1", // 4.5.0
        "core-js": "^3.0.0", // 3.6.5
        "eslint": "^6.7.2", // 6.8.0
        "eslint-config-airbnb-base": "^14.0.0", // 14.1.0
        "eslint-plugin-import": "^2.19.1", // 2.20.2
        "eslint-plugin-vue": "^6.0.1", // 6.2.2
        "regenerator-runtime": "^0.13.2", // 0.13.5
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^8.0.0", // 8.0.2
        "vue": "^2.6.11", // 2.6.11
        "vue-loader": "^15.9.1", // 15.9.2
        "vue-template-compiler": "^2.6.11", // 2.6.11
        "webpack-notifier": "^1.6.0" // 1.8.0
    }
}
userVoice