Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Empaquetar JS y CSS con Encore

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Cuando instalamos Webpack Encore, su receta nos dio este nuevo directorio assets/. Mira el archivo app.js. Es interesante. Observa cómo importa este archivo bootstrap. En realidad es bootstrap.js: este archivo de aquí. La extensión .js es opcional.

Importaciones de JavaScript

Esta es una de las cosas más importantes que nos da Webpack: la capacidad de importar un archivo JavaScript de otro. Podemos importar funciones, objetos... realmente cualquier cosa desde otro archivo. Vamos a hablar más sobre este archivobootstrap.js dentro de un rato.

Esto también importa un archivo CSS? Si no has visto esto antes, puede parecer... raro: ¿JavaScript importando CSS?

Para ver cómo funciona todo esto, en app.js, añade un console.log().

15 lines assets/app.js
... lines 1 - 12
console.log('Hi! My name is app.js!');

Y app.css ya tiene un fondo de cuerpo... pero añade un !important para que podamos ver definitivamente si se está cargando.

body {
background-color: lightgray !important;
}

Vale... ¿entonces quién lee estos archivos? Porque... no viven en el directorio public/... así que no podemos crear etiquetas script o link que apunten directamente a ellos.

webpack.config.js

Para responder a esto, abre webpack.config.js. Webpack Encore es un binario ejecutable: vamos a ejecutarlo en un minuto. Cuando lo hagamos, cargará este archivo para obtener su configuración.

Y aunque hay un montón de funciones dentro de Webpack, lo único en lo que tenemos que centrarnos ahora es en esta: addEntry(). Este app puede ser cualquier cosa... como dinosaur, no importa. Te mostraré cómo se utiliza en un minuto. Lo importante es que apunta al archivo assets/app.js. Por ello,app.js será el primer y único archivo que Webpack analizará.

Esto es bastante bueno: Webpack leerá el archivo app.js y luego seguirá todas las declaraciones deimport recursivamente hasta que finalmente tenga una colección gigante de todo el JavaScript y el CSS que nuestra aplicación necesita. Entonces, lo escribirá en el directorio public/.

Ejecutando Webpack Encore

Vamos a verlo en acción. Busca tu terminal y ejecuta:

yarn watch

Esto es, como dice, un atajo para ejecutar encore dev --watch. Si miras tu archivo package.json, viene con una sección script con algunos atajos.

En cualquier caso, yarn watch hace dos cosas. En primer lugar, crea un nuevo directorio public/build/y, dentro, los archivos app.css y app.js Pero no dejes que los nombres te engañen: app.js contiene mucho más que lo que hay dentro de assets/app.js: contiene todo el JavaScript de todas las importaciones que encuentra. app.css contiene todo el CSS de todas las importaciones.

La razón por la que estos archivos se llaman app.css y app.js es por el nombre de la entrada.

Así que la conclusión es que, gracias a Encore, de repente tenemos nuevos archivos en el directoriopublic/build/ que contienen todo el JavaScript y el CSS que necesita nuestra aplicación

Las funciones Twig de Encore

Y si te diriges a tu página de inicio y la actualizas... ¡woh! Ha funcionado al instante!? El fondo ha cambiado... y en mi inspector... ¡está el registro de la consola! ¿Cómo diablos ha ocurrido eso?

Abre tu diseño base: templates/base.html.twig. El secreto está en las funcionesencore_entry_link_tags() y encore_entry_script_tags(). Apuesto a que puedes adivinar lo que hacen: añadir la etiqueta link a build/app.css y la etiqueta scripta build/app.js.

Puedes ver esto en tu navegador. Mira la fuente de la página y... ¡sí! La etiqueta link para /build/app.css... y la etiqueta script para /build/app.js. Ah, pero también ha renderizado otras dos etiquetas script. Eso es porque Webpack es muy inteligente. Por motivos de rendimiento, en lugar de volcar un gigantesco archivo app.js, a veces Webpack lo divide en varios archivos más pequeños. Afortunadamente, estas funciones Twig de Encore son lo suficientemente inteligentes como para manejar eso: incluirá todas las etiquetas de enlace o de script necesarias.

