gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Head back to the homepage and click any of the articles. In an earlier tutorial, we added this heart icon that, when you click it, makes an AJAX request and increases the counter. Well, part of this is faked on the backend, but you get the idea.
To make this more clear, let's add a Bootstrap tooltip: when the user hovers over the heart, we can say something like "Click to like". No problem: open up the template: article/show.html.twig
. And I'll remind you that this page has its own entry: article_show.js
:
... lines 1 - 80 | |
{% block javascripts %} | |
{{ parent() }} | |
{{ encore_entry_script_tags('article_show') }} | |
{% endblock %} | |
... lines 86 - 92 |
Go open that: assets/js/article_show.js
.
Ok, let's find the anchor tag in the template... there it is... and use multiple lines for sanity. Now add title="Click to Like"
:
... lines 1 - 4 | |
{% block content_body %} | |
<div class="row"> | |
<div class="col-sm-12"> | |
... line 8 | |
<div class="show-article-title-container d-inline-block pl-3 align-middle"> | |
... lines 10 - 15 | |
<span class="pl-2 article-details"> | |
... line 17 | |
<a href="{{ path('article_toggle_heart', {slug: article.slug}) }}" class="fa fa-heart-o like-article js-like-article" title="Click to Like!"></a> | |
</span> | |
... lines 20 - 24 | |
</div> | |
</div> | |
</div> | |
... lines 28 - 78 | |
{% endblock %} | |
... lines 80 - 92 |
To make this work, all we need to do is copy the js-like-article
class, go back to article_show.js
and add $('.js-like-article').tooltip()
, which is a function added by Bootstrap:
... lines 1 - 3 | |
$(document).ready(function() { | |
$('.js-like-article').tooltip(); | |
... lines 6 - 19 | |
}); |
Coolio! Let's try it. Refresh and... of course. It doesn't work:
...tooltip is not a function
This may or may not surprise you. Think about it: at the bottom of the page, the app.js
<script>
tags are loaded first. And, if you remember, inside of app.js
, we import jquery
and then bootstrap
, which adds the tooltip()
function to jQuery:
... lines 1 - 10 | |
import $ from 'jquery'; | |
import 'bootstrap'; // adds functions to jQuery | |
... lines 13 - 26 |
So, it's reasonable to think that, inside article_show.js
, when we import jquery
, we will get the same jQuery object that's already been modified by bootstrap
. And... that's almost true. When two different files import the same module, they do get the exact same object in memory.
However, by default, Webpack treats different entrypoints like totally separate applications. So if we import jquery
from app.js
and also from get_nice_message.js
, which is part of the same entry:
... lines 1 - 10 | |
import $ from 'jquery'; | |
import 'bootstrap'; // adds functions to jQuery | |
... lines 13 - 14 | |
import getNiceMessage from './components/get_nice_message'; | |
... lines 16 - 26 |
They will get the same jQuery object. But when we import jquery
from article_show.js
, we get a different object in memory. Each entrypoint has an isolated environment. It doesn't mean that jQuery is downloaded twice, it just means that we are given two different instances.
So the fix is simple: import 'bootstrap'
.
Refresh and... this time, it works.
Understanding that modules are not shared across entries is good to know. But this also relates to a feature I want to talk about: the runtime chunk.
In webpack.config.js
, at the very beginning of the tutorial, we commented out enableSingleRuntimeChunk()
and replaced it with disableSingleRuntimeChunk()
:
... lines 1 - 2 | |
Encore | |
... lines 4 - 30 | |
// will require an extra script tag for runtime.js | |
// but, you probably want this, unless you're building a single-page app | |
//.enableSingleRuntimeChunk() | |
.disableSingleRuntimeChunk() | |
... lines 35 - 76 | |
; | |
... lines 78 - 79 |
Now, let's reverse that:
... lines 1 - 2 | |
Encore | |
... lines 4 - 30 | |
// will require an extra script tag for runtime.js | |
// but, you probably want this, unless you're building a single-page app | |
.enableSingleRuntimeChunk() | |
//.disableSingleRuntimeChunk() | |
... lines 35 - 76 | |
; | |
... lines 78 - 79 |
Because we just modified the Webpack config, come back over, press Control
+ C
and restart it:
yarn watch
If you watch closely, you'll see an immediate difference. Every single entry now includes a new file called runtime.js
, which means that it's a new file that needs to be included as the first script tag before any entry. Of course, that's not a detail that we need to worry about because, when we refresh and view the page source, our Twig functions took care of rendering everything.
Ok, so... why? What did this change and why did we care? There are two things.
First, runtime.js
contains Webpack's "runtime" code: stuff it needs to get its job done. By enabling the single runtime chunk you're saying:
Hey Webpack! Instead of adding this code at the beginning of
app.js
and at the beginning ofarticle_show.js
and all my other entry files, only add it once toruntime.js
The user now has to download an extra file, but all the entry files are a bit smaller. But, there's more to it than that. The runtime.js
file contains something called the "manifest", which is a fancy name that Webpack gives to code that contains some internal IDs that Webpack uses to identify different parts of your code. The key this is that those IDs often change between builds. So, by isolating that code into runtime.js
, it means that our other JavaScript files - the ones that contain our big code - will change less often: when those internal IDs change, it will not affect their content.
The tl;dr is that the smaller runtime.js
will change more often, but our bigger JavaScript files will change less often. That's great for caching.
The other thing that enableSingleRuntimeChunk()
changes may or may not be a good thing. Go back to article_show.js
and comment out import 'bootstrap'
. Now, move over and refresh.
Yea, it works! When you enable the single runtime chunk, it has a side effect: modules are shared across your entry points: they all work a bit more like one, single application. That's not necessarily a good or bad thing: just something to be aware of. I still do recommend treating each entry file like its own independent environment, even if there is some sharing.
Next: it's time to talk about async imports! Have some code that's only used in certain situations? Make your built files smaller by loading it... effectively, via AJAX.
Hey @Mina-R,
yes it will be shared in compiled code, but in source code you still need to import it.
Cheers!
Hello I have a question regarding "The runtime.js file contains something called the "manifest", which is a fancy name that Webpack gives to code that contains some internal IDs that Webpack uses to identify different parts of your code."
I was looking into my own webpack configuration and from what I understand, the runtime.js generated doesn't seem to be change and the ID that might change would be in the chunks that I generate. If that's the case, would that mean the benefit of not building our big .js files as often is incorrect? Thanks!
Hey Josh!
Yea... this stuff is super complicated - I've spent a lot of time looking into it for Encore... but it's also something that has changed over time :). So, you're correct that what's changing is the "id" that is used for each chunk. There are two important things to consider for this:
1) For a specific chunk, the id shouldn't change unnecessarily - like it shouldn't be called "1" and then suddenly "2" later. If I remember correctly, we use something in Encore (in production - it IS different in dev) to generate these in a static way so that they only change (I think) when the chunk filename changes.
2) When chunk ids do change (or new chunks are added) the second question is: in which file is this reflected? If we add a few new chunks to the system, we want those changes (ideally) to live in the small runtime.js and not big.js
Based on all of this, let me know if you're seeing something that doesn't make sense in your app.
Cheers!
When you said "I still do recommend treating each entry file like its own independent environment, even if there is some sharing.", do you recommend never using "enableSingleRuntimeChunk ()"?
Hey Sébastien J.
To the contrary, Ryan recommends to enable it but to be aware of the code sharing.
Cheers!
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"aws/aws-sdk-php": "^3.87", // 3.91.4
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.1
"knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
"knplabs/knp-time-bundle": "^1.8", // 1.9.0
"league/flysystem-aws-s3-v3": "^1.0", // 1.0.22
"league/flysystem-cached-adapter": "^1.0", // 1.0.9
"liip/imagine-bundle": "^2.1", // 2.1.0
"nexylan/slack-bundle": "^2.0,<2.2.0", // v2.1.0
"oneup/flysystem-bundle": "^3.0", // 3.0.3
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"sensio/framework-extra-bundle": "^5.1", // v5.3.1
"stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
"symfony/asset": "^4.0", // v4.2.5
"symfony/console": "^4.0", // v4.2.5
"symfony/flex": "^1.9", // v1.17.6
"symfony/form": "^4.0", // v4.2.5
"symfony/framework-bundle": "^4.0", // v4.2.5
"symfony/orm-pack": "^1.0", // v1.0.6
"symfony/security-bundle": "^4.0", // v4.2.5
"symfony/serializer-pack": "^1.0", // v1.0.2
"symfony/twig-bundle": "^4.0", // v4.2.5
"symfony/validator": "^4.0", // v4.2.5
"symfony/web-server-bundle": "^4.0", // v4.2.5
"symfony/webpack-encore-bundle": "^1.4", // v1.5.0
"symfony/yaml": "^4.0", // v4.2.5
"twig/extensions": "^1.5" // v1.5.4
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.0", // 3.1.0
"easycorp/easy-log-handler": "^1.0.2", // v1.0.7
"fzaninotto/faker": "^1.7", // v1.8.0
"symfony/debug-bundle": "^3.3|^4.0", // v4.2.5
"symfony/dotenv": "^4.0", // v4.2.5
"symfony/maker-bundle": "^1.0", // v1.11.5
"symfony/monolog-bundle": "^3.0", // v3.3.1
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.2.5
"symfony/profiler-pack": "^1.0", // v1.0.4
"symfony/var-dumper": "^3.3|^4.0" // v4.2.5
}
}
// package.json
{
"devDependencies": {
"@symfony/webpack-encore": "^0.27.0", // 0.27.0
"autocomplete.js": "^0.36.0",
"autoprefixer": "^9.5.1", // 9.5.1
"bootstrap": "^4.3.1", // 4.3.1
"core-js": "^3.0.0", // 3.0.1
"dropzone": "^5.5.1", // 5.5.1
"font-awesome": "^4.7.0", // 4.7.0
"jquery": "^3.4.0", // 3.4.0
"popper.js": "^1.15.0",
"postcss-loader": "^3.0.0", // 3.0.0
"sass": "^1.29.0", // 1.29.0
"sass-loader": "^7.0.1", // 7.3.1
"sortablejs": "^1.8.4", // 1.8.4
"webpack-notifier": "^1.6.0" // 1.7.0
}
}
hello i am configuring the
enableSingleRuntimeChunk()
in thewebpack.config.js
but when i used it with the tooltip component from bootstrapto make a tooltip in other entry
about.js
and the importing of the tooltip component is on theapp.js
the tooltip is not working, the tooltip is not working in about page i dont know why but as far as i know that enabling this feature shares the tooltip comp. between all of the entry points so why it doesn't work:and