If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeElimina la etiqueta <img>
, para que podamos ver nuestra página normal. No te preocupes por nuestro pequeño pingüino: seguimos teniéndolo aquí arriba, en el logotipo.
Cuando actualicemos la página, observa que tenemos un mensaje console.log()
... que dice que procede de assets/app.js
. Si nos dirigimos a assets/app.js
... ¡sí! ¡Ahí está!
... lines 1 - 6 | |
console.log('This log comes from assets/app.js - welcome to AssetMapper! 🎉') |
Sabemos que aquí podemos escribir código ES6 moderno, así como importar otros archivos. Vamos a hacer todo eso. Pero primero: ¿Cómo y por qué se ejecuta este archivo? Nuestro CSS se está cargando gracias a esta bonita y aburrida etiqueta<link>
. No vemos una etiqueta <script>
para app.js
... pero sí vemos esta funciónimportmap()
. Y ahí está la clave.
De vuelta al sitio, mira la fuente de la página. Aquí abajo... esto es lo que añade importmap
. Vamos a hablar de cada parte, pero lo más importante ahora mismo está en la parte inferior:
<script type="module">import 'app';</script>
Antes, cuando creamos un archivo app.js
dentro del directorio public/
, éste es casi exactamente el código que escribimos para cargarlo. Utilizamos import
y luego la ruta a ese archivo. Pero... esta vez, sólo dice app
. ¿No debería decir algo como/assets/app.12345.js
"? ¿Cómo sabe que app
se refiere a la versión final de este archivo? Aquí es donde brilla la parte importmap
, aquí arriba.
Esta sección se genera a partir de un archivo importmap.php
dentro de nuestro proyecto. El archivo no es superinteresante todavía: será más útil pronto, cuando hablemos de JavaScript de terceros. Pero tiene esta clave app
que apunta a nuestro archivo assets/app.js
utilizando su ruta lógica.
... lines 1 - 15 | |
return [ | |
'app' => [ | |
'path' => 'app.js', | |
'preload' => true, | |
], | |
]; |
Gracias a eso, este <script type="importmap">
se vuelca en la página. Cuando importas algo que no empieza por ".", "/" o "../", se denomina importación desnuda. Normalmente lo vemos en bibliotecas de terceros. En el entorno del navegador, cuando ve una "importación desnuda", tu navegador busca un importmap
en la página para encontrar una entrada que coincida. Nuestro navegador ve import 'app'
, encuentra esta clave aquí, y esa es la ruta que descarga. De hecho, copia esta ruta de aquí y la pega aquí. Por eso se está ejecutando nuestro archivo app.js
: ¡es un trabajo en equipo entre el importmap
y el<script type="module">
extra que arranca nuestra aplicación!
Lo mejor de importmap
es que no es una cosa de Symfony: es una cosa de Internet. Es cómo funciona tu navegador. Tenemos este archivo importmap.php
, que es una cosa de Symfony. Pero una vez que está en la página, tu navegador es la estrella.
Y importmap
funciona en... la mayoría de los navegadores. Si vas a "caniuse.com" y buscas "importmap"... actualmente funciona en cerca del 81% de los navegadores. Eso sería un gran problema, si no fuera porque la función importmap()
también vuelca un calco. Puedes verlo aquí. Gracias a esto, si un navegador no soporta importmap
, esto añade esa funcionalidad. Así que funcionará.
Entra en app.js
: vamos a escribir algo de código moderno. En assets/
, primero crea un nuevo directorio llamado lib/
. Y dentro de él, un nuevo archivo llamado vinyl.js
. Puedes organizar las cosas como quieras, y éste es un ejemplo de cómo aislar algo de código en su propio archivo.
Voy a pegar la misma clase que teníamos antes. De vuelta a app.js
, importa esto: import Vinyl
y puedo pulsar "tab" para autocompletar la partefrom './lib/vinyl'
. Instálalo utilizando el mismo código que antes... y luego console.log(mix.describe())
.
export default class { | |
constructor(title, year) { | |
this.title = title; | |
this.year = year; | |
} | |
describe() { | |
return `${this.title} was released in ${this.year}`; | |
} | |
} | |
... lines 11 - 12 |
import Vinyl from './lib/vinyl'; | |
const mix = new Vinyl('Awesome Mix Vol. 1', 2014); | |
console.log(mix.describe()); |
¡Me encanta! Estamos codificando como siempre y utilizando ./
para importar. Pero cuando pasamos y actualizamos... no funciona. Mira el 404 /assets/lib/vinyl
procedente de app.js
.
Entonces... ¿qué está pasando aquí? Hablaremos más adelante sobre depuración, pero aquí tienes una pista: si alguna vez notas que tu navegador intenta descargar una ruta que no incluye la parte "versión" en el nombre del archivo, algo va mal en tu ruta... y deberías comprobar si hay errores tipográficos.
Nuestro problema es que necesitamos añadir el .js
. Resulta que dejar el .js
desactivado es una cosa de Node... y funciona si estás programando en Node. Pero en verdaderos entornos JavaScript, como en tu navegador, sí necesitas incluirlo.
import Vinyl from './lib/vinyl.js'; | |
... lines 2 - 5 |
Si refrescamos ahora... ¡ya está! En realidad fue culpa de mi editor que faltara el .js
cuando lo autocompletó. Afortunadamente, ¡podemos arreglarlo! Entra en la configuración de tu PhpStorm y busca "usar extensión de archivo". En "Estilo de código" y "JavaScript", cambia "Usar extensión de archivo" a "Siempre".
Esta vez... si decimos import Vinyl
y pulsamos "tabulador", ¡bien! Obtenemos el .js
.
Pero la diversión no termina: hay algo interesante que ocurre entre bastidores. Haz clic en este console.log()
... como una forma fácil de ver el origen del archivo final app.js
.
Sí, su contenido es exactamente igual al del archivo original, incluido elimport from './lib/vinyl.js'
. Sólo hay un problema: ¡ese no es el nombre final del archivo vinyl.js
!
Ve a las herramientas de red, selecciona "JS" y busca "vinyl". Todos los archivos servidos por AssetMapper tienen una parte versionada en su nombre de archivo, y lo vemos para vinyl.js
. Pero entonces... ¿cómo demonios lee nuestro navegador ./lib/vinyl.js
y sabe que debe descargar este nombre de archivo tan largo?
La respuesta, si ves la fuente de la página, es... redoble dramático de tambores... el importmap
. Y esto me encanta. El importmap
se construye a partir de dos fuentes. La primera fuente es obvia: importmap.php
. Y pronto añadiremos más entradas. La segunda fuente es más sutil. Cada vez que nuestro JavaScript importa otro archivo JavaScript utilizando una ruta relativa, ese archivo importado se añade automáticamente.
Esto es poderoso. Significa que nuestro código final puede tener el aspecto original:./lib/vinyl.js
. Pero gracias a importmap
, nuestro navegador descargará inteligentemente el archivo real con la parte de la versión larga en el nombre. Esto es realmente un detalle interno, pero está bien ver cómo funciona.
Vale, hemos hablado un poco de importmaps
... pero no hemos visto su mayor superpotencia: utilizar paquetes de terceros. Exploremos eso a continuación.
Hey @Rufnex!
Yup! Good question - it's "easy", but I'm realizing that it's not as easy as I realized..
1) download it locally and commit it (e.g. public/js/es-module-shims.js
).
2) Then refer to it in the config:
# config/packages/asset_mapper.yaml
framework:
asset_mapper:
importmap_polyfill: '/js/es-module-shims.js'
That's it! The part that I don't like (and didn't realize until this moment), is that we don't run this importmap_polyfill
through AssetMapper - and we may need to fix that. If we DID run it through AssetMapper, then you could, for example, download it to assets/vendor/es-module-shims.js
and refer to it in the config via its logical path: vendor/es-module-shims.js
. That's not currently possible - hence why I had you put it directly into public/
.
Cheers!
// composer.json
{
"require": {
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"babdev/pagerfanta-bundle": "^4.0", // v4.2.0
"doctrine/doctrine-bundle": "^2.7", // 2.10.0
"doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.4
"doctrine/orm": "^2.12", // 2.15.2
"knplabs/knp-time-bundle": "^1.18", // v1.20.0
"pagerfanta/doctrine-orm-adapter": "^4.0", // v4.1.0
"pagerfanta/twig": "^4.0", // v4.1.0
"stof/doctrine-extensions-bundle": "^1.7", // v1.7.1
"symfony/asset": "6.3.*", // v6.3.0
"symfony/asset-mapper": "6.3.*", // v6.3.0
"symfony/console": "6.3.*", // v6.3.0
"symfony/dotenv": "6.3.*", // v6.3.0
"symfony/flex": "^2", // v2.3.1
"symfony/framework-bundle": "6.3.*", // v6.3.0
"symfony/http-client": "6.3.*", // v6.3.0
"symfony/monolog-bundle": "^3.0", // v3.8.0
"symfony/proxy-manager-bridge": "6.3.*", // v6.3.0
"symfony/runtime": "6.3.*", // v6.3.0
"symfony/stimulus-bundle": "^2.9", // v2.9.1
"symfony/twig-bundle": "6.3.*", // v6.3.0
"symfony/ux-turbo": "^2.9", // v2.9.1
"symfony/web-link": "6.3.*", // v6.3.0
"symfony/yaml": "6.3.*", // v6.3.0
"twig/extra-bundle": "^2.12|^3.0", // v3.6.1
"twig/twig": "^2.12|^3.0" // v3.6.1
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.4
"symfony/debug-bundle": "6.3.*", // v6.3.0
"symfony/maker-bundle": "^1.41", // v1.49.0
"symfony/stopwatch": "6.3.*", // v6.3.0
"symfony/web-profiler-bundle": "6.3.*", // v6.3.0
"zenstruck/foundry": "^1.21" // v1.33.0
}
}
Is it possible to add es-module-shims.js local, if you dont want to use external code?