Lo más importante es que el código que tenemos en nuestro archivo assets/app.js-incluyendo todo lo que importa- ¡ahora funciona y aparece en nuestra página!

Vigilancia de los cambios

Ah, y como hemos ejecutado yarn watch, Encore sigue funcionando en segundo plano en busca de cambios. Compruébalo: entra en app.css... y cambia el color de fondo. Guarda, pasa y actualiza

body {
background-color: maroon !important;
}

¡Se actualiza instantáneamente! Eso es porque Encore se ha dado cuenta del cambio y ha recompilado el archivo construido muy rápidamente.

A continuación: vamos a trasladar nuestro CSS existente al nuevo sistema y a aprender cómo podemos instalar e importar bibliotecas de terceros -mira Bootstrap o FontAwesome- directamente en nuestra configuración de Encore.

Leave a comment!

28
Login or Register to join the conversation
Yangzhi Avatar
Yangzhi Avatar Yangzhi | posted hace 1 mes | edited

hi,if i add function or const on assets/app.js:

const demo='ok'

function test(){
    alert('this is test')
}
yarn run build

then i want call the test() function on Twig:

<button onclick='test()'>Test</button>

i got error, how to use it?

Reply

Hey Yangzhi,

It's difficult to say because we don't see the actual error. Could you share with us what exactly error you see?

Cheers!

Reply
Yangzhi Avatar
Yangzhi Avatar Yangzhi | Victor | posted hace 1 mes | edited

hi, the error is:

test if not defined

if i change test function like this:

window.test=()=>{alert('ok')}

then all work fine. but i want kown is this is only and best way or has other best way?

Reply

Hey Yangzhi,

Ah, I see. Yeah, that's on purpose. Probably you found the best workaround for this with setting that function on global window object. The idea behind webpack encore is different, it makes all the code you're writing in JS files encapsulated (not global) - and that's awesome actually! No more mess in your JS code :)

So, you need to change the way you write your JS code. I'd recommend you to find the element (tag) in your JS code and register a listener for "click" event on that element instead of trying to call the method directly with onclick attribute - that's a legacy way of doing things now. Or, you can take a look into direction of Stimulus, we have a tutorial about it here: https://symfonycasts.com/screencast/stimulus - it has similar (but much cooler) way of declaring actions for special events like click on an element, etc.

Cheers!

Reply
red_smeg Avatar
red_smeg Avatar red_smeg | posted hace 2 meses

Using Symfony 6.1.6 and yarn 1.22.19.

running yarn Watch I get

getting. Error: Cannot find module 'webpack/bin/webpack'

I also received "Error: Cannot find module '@babel/core" but for that I could do yarn add @babel/core

How do I solve this ?

Reply

Hi,

Try to run yarn install and then yarn watch again

Cheers!

Reply
red_smeg Avatar

didn't work its complaining about a file that exists in the node_modules tree

Reply

Can you please share your package.json file? That's pretty strange behaviour.

Reply
red_smeg Avatar
red_smeg Avatar red_smeg | sadikoff | posted hace 2 meses | edited
{
    "devDependencies": {
        "@hotwired/stimulus": "^3.0.0", 
        "@popperjs/core": "^2.10.2", 
        "@symfony/stimulus-bridge": "^3.0.0", 
        "@symfony/webpack-encore": "^4.1.1", 
        "axios": "^0.24.0", 
        "bootstrap": "^5.0.0", 
        "core-js": "^3.0.0", 
        "jquery": "^3.6.0", 
        "regenerator-runtime": "^0.13.2", 
        "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" 
    }
}
Reply

Hm ok, let's think differently. Is your code related to this chapter? I'm asking because I see some packages which should be added later in this tutorial.

Also I'd like you to test one thing, can you tweak your package.json modify webpack-encore version to

"@symfony/webpack-encore": "^1.7.0",

then try again yarn install and then yarn watch

Cheers!

Reply
red_smeg Avatar
red_smeg Avatar red_smeg | sadikoff | posted hace 2 meses | edited

let me see. I changed it to 1.7.0 and it all works.

Reply

Yo! that's nice 🤩

Reply
Eric Avatar

Hi,
my usecase is the following:
I have 3rd-party page template and organized the js-files in a separate folder in /assets. I imported them in a separate template.js. All compiles flawlessly. I use the entry_tags in my twig template. All good.

But when I load the page it tries to load some of the imported .js-files directly with https://localhost:port1234/path/like/in/assets/folder Obviously that path doesn't exist on the local dev server as the compiled files are in /build so it fails and the respective js-plugins don't work.

I have no clue were the GET to the files on that path is coming from and why it tries to load them like that at all. Can anyone help?

Reply

Hey @Eric!

Apologies for the slow reply - we're just getting back from Symfony conference week!

This sounds like very strange behavior. A few questions:

A) You said:

But when I load the page it tries to load some of the imported .js-files directly with...

When you look at the HTML source, do you see the physical <script tags with the wrong src="" attribute? Or do all the script paths look ok... but then something inside of those script files is then trying to download those strange paths?

B) When you look in the public/build/ directory, what do you see? Does anything look strange in there?

C) Do you have any special config in webpack.config.js?

D) You mentioned:

I have 3rd-party page template and organized the js-files in a separate folder in /assets. I imported them in a separate template.js.

Can you share some of that code? What does that template.js look like and what imports that file?

Cheers!

1 Reply
Steve-D Avatar

Hi Everyone

I've come to this part of the tutorial for some help, hopefully. I've recently update a webapp to Symfony v6.1 and all appeared well until I ran yarn watch. Everything was fine in v6.0

I got this error in terminal
Error: Cannot find module '@babel/core'
I ran yarn add @babel/core
Sorted.

Running yarn watch now gives me:
Error: Cannot find module 'webpack/bin/webpack'
Require stack:

  • /Users/steve/Sites/symfony/v6/webapps/node_modules/@symfony/webpack-encore/bin/encore.js

And now I'm stuck. Is this related in anyway to you guys now using Stimulus or is it something else i'm missing? I've not got as far as using Stimulus yet.

I've tried removing and re-adding Webpack encore (this did initially remove my package.json file too, not sure if that's a problem)

Thank you

Steve

Reply
Steve-D Avatar

and by coming back to this tutorial, in particular the previous video I've found the answer...

I removed webpack encore and installed as instructed encore, copied everything over from the previous package.json, this file is removed regardless of how it was added automatically, and then updated the webpack config file. All is super great again...few.

One question why does the main Symfony site say to use: composer require symfony/webpack-encore-bundle? What's the big difference?

Cheers

Steve

Reply
red_smeg Avatar

what exact steps did you take I have the same problem !!

Reply
Jesse-Rushlow Avatar
Jesse-Rushlow Avatar Jesse-Rushlow | SFCASTS | Steve-D | posted hace 3 meses

Howdy Steve,

symfony/webpack-encore is the JavaScript library for Symfony Webpack Encore. Whereas symfony/webpack-encore-bundle is the PHP library, in the form of a Symfony Bundle, that configures a Symfony Application to use Webpack Encore - including using Symfony's Flex recipes to configure and add symfony/webpack-encore to the projects JavaScript packages.

Basically webpack-encore goes in the package.json file. webpack-encore-bundle is for composer.json if that makes sense.

Reply
Steve-D Avatar

Hey Jesse

That makes perfect sense, thank you for answering and clearing up my confusion.

Have a great day

Steve

1 Reply
Rufnex Avatar

Just a short question about the javascript embedding. Should it embed at the end oft the html instead of the head areas?

Reply
Victor Avatar Victor | SFCASTS | Rufnex | posted hace 4 meses | HIGHLIGHTED

Hey Rufnex,

Good question! For many years it was recommended to include your JS at the end of your body tag so that them do not block the page rendering and page loads faster. But lately things changed, and now it's recommended to include all your JS/CSS in the head of the page again... but adding the defer attribute to it. Check a few screencasts to learn more about it:

As you can see, Webpack Encore already knows how to handle it, you just need to configure it.

Cheers!

3 Reply
Rufnex Avatar

Thank you for your great Input. Now i've got it ;o)

Reply

Hey Rufnex,

Perfect! I'm glad to hear it ;)

Cheers!

1 Reply

although the site works correctly but they show me these errors in the console

Reply

Hello, i have JS error in console,

Uncaught (in promise) TypeError: Cannot destructure property 'eligibleURLPatterns' of 'Jt' as it is undefined companion-bubble.js:1436
at companion-bubble.js:1436:11909
at Generator.next (<anonymous>)
at tn (companion-bubble.js:1436:11431)

Uncaught TypeError: Cannot read properties of null (reading 'style') (index):212
at renderAjaxRequests ((index):212:10065)
at finishAjaxRequest ((index):212:15943)
at (index):212:21834
at xhr.onreadystatechange ((index):212:7896)

Uncaught (in promise) TypeError: Cannot read properties of null (reading 'style') (index):212
at renderAjaxRequests ((index):212:10065)
at finishAjaxRequest ((index):212:15943)
at (index):212:18218

Reply
Petr L. Avatar

Hello, I feel like there's something I'm doing wrong, but I simply cannot figure out what it is; the background color of the page won't change regardless of what I do, it always stays "lightgrey" and keeps overwriting the custom app.css that I copied earlier from the tutorial folder.
The issue I'm facing specifically is that even when I update assets/styles/app.css to a different color (let's say "maroon"), it stay lightgrey, even though the "maroon" is stated in app.css in the /build directory. According to the developer tools, the file that overwrites the color of <body> stems from webpack///assets/styles/app.css. I also noticed that the console log specified in app.js will never go through either, the console stays empty.
Everything else works as intended, yarn apparently works, but I've had this default lightgrey color there from the very beginning of the whole course and I can't figure out how to rewrite it.

Reply

Hi Petr L.!

Apologies for the slow reply! This is super weird... it's as if your code is "stuck" with the original code. That's especially true since you said the console.log in app.js also doesn't seem to work.

First:

> According to the developer tools, the file that overwrites the color of <body> stems from webpack///assets/styles/app.css.

Encore publishes a sourcemap, which helps map the styles to the "source" filename. So in case you were wondering why it doesn't say "build/app.css", that's why. It's loading it from build/app.css... but then the sourcemap tells it that the true source is assets/styles/app.css. But this doesn't answer what's wrong at all - just mentioning this.

Ok, so let's do some debugging! You mentioned that:

> even though the "maroon" is stated in app.css in the /build directory

That's a good detail to check into. To be absolutely sure, stop Encore, completely empty the public/build directory, then re-run Encore. NOW look inside there again. Does the public/build/app.css file have maroon inside of it or lightgrey?

What happens if you make a JavaScript syntax error in assets/app.js? You should get an error in your console from Encore. Do you? Or not?

Finally, triple check that you browser isn't loading out-of-date content by doing a force refresh (or opening the site in incognito mode).

Let me know what you find out - something is definitely wrong, but I bet it's a minor thing (the minor things are the hard ones to track down!).

Cheers!

Reply
Otacon Avatar

Hello , got the same issue with the app.js. cleaned my browser cache and finaly got the console.log :)

Reply
Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.0.2",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "symfony/asset": "6.0.*", // v6.0.3
        "symfony/console": "6.0.*", // v6.0.3
        "symfony/dotenv": "6.0.*", // v6.0.3
        "symfony/flex": "^2", // v2.1.5
        "symfony/framework-bundle": "6.0.*", // v6.0.4
        "symfony/monolog-bundle": "^3.0", // v3.7.1
        "symfony/runtime": "6.0.*", // v6.0.3
        "symfony/twig-bundle": "6.0.*", // v6.0.3
        "symfony/ux-turbo": "^2.0", // v2.0.1
        "symfony/webpack-encore-bundle": "^1.13", // v1.13.2
        "symfony/yaml": "6.0.*", // v6.0.3
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.8
        "twig/twig": "^2.12|^3.0" // v3.3.8
    },
    "require-dev": {
        "symfony/debug-bundle": "6.0.*", // v6.0.3
        "symfony/stopwatch": "6.0.*", // v6.0.3
        "symfony/web-profiler-bundle": "6.0.*" // v6.0.3
    }
}