-
Notifications
You must be signed in to change notification settings - Fork 0
/
asset_pipeline.html
834 lines (804 loc) · 101 KB
/
asset_pipeline.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
<!doctype html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>El Pipeline de Activos — Ruby on Rails Guides</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style-v2.css" data-turbo-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/print-v2.css" media="print">
<link rel="stylesheet" type="text/css" href="stylesheets/highlight-v2.css" data-turbo-track="reload">
<link rel="icon" href="images/favicon.ico" sizes="any">
<link rel="apple-touch-icon" href="images/icon.png">
<script src="javascripts/@hotwired--turbo.js" data-turbo-track="reload"></script>
<script src="javascripts/clipboard.js" data-turbo-track="reload"></script>
<script src="javascripts/guides.js" data-turbo-track="reload"></script>
<meta property="og:title" content="El Pipeline de Activos — Ruby on Rails Guides" />
<meta name="description" content="El Pipeline de ActivosEsta guía cubre el pipeline de activos.Después de leer esta guía, sabrás: Qué es el pipeline de activos y qué hace. Cómo organizar adecuadamente los activos de tu aplicación. Los beneficios del pipeline de activos. Cómo agregar un preprocesador al pipeline. Cómo empaquetar activos con una gema." />
<meta property="og:description" content="El Pipeline de ActivosEsta guía cubre el pipeline de activos.Después de leer esta guía, sabrás: Qué es el pipeline de activos y qué hace. Cómo organizar adecuadamente los activos de tu aplicación. Los beneficios del pipeline de activos. Cómo agregar un preprocesador al pipeline. Cómo empaquetar activos con una gema." />
<meta property="og:locale" content="en_US" />
<meta property="og:site_name" content="Ruby on Rails Guides" />
<meta property="og:image" content="https://avatars.githubusercontent.com/u/4223" />
<meta property="og:type" content="website" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Arabic:[email protected]&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Heebo:[email protected]&family=Noto+Sans+Arabic:[email protected]&display=swap" rel="stylesheet">
<meta name="theme-color" content="#C81418">
</head>
<body class="guide">
<nav id="topNav" aria-label="Secondary">
<div class="wrapper">
<strong class="more-info-label">Más en <a href="https://rubyonrails.org/">rubyonrails.org:</a> </strong>
<span class="red-button more-info-button">
Más Ruby on Rails
</span>
<ul class="more-info-links s-hidden">
<li class="more-info"><a href="https://rubyonrails.org/blog">Blog</a></li>
<li class="more-info"><a href="https://guides.rubyonrails.org/">Guías</a></li>
<li class="more-info"><a href="https://api.rubyonrails.org/">API</a></li>
<li class="more-info"><a href="https://discuss.rubyonrails.org/">Foro</a></li>
<li class="more-info"><a href="https://github.com/rails/rails">Contribuir en GitHub</a></li>
</ul>
</div>
</nav>
<header id="page_header">
<div class="wrapper clearfix">
<nav id="feature_nav">
<div class="header-logo">
<a href="index.html" title="Regresar a la página principal de Guías para Edge">Guías</a>
<span id="version_switcher">
Versión:
<select class="guides-version">
<option value="https://edgeguides.rubyonrails.org/" selected>Edge</option>
<option value="https://guides.rubyonrails.org/v7.2/">7.2</option>
<option value="https://guides.rubyonrails.org/v7.1/">7.1</option>
<option value="https://guides.rubyonrails.org/v7.0/">7.0</option>
<option value="https://guides.rubyonrails.org/v6.1/">6.1</option>
<option value="https://guides.rubyonrails.org/v6.0/">6.0</option>
<option value="https://guides.rubyonrails.org/v5.2/">5.2</option>
<option value="https://guides.rubyonrails.org/v5.1/">5.1</option>
<option value="https://guides.rubyonrails.org/v5.0/">5.0</option>
<option value="https://guides.rubyonrails.org/v4.2/">4.2</option>
<option value="https://guides.rubyonrails.org/v4.1/">4.1</option>
<option value="https://guides.rubyonrails.org/v4.0/">4.0</option>
<option value="https://guides.rubyonrails.org/v3.2/">3.2</option>
<option value="https://guides.rubyonrails.org/v3.1/">3.1</option>
<option value="https://guides.rubyonrails.org/v3.0/">3.0</option>
<option value="https://guides.rubyonrails.org/v2.3/">2.3</option>
</select>
</span>
</div>
<ul class="nav">
<li><a class="nav-item" id="home_nav" href="https://rubyonrails.org/">Inicio</a></li>
<li class="guides-index guides-index-large">
<a href="index.html" id="guidesMenu" class="guides-index-item nav-item">Índice de Guías</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<dl class="guides-section-container">
<div class="guides-section">
<dt>Comienza Aquí</dt>
<dd><a href="getting_started.html">Primeros Pasos con Rails</a></dd>
</div>
<div class="guides-section">
<dt>Modelos</dt>
<dd><a href="active_record_basics.html">Conceptos Básicos de Active Record</a></dd>
<dd><a href="active_record_migrations.html">Migraciones de Active Record</a></dd>
<dd><a href="active_record_validations.html">Validaciones de Active Record</a></dd>
</div>
<div class="guides-section">
<dt>Vistas</dt>
<dd><a href="action_view_overview.html">Resumen de Action View</a></dd>
<dd><a href="layouts_and_rendering.html">Diseños y Renderizado en Rails</a></dd>
</div>
<div class="guides-section">
<dt>Controladores</dt>
<dd><a href="action_controller_overview.html">Resumen de Action Controller</a></dd>
<dd><a href="routing.html">Enrutamiento en Rails desde el Exterior</a></dd>
</div>
<div class="guides-section">
<dt>Otros Componentes</dt>
<dd><a href="active_support_core_extensions.html">Extensiones Básicas de Active Support</a></dd>
<dd><a href="action_mailer_basics.html">Conceptos Básicos de Action Mailer</a></dd>
<dd><a href="action_mailbox_basics.html">Conceptos Básicos de Action Mailbox</a></dd>
<dd><a href="action_text_overview.html">Resumen de Action Text</a></dd>
<dd><a href="active_job_basics.html">Conceptos Básicos de Active Job</a></dd>
</div>
<div class="guides-section">
<dt>Políticas</dt>
<dd><a href="maintenance_policy.html">Política de Mantenimiento</a></dd>
</div>
<div class="guides-section">
<dt>Notas de Lanzamiento</dt>
<dd><a href="upgrading_ruby_on_rails.html">Actualizando Ruby on Rails</a></dd>
<dd><a href="7_2_release_notes.html">Versión 7.2 - ?</a></dd>
<dd><a href="7_1_release_notes.html">Versión 7.1 - Octubre 2023</a></dd>
<dd><a href="7_0_release_notes.html">Versión 7.0 - Diciembre 2021</a></dd>
<dd><a href="6_1_release_notes.html">Versión 6.1 - Diciembre 2020</a></dd>
</div>
</dl>
</div>
</li>
<li><a class="nav-item" href="contributing_to_ruby_on_rails.html">Contribuir</a></li>
<li class="guides-index guides-index-small">
<select class="guides-index-item nav-item">
<option value="index.html">Índice de Guías</option>
<optgroup label="Comienza Aquí">
<option value="getting_started.html">Primeros Pasos con Rails</option>
</optgroup>
<optgroup label="Modelos">
<option value="active_record_basics.html">Conceptos Básicos de Active Record</option>
<option value="active_record_migrations.html">Migraciones de Active Record</option>
<option value="active_record_validations.html">Validaciones de Active Record</option>
</optgroup>
<optgroup label="Vistas">
<option value="action_view_overview.html">Resumen de Action View</option>
<option value="layouts_and_rendering.html">Diseños y Renderizado en Rails</option>
</optgroup>
<optgroup label="Controladores">
<option value="action_controller_overview.html">Resumen de Action Controller</option>
<option value="routing.html">Enrutamiento en Rails desde el Exterior</option>
</optgroup>
<optgroup label="Otros Componentes">
<option value="active_support_core_extensions.html">Extensiones Básicas de Active Support</option>
<option value="action_mailer_basics.html">Conceptos Básicos de Action Mailer</option>
<option value="action_mailbox_basics.html">Conceptos Básicos de Action Mailbox</option>
<option value="action_text_overview.html">Resumen de Action Text</option>
<option value="active_job_basics.html">Conceptos Básicos de Active Job</option>
</optgroup>
<optgroup label="Políticas">
<option value="maintenance_policy.html">Política de Mantenimiento</option>
</optgroup>
<optgroup label="Notas de Lanzamiento">
<option value="upgrading_ruby_on_rails.html">Actualizando Ruby on Rails</option>
<option value="7_2_release_notes.html">Versión 7.2 - ?</option>
<option value="7_1_release_notes.html">Versión 7.1 - Octubre 2023</option>
<option value="7_0_release_notes.html">Versión 7.0 - Diciembre 2021</option>
<option value="6_1_release_notes.html">Versión 6.1 - Diciembre 2020</option>
</optgroup>
</select>
</li>
</ul>
</nav>
</div>
</header>
<hr class="hide" />
<section id="feature">
<div class="wrapper">
<h1>El Pipeline de Activos</h1><p>Esta guía cubre el pipeline de activos.</p><p>Después de leer esta guía, sabrás:</p>
<ul>
<li>Qué es el pipeline de activos y qué hace.</li>
<li>Cómo organizar adecuadamente los activos de tu aplicación.</li>
<li>Los beneficios del pipeline de activos.</li>
<li>Cómo agregar un preprocesador al pipeline.</li>
<li>Cómo empaquetar activos con una gema.</li>
</ul>
<nav id="subCol">
<h3 class="chapter">
<picture>
<!-- Using the `source` HTML tag to set the dark theme image -->
<source
srcset="images/icon_book-close-bookmark-1-wht.svg"
media="(prefers-color-scheme: dark)"
/>
<img src="images/icon_book-close-bookmark-1.svg" alt="Chapter Icon" />
</picture>
Chapters
</h3>
<ol class="chapters">
<li><a href="#¿qué-es-el-pipeline-de-activos-questionmark">¿Qué es el Pipeline de Activos?</a></li>
<li><a href="#características-principales">Características Principales</a>
<ul>
<li><a href="#¿qué-es-la-huella-digital-y-por-qué-debería-importarme-questionmark">¿Qué es la Huella Digital y Por Qué Debería Importarme?</a></li>
<li><a href="#¿qué-son-los-import-maps-y-por-qué-debería-importarme-questionmark">¿Qué son los Import Maps y Por Qué Debería Importarme?</a></li>
</ul></li>
<li><a href="#cómo-usar-import-maps-como-pipeline-de-activos-de-javascript">Cómo usar Import Maps como Pipeline de Activos de JavaScript</a>
<ul>
<li><a href="#cómo-funciona">Cómo funciona</a></li>
<li><a href="#uso">Uso</a></li>
<li><a href="#usar-paquetes-npm-a-través-de-cdns-de-javascript">Usar paquetes npm a través de CDNs de JavaScript</a></li>
<li><a href="#pre-carga-de-módulos-fijados">Pre-carga de módulos fijados</a></li>
</ul></li>
<li><a href="#cómo-usar-sprockets">Cómo Usar Sprockets</a>
<ul>
<li><a href="#archivos-de-manifiesto-y-directivas">Archivos de Manifiesto y Directivas</a></li>
<li><a href="#activos-específicos-del-controlador">Activos Específicos del Controlador</a></li>
<li><a href="#organización-de-activos">Organización de Activos</a></li>
<li><a href="#codificación-de-enlaces-a-activos">Codificación de Enlaces a Activos</a></li>
<li><a href="#generar-un-error-cuando-un-activo-no-se-encuentra">Generar un Error Cuando un Activo No se Encuentra</a></li>
<li><a href="#desactivar-huellas-digitales">Desactivar Huellas Digitales</a></li>
<li><a href="#activar-mapas-de-fuentes">Activar Mapas de Fuentes</a></li>
</ul></li>
<li><a href="#en-producción">En Producción</a>
<ul>
<li><a href="#precompilación-de-activos">Precompilación de Activos</a></li>
<li><a href="#precompilación-local">Precompilación Local</a></li>
<li><a href="#compilación-en-vivo">Compilación en Vivo</a></li>
<li><a href="#cdns">CDNs</a></li>
</ul></li>
<li><a href="#personalizando-el-pipeline">Personalizando el Pipeline</a>
<ul>
<li><a href="#compresión-de-css">Compresión de CSS</a></li>
<li><a href="#compresión-de-javascript">Compresión de JavaScript</a></li>
<li><a href="#comprimir-tus-activos-con-gzip">Comprimir tus activos con GZIP</a></li>
<li><a href="#usar-tu-propio-compresor">Usar tu Propio Compresor</a></li>
<li><a href="#cambiar-la-ruta-de-assets">Cambiar la Ruta de <em>assets</em></a></li>
<li><a href="#encabezados-x-sendfile">Encabezados X-Sendfile</a></li>
</ul></li>
<li><a href="#almacenamiento-en-caché-de-activos">Almacenamiento en Caché de Activos</a></li>
<li><a href="#agregar-activos-a-tus-gemas">Agregar Activos a tus Gemas</a></li>
<li><a href="#hacer-que-tu-biblioteca-o-gema-sea-un-preprocesador">Hacer que tu Biblioteca o Gema sea un Preprocesador</a></li>
<li><a href="#bibliotecas-alternativas">Bibliotecas Alternativas</a>
<ul>
<li><a href="#jsbundling-rails">jsbundling-rails</a></li>
<li><a href="#webpacker-shakapacker">Webpacker/Shakapacker</a></li>
<li><a href="#cssbundling-rails">cssbundling-rails</a></li>
<li><a href="#dartsass-rails">dartsass-rails</a></li>
<li><a href="#tailwindcss-rails">tailwindcss-rails</a></li>
</ul></li>
</ol>
</nav>
<hr>
</div>
</section>
<main id="container">
<div class="wrapper">
<div id="mainCol">
<h2 id="¿qué-es-el-pipeline-de-activos-questionmark"><a class="anchorlink" href="#¿qué-es-el-pipeline-de-activos-questionmark"><span>1</span> ¿Qué es el Pipeline de Activos?</a></h2><p>El pipeline de activos proporciona un marco para manejar la entrega de activos JavaScript y CSS. Esto se hace aprovechando tecnologías como HTTP/2 y técnicas como concatenación y minificación. Finalmente, permite que tu aplicación se combine automáticamente con activos de otras gemas.</p><p>El pipeline de activos está implementado por las gemas <a href="https://github.com/rails/importmap-rails">importmap-rails</a>, <a href="https://github.com/rails/sprockets">sprockets</a> y <a href="https://github.com/rails/sprockets-rails">sprockets-rails</a>, y está habilitado por defecto. Puedes desactivarlo al crear una nueva aplicación pasando la opción <code>--skip-asset-pipeline</code>.</p><div class="interstitial code">
<pre><code class="highlight console"><span class="gp">$</span><span class="w"> </span><span class="nb">rails </span>new appname <span class="nt">--skip-asset-pipeline</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="rails new appname --skip-asset-pipeline
">Copy</button>
</div>
<p>NOTA: Esta guía se centra en el pipeline de activos predeterminado utilizando solo <code>sprockets</code> para CSS y <code>importmap-rails</code> para el procesamiento de JavaScript. La principal limitación de estos dos es que no hay soporte para transpilar, por lo que no puedes usar cosas como Babel, TypeScript, formato React JSX o Tailwind CSS. Te animamos a leer la <a href="#alternative-libraries">sección de Bibliotecas Alternativas</a> si necesitas transpilar tu JavaScript/CSS.</p><h2 id="características-principales"><a class="anchorlink" href="#características-principales"><span>2</span> Características Principales</a></h2><p>La primera característica del pipeline de activos es insertar una huella digital SHA256 en cada nombre de archivo para que el archivo sea cacheado por el navegador web y el CDN. Esta huella digital se actualiza automáticamente cuando cambias el contenido del archivo, lo que invalida el caché.</p><p>La segunda característica del pipeline de activos es usar <a href="https://github.com/WICG/import-maps">import maps</a> al servir archivos JavaScript. Esto te permite construir aplicaciones modernas utilizando bibliotecas JavaScript hechas para módulos ES (ESM) sin la necesidad de transpilar y empaquetar. A su vez, <strong>esto elimina la necesidad de Webpack, yarn, node o cualquier otra parte de la cadena de herramientas JavaScript</strong>.</p><p>La tercera característica del pipeline de activos es concatenar todos los archivos CSS en un archivo principal <code>.css</code>, que luego se minifica o comprime. Como aprenderás más adelante en esta guía, puedes personalizar esta estrategia para agrupar archivos de la manera que desees. En producción, Rails inserta una huella digital SHA256 en cada nombre de archivo para que el archivo sea cacheado por el navegador web. Puedes invalidar el caché alterando esta huella digital, lo que ocurre automáticamente cada vez que cambias el contenido del archivo.</p><p>La cuarta característica del pipeline de activos es que permite codificar activos a través de un lenguaje de nivel superior para CSS.</p><h3 id="¿qué-es-la-huella-digital-y-por-qué-debería-importarme-questionmark"><a class="anchorlink" href="#¿qué-es-la-huella-digital-y-por-qué-debería-importarme-questionmark"><span>2.1</span> ¿Qué es la Huella Digital y Por Qué Debería Importarme?</a></h3><p>La huella digital es una técnica que hace que el nombre de un archivo dependa del contenido del archivo. Cuando el contenido del archivo cambia, el nombre del archivo también cambia. Para contenido estático o que cambia con poca frecuencia, esto proporciona una manera fácil de saber si dos versiones de un archivo son idénticas, incluso a través de diferentes servidores o fechas de implementación.</p><p>Cuando un nombre de archivo es único y se basa en su contenido, se pueden establecer encabezados HTTP para alentar a los cachés en todas partes (ya sea en CDNs, ISPs, equipos de red o en navegadores web) a mantener su propia copia del contenido. Cuando el contenido se actualiza, la huella digital cambiará. Esto hará que los clientes remotos soliciten una nueva copia del contenido. Esto se conoce generalmente como <em>cache busting</em>.</p><p>La técnica que Sprockets utiliza para la huella digital es insertar un hash del contenido en el nombre, generalmente al final. Por ejemplo, un archivo CSS <code>global.css</code></p><div class="interstitial code">
<pre><code class="highlight plaintext">global-908e25f4bf641868d8683022a5b62f54.css
</code></pre>
<button class="clipboard-button" data-clipboard-text="global-908e25f4bf641868d8683022a5b62f54.css
">Copy</button>
</div>
<p>Esta es la estrategia adoptada por el pipeline de activos de Rails.</p><p>La huella digital está habilitada por defecto tanto para los entornos de desarrollo como de producción. Puedes habilitarla o deshabilitarla en tu configuración a través de la opción <a href="configuring.html#config-assets-digest"><code>config.assets.digest</code></a>.</p><h3 id="¿qué-son-los-import-maps-y-por-qué-debería-importarme-questionmark"><a class="anchorlink" href="#¿qué-son-los-import-maps-y-por-qué-debería-importarme-questionmark"><span>2.2</span> ¿Qué son los Import Maps y Por Qué Debería Importarme?</a></h3><p>Los import maps te permiten importar módulos JavaScript utilizando nombres lógicos que se mapean a archivos versionados/digeridos, directamente desde el navegador. Así puedes construir aplicaciones JavaScript modernas utilizando bibliotecas JavaScript hechas para módulos ES (ESM) sin la necesidad de transpilar o empaquetar.</p><p>Con este enfoque, enviarás muchos archivos JavaScript pequeños en lugar de un archivo JavaScript grande. Gracias a HTTP/2, eso ya no conlleva una penalización de rendimiento material durante el transporte inicial, y de hecho ofrece beneficios sustanciales a largo plazo debido a mejores dinámicas de caché.</p><h2 id="cómo-usar-import-maps-como-pipeline-de-activos-de-javascript"><a class="anchorlink" href="#cómo-usar-import-maps-como-pipeline-de-activos-de-javascript"><span>3</span> Cómo usar Import Maps como Pipeline de Activos de JavaScript</a></h2><p>Los Import Maps son el procesador JavaScript predeterminado, la lógica de generación de import maps es manejada por la gema <a href="https://github.com/rails/importmap-rails"><code>importmap-rails</code></a>.</p><p>ADVERTENCIA: Los import maps se utilizan solo para archivos JavaScript y no se pueden usar para la entrega de CSS. Consulta la <a href="#how-to-use-sprockets">sección de Sprockets</a> para aprender sobre CSS.</p><p>Puedes encontrar instrucciones detalladas de uso en la página de inicio de la gema, pero es importante entender los conceptos básicos de <code>importmap-rails</code>.</p><h3 id="cómo-funciona"><a class="anchorlink" href="#cómo-funciona"><span>3.1</span> Cómo funciona</a></h3><p>Los import maps son esencialmente una sustitución de cadenas para lo que se conoce como "especificadores de módulo descalzos". Te permiten estandarizar los nombres de las importaciones de módulos JavaScript.</p><p>Toma por ejemplo tal definición de importación, no funcionará sin un import map:</p><div class="interstitial code">
<pre><code class="highlight javascript"><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="import React from "react"
">Copy</button>
</div>
<p>Tendrías que definirlo así para que funcione:</p><div class="interstitial code">
<pre><code class="highlight javascript"><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">https://ga.jspm.io/npm:[email protected]/index.js</span><span class="dl">"</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="import React from "https://ga.jspm.io/npm:[email protected]/index.js"
">Copy</button>
</div>
<p>Aquí es donde entra el import map, definimos el nombre <code>react</code> para que esté fijado en la dirección <code>https://ga.jspm.io/npm:[email protected]/index.js</code>. Con tal información, nuestro navegador acepta la definición simplificada <code>import React from "react"</code>. Piensa en el import map como un alias para la dirección fuente de la biblioteca.</p><h3 id="uso"><a class="anchorlink" href="#uso"><span>3.2</span> Uso</a></h3><p>Con <code>importmap-rails</code> creas el archivo de configuración del import map fijando la ruta de la biblioteca a un nombre:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="c1"># config/importmap.rb</span>
<span class="n">pin</span> <span class="s2">"application"</span>
<span class="n">pin</span> <span class="s2">"react"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"https://ga.jspm.io/npm:[email protected]/index.js"</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="pin "application"
pin "react", to: "https://ga.jspm.io/npm:[email protected]/index.js"
">Copy</button>
</div>
<p>Todos los import maps configurados deben adjuntarse en el elemento <code><head></code> de tu aplicación agregando <code><%= javascript_importmap_tags %></code>. El <code>javascript_importmap_tags</code> genera un montón de scripts en el elemento <code>head</code>:</p>
<ul>
<li>JSON con todos los import maps configurados:</li>
</ul>
<div class="interstitial code">
<pre><code class="highlight html"><span class="nt"><script </span><span class="na">type=</span><span class="s">"importmap"</span><span class="nt">></span>
<span class="p">{</span>
<span class="dl">"</span><span class="s2">imports</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">application</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">/assets/application-39f16dc3f3....js</span><span class="dl">"</span>
<span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">https://ga.jspm.io/npm:[email protected]/index.js</span><span class="dl">"</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nt"></script></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<script type="importmap">
{
"imports": {
"application": "/assets/application-39f16dc3f3....js"
"react": "https://ga.jspm.io/npm:[email protected]/index.js"
}
}
</script>
">Copy</button>
</div>
<ul>
<li>Punto de entrada para cargar JavaScript desde <code>app/javascript/application.js</code>:</li>
</ul>
<div class="interstitial code">
<pre><code class="highlight html"><span class="nt"><script </span><span class="na">type=</span><span class="s">"module"</span><span class="nt">></span><span class="k">import</span> <span class="dl">"</span><span class="s2">application</span><span class="dl">"</span><span class="nt"></script></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<script type="module">import "application"</script>
">Copy</button>
</div>
<p>NOTA: Antes de la versión 2.0.0, <code>importmap-rails</code> incluía <a href="https://github.com/guybedford/es-module-shims"><code>Es-module-shims</code></a> en la salida de <code>javascript_importmap_tags</code> como un polyfill para garantizar el soporte de import maps en navegadores antiguos. Sin embargo, con el soporte nativo para import maps en todos los navegadores principales, la versión 2.0.0 ha eliminado el shim incluido. Si deseas soportar navegadores heredados que carecen de soporte para import maps, inserta manualmente <code>Es-module-shims</code> antes de <code>javascript_importmap_tags</code>. Para más información, consulta el <a href="https://github.com/rails/importmap-rails?tab=readme-ov-file#supporting-legacy-browsers-such-as-safari-on-ios-15">README de importmap-rails</a>.</p><h3 id="usar-paquetes-npm-a-través-de-cdns-de-javascript"><a class="anchorlink" href="#usar-paquetes-npm-a-través-de-cdns-de-javascript"><span>3.3</span> Usar paquetes npm a través de CDNs de JavaScript</a></h3><p>Puedes usar el comando <code>bin/importmap</code> que se agrega como parte de la instalación de <code>importmap-rails</code> para fijar, desfijar o actualizar paquetes npm en tu import map. El binstub utiliza <a href="https://jspm.org/"><code>JSPM.org</code></a>.</p><p>Funciona así:</p><div class="interstitial code">
<pre><code class="highlight console"><span class="gp">$</span><span class="w"> </span>bin/importmap pin react react-dom
<span class="go">Pinning "react" to https://ga.jspm.io/npm:[email protected]/index.js
Pinning "react-dom" to https://ga.jspm.io/npm:[email protected]/index.js
Pinning "object-assign" to https://ga.jspm.io/npm:[email protected]/index.js
Pinning "scheduler" to https://ga.jspm.io/npm:[email protected]/index.js
bin/importmap json
{
"imports": {
"application": "/assets/application-37f365cbecf1fa2810a8303f4b6571676fa1f9c56c248528bc14ddb857531b95.js",
"react": "https://ga.jspm.io/npm:[email protected]/index.js",
"react-dom": "https://ga.jspm.io/npm:[email protected]/index.js",
"object-assign": "https://ga.jspm.io/npm:[email protected]/index.js",
"scheduler": "https://ga.jspm.io/npm:[email protected]/index.js"
}
}
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="bin/importmap pin react react-dom
">Copy</button>
</div>
<p>Como puedes ver, los dos paquetes react y react-dom se resuelven en un total de cuatro dependencias, cuando se resuelven a través del jspm por defecto.</p><p>Ahora puedes usar estos en tu punto de entrada <code>application.js</code> como lo harías con cualquier otro módulo:</p><div class="interstitial code">
<pre><code class="highlight javascript"><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span>
<span class="k">import</span> <span class="nx">ReactDOM</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react-dom</span><span class="dl">"</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="import React from "react"
import ReactDOM from "react-dom"
">Copy</button>
</div>
<p>También puedes designar una versión específica para fijar:</p><div class="interstitial code">
<pre><code class="highlight console"><span class="gp">$</span><span class="w"> </span>bin/importmap pin [email protected]
<span class="go">Pinning "react" to https://ga.jspm.io/npm:[email protected]/index.js
Pinning "object-assign" to https://ga.jspm.io/npm:[email protected]/index.js
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="bin/importmap pin [email protected]
">Copy</button>
</div>
<p>O incluso eliminar fijaciones:</p><div class="interstitial code">
<pre><code class="highlight console"><span class="gp">$</span><span class="w"> </span>bin/importmap unpin react
<span class="go">Unpinning "react"
Unpinning "object-assign"
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="bin/importmap unpin react
">Copy</button>
</div>
<p>Puedes controlar el entorno del paquete para paquetes con compilaciones separadas de "producción" (el valor predeterminado) y "desarrollo":</p><div class="interstitial code">
<pre><code class="highlight console"><span class="gp">$</span><span class="w"> </span>bin/importmap pin react <span class="nt">--env</span> development
<span class="go">Pinning "react" to https://ga.jspm.io/npm:[email protected]/dev.index.js
Pinning "object-assign" to https://ga.jspm.io/npm:[email protected]/index.js
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="bin/importmap pin react --env development
">Copy</button>
</div>
<p>También puedes elegir un proveedor de CDN alternativo y compatible al fijar, como <a href="https://unpkg.com/"><code>unpkg</code></a> o <a href="https://www.jsdelivr.com/"><code>jsdelivr</code></a> (<a href="https://jspm.org/"><code>jspm</code></a> es el predeterminado):</p><div class="interstitial code">
<pre><code class="highlight console"><span class="gp">$</span><span class="w"> </span>bin/importmap pin react <span class="nt">--from</span> jsdelivr
<span class="go">Pinning "react" to https://cdn.jsdelivr.net/npm/[email protected]/index.js
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="bin/importmap pin react --from jsdelivr
">Copy</button>
</div>
<p>Recuerda, sin embargo, que si cambias una fijación de un proveedor a otro, es posible que debas limpiar las dependencias agregadas por el primer proveedor que no se utilizan por el segundo proveedor.</p><p>Ejecuta <code>bin/importmap</code> para ver todas las opciones.</p><p>Ten en cuenta que este comando es simplemente un envoltorio de conveniencia para resolver nombres de paquetes lógicos a URL de CDN. También puedes buscar las URL de CDN tú mismo y luego fijarlas. Por ejemplo, si quisieras usar Skypack para React, podrías simplemente agregar lo siguiente a <code>config/importmap.rb</code>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">pin</span> <span class="s2">"react"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"https://cdn.skypack.dev/react"</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="pin "react", to: "https://cdn.skypack.dev/react"
">Copy</button>
</div>
<h3 id="pre-carga-de-módulos-fijados"><a class="anchorlink" href="#pre-carga-de-módulos-fijados"><span>3.4</span> Pre-carga de módulos fijados</a></h3><p>Para evitar el efecto cascada donde el navegador tiene que cargar un archivo tras otro antes de poder llegar a la importación más anidada, importmap-rails soporta <a href="https://developers.google.com/web/updates/2017/12/modulepreload">enlaces modulepreload</a>. Los módulos fijados pueden ser precargados añadiendo <code>preload: true</code> a la fijación.</p><p>Es una buena idea precargar bibliotecas o frameworks que se utilizan en toda tu aplicación, ya que esto le dirá al navegador que los descargue antes.</p><p>Ejemplo:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="c1"># config/importmap.rb</span>
<span class="n">pin</span> <span class="s2">"@github/hotkey"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"https://ga.jspm.io/npm:@github/[email protected]/dist/index.js"</span><span class="p">,</span> <span class="ss">preload: </span><span class="kp">true</span>
<span class="n">pin</span> <span class="s2">"md5"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"https://cdn.jsdelivr.net/npm/[email protected]/md5.js"</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="pin "@github/hotkey", to: "https://ga.jspm.io/npm:@github/[email protected]/dist/index.js", preload: true
pin "md5", to: "https://cdn.jsdelivr.net/npm/[email protected]/md5.js"
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight erb"><span class="c"><%# app/views/layouts/application.html.erb %></span>
<span class="cp"><%=</span> <span class="n">javascript_importmap_tags</span> <span class="cp">%></span>
<span class="c"><%# incluirá el siguiente enlace antes de que se configure el importmap: %></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"modulepreload"</span> <span class="na">href=</span><span class="s">"https://ga.jspm.io/npm:@github/[email protected]/dist/index.js"</span><span class="nt">></span>
...
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= javascript_importmap_tags %>
<%# incluirá el siguiente enlace antes de que se configure el importmap: %>
<link rel="modulepreload" href="https://ga.jspm.io/npm:@github/[email protected]/dist/index.js">
...
">Copy</button>
</div>
<p>NOTA: Consulta el repositorio de <a href="https://github.com/rails/importmap-rails"><code>importmap-rails</code></a> para la documentación más actualizada.</p><h2 id="cómo-usar-sprockets"><a class="anchorlink" href="#cómo-usar-sprockets"><span>4</span> Cómo Usar Sprockets</a></h2><p>El enfoque ingenuo para exponer los activos de tu aplicación a la web sería almacenarlos en subdirectorios de la carpeta <code>public</code> como <code>images</code> y <code>stylesheets</code>. Hacerlo manualmente sería difícil, ya que la mayoría de las aplicaciones web modernas requieren que los activos se procesen de una manera específica, por ejemplo, comprimiendo y agregando huellas digitales a los activos.</p><p>Sprockets está diseñado para preprocesar automáticamente tus activos almacenados en los directorios configurados y, después del procesamiento, exponerlos en la carpeta <code>public/assets</code> con huellas digitales, compresión, generación de mapas de fuentes y otras características configurables.</p><p>Los activos aún pueden colocarse en la jerarquía <code>public</code>. Cualquier activo bajo <code>public</code> será servido como archivos estáticos por la aplicación o el servidor web cuando <a href="configuring.html#config-public-file-server-enabled"><code>config.public_file_server.enabled</code></a> esté configurado en true. Debes definir directivas <code>manifest.js</code> para archivos que deben someterse a algún preprocesamiento antes de ser servidos.</p><p>En producción, Rails precompila estos archivos en <code>public/assets</code> por defecto. Las copias precompiladas son luego servidas como activos estáticos por el servidor web. Los archivos en <code>app/assets</code> nunca se sirven directamente en producción.</p><h3 id="archivos-de-manifiesto-y-directivas"><a class="anchorlink" href="#archivos-de-manifiesto-y-directivas"><span>4.1</span> Archivos de Manifiesto y Directivas</a></h3><p>Al compilar activos con Sprockets, Sprockets necesita decidir qué objetivos de nivel superior compilar, generalmente <code>application.css</code> e imágenes. Los objetivos de nivel superior se definen en el archivo <code>manifest.js</code> de Sprockets, por defecto se ve así:</p><div class="interstitial code">
<pre><code class="highlight js"><span class="c1">//= link_tree ../images</span>
<span class="c1">//= link_directory ../stylesheets .css</span>
<span class="c1">//= link_tree ../../javascript .js</span>
<span class="c1">//= link_tree ../../../vendor/javascript .js</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="//= link_tree ../images
//= link_directory ../stylesheets .css
//= link_tree ../../javascript .js
//= link_tree ../../../vendor/javascript .js
">Copy</button>
</div>
<p>Contiene <em>directivas</em> - instrucciones que le dicen a Sprockets qué archivos requerir para construir un solo archivo CSS o JavaScript.</p><p>Esto está destinado a incluir el contenido de todos los archivos encontrados en el directorio <code>./app/assets/images</code> o cualquier subdirectorio, así como cualquier archivo reconocido como JS directamente en <code>./app/javascript</code> o <code>./vendor/javascript</code>.</p><p>Cargará cualquier CSS del directorio <code>./app/assets/stylesheets</code> (sin incluir subdirectorios). Suponiendo que tengas archivos <code>application.css</code> y <code>marketing.css</code> en la carpeta <code>./app/assets/stylesheets</code>, te permitirá cargar esas hojas de estilo con <code><%= stylesheet_link_tag "application" %></code> o <code><%= stylesheet_link_tag "marketing" %></code> desde tus vistas.</p><p>Podrías notar que nuestros archivos JavaScript no se cargan desde el directorio <code>assets</code> por defecto, es porque <code>./app/javascript</code> es el punto de entrada predeterminado para la gema <code>importmap-rails</code> y la carpeta <code>vendor</code> es el lugar donde se almacenarían los paquetes JS descargados.</p><p>En el <code>manifest.js</code> también podrías especificar la directiva <code>link</code> para cargar un archivo específico en lugar de todo el directorio. La directiva <code>link</code> requiere proporcionar una extensión de archivo explícita.</p><p>Sprockets carga los archivos especificados, los procesa si es necesario, los concatena en un solo archivo y luego los comprime (basado en el valor de <code>config.assets.css_compressor</code> o <code>config.assets.js_compressor</code>). La compresión reduce el tamaño del archivo, permitiendo que el navegador descargue los archivos más rápido.</p><h3 id="activos-específicos-del-controlador"><a class="anchorlink" href="#activos-específicos-del-controlador"><span>4.2</span> Activos Específicos del Controlador</a></h3><p>Cuando generas un scaffold o un controlador, Rails también genera un archivo de hoja de estilo en cascada para ese controlador. Además, al generar un scaffold, Rails genera el archivo <code>scaffolds.css</code>.</p><p>Por ejemplo, si generas un <code>ProjectsController</code>, Rails también agregará un nuevo archivo en <code>app/assets/stylesheets/projects.css</code>. Por defecto, estos archivos estarán listos para ser usados por tu aplicación inmediatamente usando la directiva <code>link_directory</code> en el archivo <code>manifest.js</code>.</p><p>También puedes optar por incluir archivos de hojas de estilo específicos del controlador solo en sus respectivos controladores usando lo siguiente:</p><div class="interstitial code">
<pre><code class="highlight erb"><span class="cp"><%=</span> <span class="n">stylesheet_link_tag</span> <span class="n">params</span><span class="p">[</span><span class="ss">:controller</span><span class="p">]</span> <span class="cp">%></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= stylesheet_link_tag params[:controller] %>
">Copy</button>
</div>
<p>Al hacer esto, asegúrate de no estar usando la directiva <code>require_tree</code> en tu <code>application.css</code>, ya que eso podría resultar en que tus activos específicos del controlador se incluyan más de una vez.</p><h3 id="organización-de-activos"><a class="anchorlink" href="#organización-de-activos"><span>4.3</span> Organización de Activos</a></h3><p>Los activos del pipeline pueden colocarse dentro de una aplicación en una de tres ubicaciones: <code>app/assets</code>, <code>lib/assets</code> o <code>vendor/assets</code>.</p>
<ul>
<li><p><code>app/assets</code> es para activos que son propiedad de la aplicación, como imágenes o hojas de estilo personalizadas.</p></li>
<li><p><code>app/javascript</code> es para tu código JavaScript</p></li>
<li><p><code>vendor/[assets|javascript]</code> es para activos que son propiedad de entidades externas, como frameworks CSS o bibliotecas JavaScript. Ten en cuenta que el código de terceros con referencias a otros archivos también procesados por el pipeline de activos (imágenes, hojas de estilo, etc.), necesitará ser reescrito para usar ayudantes como <code>asset_path</code>.</p></li>
</ul>
<p>Otras ubicaciones podrían configurarse en el archivo <code>manifest.js</code>, consulta la <a href="#manifest-files-and-directives">sección de Archivos de Manifiesto y Directivas</a>.</p><h4 id="rutas-de-búsqueda"><a class="anchorlink" href="#rutas-de-búsqueda"><span>4.3.1</span> Rutas de Búsqueda</a></h4><p>Cuando se hace referencia a un archivo desde un manifiesto o un ayudante, Sprockets busca en todas las ubicaciones especificadas en <code>manifest.js</code>. Puedes ver la ruta de búsqueda inspeccionando <a href="configuring.html#config-assets-paths"><code>Rails.application.config.assets.paths</code></a> en la consola de Rails.</p><h4 id="usar-archivos-índice-como-proxies-para-carpetas"><a class="anchorlink" href="#usar-archivos-índice-como-proxies-para-carpetas"><span>4.3.2</span> Usar Archivos Índice como Proxies para Carpetas</a></h4><p>Sprockets utiliza archivos llamados <code>index</code> (con las extensiones relevantes) para un propósito especial.</p><p>Por ejemplo, si tienes una biblioteca CSS con muchos módulos, que se almacena en <code>lib/assets/stylesheets/library_name</code>, el archivo <code>lib/assets/stylesheets/library_name/index.css</code> sirve como el manifiesto para todos los archivos en esta biblioteca. Este archivo podría incluir una lista de todos los archivos requeridos en orden, o una simple directiva <code>require_tree</code>.</p><p>También es algo similar a la forma en que un archivo en <code>public/library_name/index.html</code> puede ser alcanzado por una solicitud a <code>/library_name</code>. Esto significa que no puedes usar directamente un archivo índice.</p><p>La biblioteca como un todo puede ser accedida en los archivos <code>.css</code> así:</p><div class="interstitial code">
<pre><code class="highlight css"><span class="c">/* ...
*= require library_name
*/</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="/* ...
*= require library_name
*/
">Copy</button>
</div>
<p>Esto simplifica el mantenimiento y mantiene las cosas limpias al permitir que el código relacionado se agrupe antes de incluirse en otros lugares.</p><h3 id="codificación-de-enlaces-a-activos"><a class="anchorlink" href="#codificación-de-enlaces-a-activos"><span>4.4</span> Codificación de Enlaces a Activos</a></h3><p>Sprockets no agrega ningún método nuevo para acceder a tus activos: todavía usas el conocido <code>stylesheet_link_tag</code>:</p><div class="interstitial code">
<pre><code class="highlight erb"><span class="cp"><%=</span> <span class="n">stylesheet_link_tag</span> <span class="s2">"application"</span><span class="p">,</span> <span class="ss">media: </span><span class="s2">"all"</span> <span class="cp">%></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= stylesheet_link_tag "application", media: "all" %>
">Copy</button>
</div>
<p>Si usas la gema <a href="https://github.com/hotwired/turbo-rails"><code>turbo-rails</code></a>, que está incluida por defecto en Rails, entonces incluye la opción <code>data-turbo-track</code> que hace que Turbo verifique si un activo ha sido actualizado y, si es así, lo carga en la página:</p><div class="interstitial code">
<pre><code class="highlight erb"><span class="cp"><%=</span> <span class="n">stylesheet_link_tag</span> <span class="s2">"application"</span><span class="p">,</span> <span class="s2">"data-turbo-track"</span><span class="p">:</span> <span class="s2">"reload"</span> <span class="cp">%></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
">Copy</button>
</div>
<p>En vistas regulares puedes acceder a imágenes en el directorio <code>app/assets/images</code> así:</p><div class="interstitial code">
<pre><code class="highlight erb"><span class="cp"><%=</span> <span class="n">image_tag</span> <span class="s2">"rails.png"</span> <span class="cp">%></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= image_tag "rails.png" %>
">Copy</button>
</div>
<p>Siempre que el pipeline esté habilitado dentro de tu aplicación (y no deshabilitado en el contexto del entorno actual), este archivo es servido por Sprockets. Si existe un archivo en <code>public/assets/rails.png</code>, es servido por el servidor web.</p><p>Alternativamente, una solicitud para un archivo con un hash SHA256 como <code>public/assets/rails-f90d8a84c707a8dc923fca1ca1895ae8ed0a09237f6992015fef1e11be77c023.png</code> se trata de la misma manera. Cómo se generan estos hashes se cubre en la sección <a href="#in-production">En Producción</a> más adelante en esta guía.</p><p>Las imágenes también pueden organizarse en subdirectorios si es necesario, y luego pueden accederse especificando el nombre del directorio en la etiqueta:</p><div class="interstitial code">
<pre><code class="highlight erb"><span class="cp"><%=</span> <span class="n">image_tag</span> <span class="s2">"icons/rails.png"</span> <span class="cp">%></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= image_tag "icons/rails.png" %>
">Copy</button>
</div>
<p>ADVERTENCIA: Si estás precompilando tus activos (ver <a href="#in-production">En Producción</a> a continuación), enlazar a un activo que no existe generará una excepción en la página que llama. Esto incluye enlazar a una cadena en blanco. Por lo tanto, ten cuidado al usar <code>image_tag</code> y los otros ayudantes con datos proporcionados por el usuario.</p><h4 id="css-y-erb"><a class="anchorlink" href="#css-y-erb"><span>4.4.1</span> CSS y ERB</a></h4><p>El pipeline de activos evalúa automáticamente ERB. Esto significa que si agregas una extensión <code>erb</code> a un activo CSS (por ejemplo, <code>application.css.erb</code>), entonces los ayudantes como <code>asset_path</code> están disponibles en tus reglas CSS:</p><div class="interstitial code">
<pre><code class="highlight css"><span class="nc">.class</span> <span class="p">{</span> <span class="nl">background-image</span><span class="p">:</span> <span class="sx">url(<%= asset_path 'image.png' %>)</span> <span class="p">}</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text=".class { background-image: url(<%= asset_path 'image.png' %>) }
">Copy</button>
</div>
<p>Esto escribe la ruta al activo particular que se está haciendo referencia. En este ejemplo, tendría sentido tener una imagen en una de las rutas de carga de activos, como <code>app/assets/images/image.png</code>, que se referenciaría aquí. Si esta imagen ya está disponible en <code>public/assets</code> como un archivo con huella digital, entonces se hace referencia a esa ruta.</p><p>Si deseas usar un <a href="https://en.wikipedia.org/wiki/Data_URI_scheme">URI de datos</a>, un método para incrustar los datos de la imagen directamente en el archivo CSS, puedes usar el ayudante <code>asset_data_uri</code>.</p><div class="interstitial code">
<pre><code class="highlight css"><span class="nf">#logo</span> <span class="p">{</span> <span class="nl">background</span><span class="p">:</span> <span class="sx">url(<%= asset_data_uri 'logo.png' %>)</span> <span class="p">}</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="#logo { background: url(<%= asset_data_uri 'logo.png' %>) }
">Copy</button>
</div>
<p>Esto inserta un URI de datos correctamente formateado en la fuente CSS.</p><p>Ten en cuenta que la etiqueta de cierre no puede ser del estilo <code>-%></code>.</p><h3 id="generar-un-error-cuando-un-activo-no-se-encuentra"><a class="anchorlink" href="#generar-un-error-cuando-un-activo-no-se-encuentra"><span>4.5</span> Generar un Error Cuando un Activo No se Encuentra</a></h3><p>Si estás usando sprockets-rails >= 3.2.0, puedes configurar lo que sucede cuando se realiza una búsqueda de activos y no se encuentra nada. Si desactivas el "fallback de activos", se generará un error cuando no se pueda encontrar un activo.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">unknown_asset_fallback</span> <span class="o">=</span> <span class="kp">false</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.unknown_asset_fallback = false
">Copy</button>
</div>
<p>Si el "fallback de activos" está habilitado, entonces cuando no se pueda encontrar un activo, se imprimirá la ruta en su lugar y no se generará ningún error. El comportamiento del fallback de activos está deshabilitado por defecto.</p><h3 id="desactivar-huellas-digitales"><a class="anchorlink" href="#desactivar-huellas-digitales"><span>4.6</span> Desactivar Huellas Digitales</a></h3><p>Puedes desactivar las huellas digitales actualizando <code>config/environments/development.rb</code> para incluir:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">digest</span> <span class="o">=</span> <span class="kp">false</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.digest = false
">Copy</button>
</div>
<p>Cuando esta opción es verdadera, se generarán huellas digitales para las URLs de los activos.</p><h3 id="activar-mapas-de-fuentes"><a class="anchorlink" href="#activar-mapas-de-fuentes"><span>4.7</span> Activar Mapas de Fuentes</a></h3><p>Puedes activar los mapas de fuentes actualizando <code>config/environments/development.rb</code> para incluir:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">debug</span> <span class="o">=</span> <span class="kp">true</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.debug = true
">Copy</button>
</div>
<p>Cuando el modo de depuración está activado, Sprockets generará un Mapa de Fuentes para cada activo. Esto te permite depurar cada archivo individualmente en las herramientas de desarrollo de tu navegador.</p><p>Los activos se compilan y almacenan en caché en la primera solicitud después de que el servidor se inicia. Sprockets establece un encabezado HTTP <code>must-revalidate</code> para reducir la sobrecarga de solicitudes en solicitudes posteriores: en estas, el navegador obtiene una respuesta 304 (No Modificado).</p><p>Si alguno de los archivos en el manifiesto cambia entre solicitudes, el servidor responde con un nuevo archivo compilado.</p><h2 id="en-producción"><a class="anchorlink" href="#en-producción"><span>5</span> En Producción</a></h2><p>En el entorno de producción, Sprockets utiliza el esquema de huellas digitales descrito anteriormente. Por defecto, Rails asume que los activos han sido precompilados y serán servidos como activos estáticos por tu servidor web.</p><p>Durante la fase de precompilación, se genera un SHA256 a partir del contenido de los archivos compilados y se inserta en los nombres de archivo a medida que se escriben en el disco. Estos nombres con huellas digitales son utilizados por los ayudantes de Rails en lugar del nombre del manifiesto.</p><p>Por ejemplo, esto:</p><div class="interstitial code">
<pre><code class="highlight erb"><span class="cp"><%=</span> <span class="n">stylesheet_link_tag</span> <span class="s2">"application"</span> <span class="cp">%></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= stylesheet_link_tag "application" %>
">Copy</button>
</div>
<p>genera algo como esto:</p><div class="interstitial code">
<pre><code class="highlight html"><span class="nt"><link</span> <span class="na">href=</span><span class="s">"/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css"</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="nt">/></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" rel="stylesheet" />
">Copy</button>
</div>
<p>El comportamiento de las huellas digitales está controlado por la opción de inicialización <a href="configuring.html#config-assets-digest"><code>config.assets.digest</code></a> (que por defecto es <code>true</code>).</p><p>NOTA: En circunstancias normales, la opción predeterminada <code>config.assets.digest</code> no debería cambiarse. Si no hay huellas digitales en los nombres de archivo, y se establecen encabezados de futuro lejano, los clientes remotos nunca sabrán que deben volver a buscar los archivos cuando su contenido cambie.</p><h3 id="precompilación-de-activos"><a class="anchorlink" href="#precompilación-de-activos"><span>5.1</span> Precompilación de Activos</a></h3><p>Rails viene con un comando para compilar los manifiestos de activos y otros archivos en el pipeline.</p><p>Los activos compilados se escriben en la ubicación especificada en <a href="configuring.html#config-assets-prefix"><code>config.assets.prefix</code></a>. Por defecto, este es el directorio <code>/assets</code>.</p><p>Puedes llamar a este comando en el servidor durante la implementación para crear versiones compiladas de tus activos directamente en el servidor. Consulta la siguiente sección para obtener información sobre la compilación local.</p><p>El comando es:</p><div class="interstitial code">
<pre><code class="highlight console"><span class="gp">$</span><span class="w"> </span><span class="nv">RAILS_ENV</span><span class="o">=</span>production <span class="nb">rails </span>assets:precompile
</code></pre>
<button class="clipboard-button" data-clipboard-text="RAILS_ENV=production rails assets:precompile
">Copy</button>
</div>
<p>Esto enlaza la carpeta especificada en <code>config.assets.prefix</code> a <code>shared/assets</code>. Si ya usas esta carpeta compartida, necesitarás escribir tu propio comando de implementación.</p><p>Es importante que esta carpeta sea compartida entre implementaciones para que las páginas en caché remotamente que hacen referencia a los activos compilados antiguos aún funcionen durante la vida útil de la página en caché.</p><p>NOTA: Siempre especifica un nombre de archivo compilado esperado que termine con <code>.js</code> o <code>.css</code>.</p><p>El comando también genera un <code>.sprockets-manifest-randomhex.json</code> (donde <code>randomhex</code> es una cadena hexadecimal aleatoria de 16 bytes) que contiene una lista con todos tus activos y sus respectivas huellas digitales. Esto es utilizado por los métodos de ayuda de Rails para evitar devolver las solicitudes de mapeo a Sprockets. Un archivo de manifiesto típico se ve así:</p><div class="interstitial code">
<pre><code class="highlight json"><span class="p">{</span><span class="nl">"files"</span><span class="p">:{</span><span class="nl">"application-<fingerprint>.js"</span><span class="p">:{</span><span class="nl">"logical_path"</span><span class="p">:</span><span class="s2">"application.js"</span><span class="p">,</span><span class="nl">"mtime"</span><span class="p">:</span><span class="s2">"2016-12-23T20:12:03-05:00"</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">412383</span><span class="p">,</span><span class="w">
</span><span class="nl">"digest"</span><span class="p">:</span><span class="s2">"<fingerprint>"</span><span class="p">,</span><span class="nl">"integrity"</span><span class="p">:</span><span class="s2">"sha256-<random-string>"</span><span class="p">}},</span><span class="w">
</span><span class="nl">"assets"</span><span class="p">:{</span><span class="nl">"application.js"</span><span class="p">:</span><span class="s2">"application-<fingerprint>.js"</span><span class="p">}}</span><span class="w">
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="{"files":{"application-<fingerprint>.js":{"logical_path":"application.js","mtime":"2016-12-23T20:12:03-05:00","size":412383,
"digest":"<fingerprint>","integrity":"sha256-<random-string>"}},
"assets":{"application.js":"application-<fingerprint>.js"}}
">Copy</button>
</div>
<p>En tu aplicación, habrá más archivos y activos listados en el manifiesto, <code><fingerprint></code> y <code><random-string></code> también se generarán.</p><p>La ubicación predeterminada para el manifiesto es la raíz de la ubicación especificada en <code>config.assets.prefix</code> ('/assets' por defecto).</p><p>NOTA: Si faltan archivos precompilados en producción, recibirás una excepción <code>Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError</code> que indica el nombre del archivo(s) faltante(s).</p><h4 id="encabezado-expires-de-futuro-lejano"><a class="anchorlink" href="#encabezado-expires-de-futuro-lejano"><span>5.1.1</span> Encabezado Expires de Futuro Lejano</a></h4><p>Los activos precompilados existen en el sistema de archivos y son servidos directamente por tu servidor web. No tienen encabezados de futuro lejano por defecto, por lo que para obtener el beneficio de las huellas digitales, tendrás que actualizar la configuración de tu servidor para agregar esos encabezados.</p><p>Para Apache:</p><div class="interstitial code">
<pre><code class="highlight apache"><span class="c"># Las directivas Expires* requieren que el módulo Apache</span>
<span class="c"># `mod_expires` esté habilitado.</span>
<span class="p"><</span><span class="nl">Location</span><span class="sr"> /assets/</span><span class="p">>
</span> <span class="c"># El uso de ETag no se recomienda cuando Last-Modified está presente</span>
<span class="nc">Header</span> <span class="ss">unset</span> ETag
<span class="nc">FileETag</span> <span class="ss">None</span>
<span class="c"># RFC dice que solo se debe almacenar en caché durante 1 año</span>
<span class="nc">ExpiresActive</span> <span class="ss">On</span>
<span class="nc">ExpiresDefault</span> "access plus 1 year"
<span class="p"></</span><span class="nl">Location</span><span class="p">>
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="# Las directivas Expires* requieren que el módulo Apache
# `mod_expires` esté habilitado.
<Location /assets/>
# El uso de ETag no se recomienda cuando Last-Modified está presente
Header unset ETag
FileETag None
# RFC dice que solo se debe almacenar en caché durante 1 año
ExpiresActive On
ExpiresDefault "access plus 1 year"
</Location>
">Copy</button>
</div>
<p>Para NGINX:</p><div class="interstitial code">
<pre><code class="highlight nginx"><span class="k">location</span> <span class="p">~</span> <span class="sr">^/assets/</span> <span class="p">{</span>
<span class="kn">expires</span> <span class="s">1y</span><span class="p">;</span>
<span class="kn">add_header</span> <span class="s">Cache-Control</span> <span class="s">public</span><span class="p">;</span>
<span class="kn">add_header</span> <span class="s">ETag</span> <span class="s">""</span><span class="p">;</span>
<span class="p">}</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="location ~ ^/assets/ {
expires 1y;
add_header Cache-Control public;
add_header ETag "";
}
">Copy</button>
</div>
<h3 id="precompilación-local"><a class="anchorlink" href="#precompilación-local"><span>5.2</span> Precompilación Local</a></h3><p>A veces, es posible que no desees o no puedas compilar activos en el servidor de producción. Por ejemplo, es posible que tengas acceso limitado de escritura a tu sistema de archivos de producción, o que planees implementar con frecuencia sin hacer ningún cambio en tus activos.</p><p>En tales casos, puedes precompilar activos <em>localmente</em>, es decir, agregar un conjunto final de activos compilados y listos para producción a tu repositorio de código fuente antes de enviarlos a producción. De esta manera, no necesitan ser precompilados por separado en el servidor de producción en cada implementación.</p><p>Como se mencionó anteriormente, puedes realizar este paso usando</p><div class="interstitial code">
<pre><code class="highlight console"><span class="gp">$</span><span class="w"> </span><span class="nv">RAILS_ENV</span><span class="o">=</span>production <span class="nb">rails </span>assets:precompile
</code></pre>
<button class="clipboard-button" data-clipboard-text="RAILS_ENV=production rails assets:precompile
">Copy</button>
</div>
<p>Ten en cuenta las siguientes advertencias:</p>
<ul>
<li><p>Si los activos precompilados están disponibles, se servirán, incluso si ya no coinciden con los activos originales (no compilados), <em>incluso en el servidor de desarrollo.</em></p><p>Para asegurarte de que el servidor de desarrollo siempre compile activos sobre la marcha (y por lo tanto siempre refleje el estado más reciente del código), el entorno de desarrollo <em>debe configurarse para mantener los activos precompilados en una ubicación diferente a la de producción.</em> De lo contrario, cualquier activo precompilado para su uso en producción sobrescribirá las solicitudes de los mismos en desarrollo (<em>es decir</em>, los cambios que realices en los activos no se reflejarán en el navegador).</p><p>Puedes hacer esto agregando la siguiente línea a <code>config/environments/development.rb</code>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">prefix</span> <span class="o">=</span> <span class="s2">"/dev-assets"</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.prefix = "/dev-assets"
">Copy</button>
</div></li>
<li><p>La tarea de precompilación de activos en tu herramienta de implementación (<em>por ejemplo</em>, Capistrano) debe deshabilitarse.</p></li>
<li><p>Cualquier compresor o minificador necesario debe estar disponible en tu sistema de desarrollo.</p></li>
</ul>
<p>También puedes establecer <code>ENV["SECRET_KEY_BASE_DUMMY"]</code> para activar el uso de una <code>secret_key_base</code> generada aleatoriamente que se almacena en un archivo temporal. Esto es útil cuando se precompilan activos para producción como parte de un paso de construcción que de otro modo no necesitaría acceso a los secretos de producción.</p><div class="interstitial code">
<pre><code class="highlight console"><span class="gp">$</span><span class="w"> </span><span class="nv">SECRET_KEY_BASE_DUMMY</span><span class="o">=</span>1 <span class="nb">bundle exec rails </span>assets:precompile
</code></pre>
<button class="clipboard-button" data-clipboard-text="SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile
">Copy</button>
</div>
<h3 id="compilación-en-vivo"><a class="anchorlink" href="#compilación-en-vivo"><span>5.3</span> Compilación en Vivo</a></h3><p>En algunas circunstancias, es posible que desees usar la compilación en vivo. En este modo, todas las solicitudes de activos en el pipeline son manejadas directamente por Sprockets.</p><p>Para habilitar esta opción, establece:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">compile</span> <span class="o">=</span> <span class="kp">true</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.compile = true
">Copy</button>
</div>
<p>En la primera solicitud, los activos se compilan y almacenan en caché como se describe en <a href="#assets-cache-store">Almacenamiento en Caché de Activos</a>, y los nombres de manifiesto utilizados en los ayudantes se alteran para incluir el hash SHA256.</p><p>Sprockets también establece el encabezado HTTP <code>Cache-Control</code> en <code>max-age=31536000</code>. Esto señala a todos los cachés entre tu servidor y el navegador del cliente que este contenido (el archivo servido) puede almacenarse en caché durante 1 año. El efecto de esto es reducir el número de solicitudes para este activo desde tu servidor; el activo tiene una buena oportunidad de estar en el caché local del navegador o en algún caché intermedio.</p><p>Este modo utiliza más memoria, funciona peor que el predeterminado y no se recomienda.</p><h3 id="cdns"><a class="anchorlink" href="#cdns"><span>5.4</span> CDNs</a></h3><p>CDN significa <a href="https://es.wikipedia.org/wiki/Red_de_entrega_de_contenidos">Red de Entrega de Contenidos</a>, están diseñadas principalmente para almacenar en caché activos en todo el mundo para que cuando un navegador solicite el activo, haya una copia en caché geográficamente cercana a ese navegador. Si estás sirviendo activos directamente desde tu servidor Rails en producción, la mejor práctica es usar un CDN frente a tu aplicación.</p><p>Un patrón común para usar un CDN es establecer tu aplicación de producción como el servidor de "origen". Esto significa que cuando un navegador solicita un activo del CDN y hay un fallo de caché, lo obtendrá de tu servidor sobre la marcha y luego lo almacenará en caché. Por ejemplo, si estás ejecutando una aplicación Rails en <code>example.com</code> y tienes un CDN configurado en <code>mi-subdominio-cdn.fictional-cdn.com</code>, entonces cuando se realiza una solicitud a <code>mi-subdominio-cdn.fictional-cdn.com/assets/smile.png</code>, el CDN consultará tu servidor una vez en <code>example.com/assets/smile.png</code> y almacenará la solicitud en caché. La próxima solicitud al CDN que llegue a la misma URL utilizará la copia en caché. Cuando el CDN puede servir un activo directamente, la solicitud nunca toca tu servidor Rails. Dado que los activos de un CDN están geográficamente más cerca del navegador, la solicitud es más rápida, y dado que tu servidor no necesita dedicar tiempo a servir activos, puede centrarse en servir el código de la aplicación lo más rápido posible.</p><h4 id="configurar-un-cdn-para-servir-activos-estáticos"><a class="anchorlink" href="#configurar-un-cdn-para-servir-activos-estáticos"><span>5.4.1</span> Configurar un CDN para Servir Activos Estáticos</a></h4><p>Para configurar tu CDN, debes tener tu aplicación ejecutándose en producción en Internet en una URL públicamente disponible, por ejemplo, <code>example.com</code>. Luego, deberás registrarte en un servicio CDN de un proveedor de alojamiento en la nube. Cuando hagas esto, debes configurar el "origen" del CDN para que apunte de regreso a tu sitio web <code>example.com</code>. Consulta la documentación de tu proveedor para obtener información sobre cómo configurar el servidor de origen.</p><p>El CDN que aprovisionaste debería darte un subdominio personalizado para tu aplicación, como <code>mi-subdominio-cdn.fictional-cdn.com</code> (ten en cuenta que fictional-cdn.com no es un proveedor de CDN válido en el momento de escribir esto). Ahora que has configurado tu servidor CDN, necesitas decirle a los navegadores que usen tu CDN para obtener activos en lugar de tu servidor Rails directamente. Puedes hacer esto configurando Rails para establecer tu CDN como el host de activos en lugar de usar una ruta relativa. Para establecer tu host de activos en Rails, necesitas configurar <a href="configuring.html#config-asset-host"><code>config.asset_host</code></a> en <code>config/environments/production.rb</code>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">asset_host</span> <span class="o">=</span> <span class="s1">'mi-subdominio-cdn.fictional-cdn.com'</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.asset_host = 'mi-subdominio-cdn.fictional-cdn.com'
">Copy</button>
</div>
<p>NOTA: Solo necesitas proporcionar el "host", este es el subdominio y el dominio raíz, no necesitas especificar un protocolo o "esquema" como <code>http://</code> o <code>https://</code>. Cuando se solicita una página web, el protocolo en el enlace a tu activo que se genera coincidirá con cómo se accede a la página web por defecto.</p><p>También puedes establecer este valor a través de una <a href="https://es.wikipedia.org/wiki/Variable_de_entorno">variable de entorno</a> para facilitar la ejecución de una copia de ensayo de tu sitio:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">asset_host</span> <span class="o">=</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'CDN_HOST'</span><span class="p">]</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.asset_host = ENV['CDN_HOST']
">Copy</button>
</div>
<p>NOTA: Necesitarías establecer <code>CDN_HOST</code> en tu servidor a <code>mi-subdominio-cdn.fictional-cdn.com</code> para que esto funcione.</p><p>Una vez que hayas configurado tu servidor y tu CDN, las rutas de los activos desde los ayudantes como:</p><div class="interstitial code">
<pre><code class="highlight erb"><span class="cp"><%=</span> <span class="n">asset_path</span><span class="p">(</span><span class="s1">'smile.png'</span><span class="p">)</span> <span class="cp">%></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= asset_path('smile.png') %>
">Copy</button>
</div>
<p>Se representarán como URL completas de CDN como <code>http://mi-subdominio-cdn.fictional-cdn.com/assets/smile.png</code> (huella digital omitida para mayor claridad).</p><p>Si el CDN tiene una copia de <code>smile.png</code>, la servirá al navegador, y tu servidor ni siquiera sabrá que se solicitó. Si el CDN no tiene una copia, intentará encontrarla en el "origen" <code>example.com/assets/smile.png</code>, y luego la almacenará para uso futuro.</p><p>Si deseas servir solo algunos activos desde tu CDN, puedes usar la opción personalizada <code>:host</code> en tu ayudante de activos, que sobrescribe el valor establecido en <a href="configuring.html#config-action-controller-asset-host"><code>config.action_controller.asset_host</code></a>.</p><div class="interstitial code">
<pre><code class="highlight erb"><span class="cp"><%=</span> <span class="n">asset_path</span> <span class="s1">'image.png'</span><span class="p">,</span> <span class="ss">host: </span><span class="s1">'mi-subdominio-cdn.fictional-cdn.com'</span> <span class="cp">%></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<%= asset_path 'image.png', host: 'mi-subdominio-cdn.fictional-cdn.com' %>
">Copy</button>
</div>
<h4 id="personalizar-el-comportamiento-de-caché-del-cdn"><a class="anchorlink" href="#personalizar-el-comportamiento-de-caché-del-cdn"><span>5.4.2</span> Personalizar el Comportamiento de Caché del CDN</a></h4><p>Un CDN funciona almacenando contenido en caché. Si el CDN tiene contenido obsoleto o incorrecto, entonces está perjudicando en lugar de ayudar a tu aplicación. El propósito de esta sección es describir el comportamiento general de caché de la mayoría de los CDNs. Tu proveedor específico puede comportarse de manera ligeramente diferente.</p><h5 id="caché-de-solicitudes-del-cdn"><a class="anchorlink" href="#caché-de-solicitudes-del-cdn"><span>5.4.2.1</span> Caché de Solicitudes del CDN</a></h5><p>Mientras que un CDN se describe como bueno para almacenar activos en caché, en realidad almacena en caché toda la solicitud. Esto incluye el cuerpo del activo, así como cualquier encabezado. El más importante es <code>Cache-Control</code>, que le dice al CDN (y a los navegadores web) cómo almacenar en caché los contenidos. Esto significa que si alguien solicita un activo que no existe, como <code>/assets/i-dont-exist.png</code>, y tu aplicación Rails devuelve un 404, entonces tu CDN probablemente almacenará en caché la página 404 si hay un encabezado <code>Cache-Control</code> válido presente.</p><h5 id="depuración-de-encabezados-del-cdn"><a class="anchorlink" href="#depuración-de-encabezados-del-cdn"><span>5.4.2.2</span> Depuración de Encabezados del CDN</a></h5><p>Una forma de comprobar que los encabezados se almacenan correctamente en caché en tu CDN es utilizando <a href="https://explainshell.com/explain?cmd=curl+-I+http%3A%2F%2Fwww.example.com">curl</a>. Puedes solicitar los encabezados tanto de tu servidor como de tu CDN para verificar que sean los mismos:</p><div class="interstitial code">
<pre><code class="highlight console"><span class="gp">$</span><span class="w"> </span>curl <span class="nt">-I</span> http://www.example/assets/application-
<span class="go">d0e099e021c95eb0de3615fd1d8c4d83.css
HTTP/1.1 200 OK
Server: Cowboy
Date: Sun, 24 Aug 2014 20:27:50 GMT
Connection: keep-alive
Last-Modified: Thu, 08 May 2014 01:24:14 GMT
Content-Type: text/css
Cache-Control: public, max-age=2592000
Content-Length: 126560
Via: 1.1 vegur
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="curl -I http://www.example/assets/application-
">Copy</button>
</div>
<p>Versus la copia del CDN:</p><div class="interstitial code">
<pre><code class="highlight console"><span class="gp">$</span><span class="w"> </span>curl <span class="nt">-I</span> http://mi-subdominio-cdn.fictional-cdn.com/application-
<span class="go">d0e099e021c95eb0de3615fd1d8c4d83.css
HTTP/1.1 200 OK Server: Cowboy Last-
Modified: Thu, 08 May 2014 01:24:14 GMT Content-Type: text/css
Cache-Control:
public, max-age=2592000
Via: 1.1 vegur
Content-Length: 126560
Accept-Ranges:
bytes
Date: Sun, 24 Aug 2014 20:28:45 GMT
Via: 1.1 varnish
Age: 885814
Connection: keep-alive
X-Served-By: cache-dfw1828-DFW
X-Cache: HIT
X-Cache-Hits:
68
X-Timer: S1408912125.211638212,VS0,VE0
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="curl -I http://mi-subdominio-cdn.fictional-cdn.com/application-
">Copy</button>
</div>
<p>Consulta la documentación de tu CDN para obtener información adicional que puedan proporcionar, como <code>X-Cache</code> o para cualquier encabezado adicional que puedan agregar.</p><h5 id="cdns-y-el-encabezado-cache-control"><a class="anchorlink" href="#cdns-y-el-encabezado-cache-control"><span>5.4.2.3</span> CDNs y el Encabezado Cache-Control</a></h5><p>El encabezado <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control"><code>Cache-Control</code></a> describe cómo se puede almacenar en caché una solicitud. Cuando no se utiliza un CDN, un navegador utilizará esta información para almacenar contenidos en caché. Esto es muy útil para los activos que no se modifican, de modo que un navegador no necesita volver a descargar el CSS o JavaScript de un sitio web en cada solicitud. Generalmente queremos que nuestro servidor Rails le diga a nuestro CDN (y al navegador) que el activo es "public". Eso significa que cualquier caché puede almacenar la solicitud. También queremos comúnmente establecer <code>max-age</code>, que es cuánto tiempo el caché almacenará el objeto antes de invalidar el caché. El valor de <code>max-age</code> se establece en segundos con un valor máximo posible de <code>31536000</code>, que es un año. Puedes hacer esto en tu aplicación Rails configurando</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">public_file_server</span><span class="p">.</span><span class="nf">headers</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'Cache-Control'</span> <span class="o">=></span> <span class="s1">'public, max-age=31536000'</span>
<span class="p">}</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=31536000'
}
">Copy</button>
</div>
<p>Ahora, cuando tu aplicación sirva un activo en producción, el CDN almacenará el activo hasta por un año. Dado que la mayoría de los CDNs también almacenan en caché los encabezados de la solicitud, este <code>Cache-Control</code> se pasará a todos los navegadores futuros que busquen este activo. El navegador luego sabe que puede almacenar este activo por un tiempo muy largo antes de necesitar volver a solicitarlo.</p><h5 id="cdns-e-invalidación-de-caché-basada-en-url"><a class="anchorlink" href="#cdns-e-invalidación-de-caché-basada-en-url"><span>5.4.2.4</span> CDNs e Invalidación de Caché Basada en URL</a></h5><p>La mayoría de los CDNs almacenarán en caché el contenido de un activo basado en la URL completa. Esto significa que una solicitud a</p><div class="interstitial code">
<pre><code class="highlight plaintext">http://mi-subdominio-cdn.fictional-cdn.com/assets/smile-123.png
</code></pre>
<button class="clipboard-button" data-clipboard-text="http://mi-subdominio-cdn.fictional-cdn.com/assets/smile-123.png
">Copy</button>
</div>
<p>Será un caché completamente diferente de</p><div class="interstitial code">
<pre><code class="highlight plaintext">http://mi-subdominio-cdn.fictional-cdn.com/assets/smile.png
</code></pre>
<button class="clipboard-button" data-clipboard-text="http://mi-subdominio-cdn.fictional-cdn.com/assets/smile.png
">Copy</button>
</div>
<p>Si deseas establecer un <code>max-age</code> de futuro lejano en tu <code>Cache-Control</code> (y lo deseas), entonces asegúrate de que cuando cambies tus activos, tu caché se invalide. Por ejemplo, al cambiar la cara sonriente en una imagen de amarilla a azul, deseas que todos los visitantes de tu sitio obtengan la nueva cara azul. Cuando usas un CDN con el pipeline de activos de Rails, <code>config.assets.digest</code> está configurado en true por defecto para que cada activo tenga un nombre de archivo diferente cuando se cambia. De esta manera, nunca tienes que invalidar manualmente ningún elemento en tu caché. Al usar un nombre de activo único diferente, tus usuarios obtienen el último activo.</p><h2 id="personalizando-el-pipeline"><a class="anchorlink" href="#personalizando-el-pipeline"><span>6</span> Personalizando el Pipeline</a></h2><h3 id="compresión-de-css"><a class="anchorlink" href="#compresión-de-css"><span>6.1</span> Compresión de CSS</a></h3><p>Una de las opciones para comprimir CSS es YUI. El <a href="https://yui.github.io/yuicompressor/css.html">compresor de CSS de YUI</a> proporciona minificación.</p><p>La siguiente línea habilita la compresión de YUI y requiere la gema <code>yui-compressor</code>.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">css_compressor</span> <span class="o">=</span> <span class="ss">:yui</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.css_compressor = :yui
">Copy</button>
</div>
<h3 id="compresión-de-javascript"><a class="anchorlink" href="#compresión-de-javascript"><span>6.2</span> Compresión de JavaScript</a></h3><p>Las opciones posibles para la compresión de JavaScript son <code>:terser</code>, <code>:closure</code> y <code>:yui</code>. Estas requieren el uso de las gemas <code>terser</code>, <code>closure-compiler</code> o <code>yui-compressor</code>, respectivamente.</p><p>Toma la gema <code>terser</code>, por ejemplo. Esta gema envuelve <a href="https://github.com/terser/terser">Terser</a> (escrito para Node.js) en Ruby. Comprime tu código eliminando espacios en blanco y comentarios, acortando nombres de variables locales y realizando otras micro-optimizaciones como cambiar declaraciones <code>if</code> y <code>else</code> a operadores ternarios donde sea posible.</p><p>La siguiente línea invoca <code>terser</code> para la compresión de JavaScript.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">js_compressor</span> <span class="o">=</span> <span class="ss">:terser</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.js_compressor = :terser
">Copy</button>
</div>
<p>NOTA: Necesitarás un runtime compatible con <a href="https://github.com/rails/execjs#readme">ExecJS</a> para usar <code>terser</code>. Si estás usando macOS o Windows, tienes un runtime JavaScript instalado en tu sistema operativo.</p><p>NOTA: La compresión de JavaScript también funcionará para tus archivos JavaScript cuando estés cargando tus activos a través de las gemas <code>importmap-rails</code> o <code>jsbundling-rails</code>.</p><h3 id="comprimir-tus-activos-con-gzip"><a class="anchorlink" href="#comprimir-tus-activos-con-gzip"><span>6.3</span> Comprimir tus activos con GZIP</a></h3><p>Por defecto, se generará una versión comprimida con GZIP de los activos compilados, junto con la versión no comprimida de los activos. Los activos comprimidos con GZIP ayudan a reducir la transmisión de datos por la red. Puedes configurar esto estableciendo el indicador <code>gzip</code>.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">gzip</span> <span class="o">=</span> <span class="kp">false</span> <span class="c1"># desactivar la generación de activos comprimidos con GZIP</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.gzip = false # desactivar la generación de activos comprimidos con GZIP
">Copy</button>
</div>
<p>Consulta la documentación de tu servidor web para obtener instrucciones sobre cómo servir activos comprimidos con GZIP.</p><h3 id="usar-tu-propio-compresor"><a class="anchorlink" href="#usar-tu-propio-compresor"><span>6.4</span> Usar tu Propio Compresor</a></h3><p>Las configuraciones de compresor para CSS y JavaScript también aceptan cualquier objeto. Este objeto debe tener un método <code>compress</code> que tome una cadena como único argumento y debe devolver una cadena.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">Transformer</span>
<span class="k">def</span> <span class="nf">compress</span><span class="p">(</span><span class="n">string</span><span class="p">)</span>
<span class="n">do_something_returning_a_string</span><span class="p">(</span><span class="n">string</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class Transformer
def compress(string)
do_something_returning_a_string(string)
end
end
">Copy</button>
</div>
<p>Para habilitar esto, pasa un nuevo objeto a la opción de configuración en <code>application.rb</code>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">css_compressor</span> <span class="o">=</span> <span class="no">Transformer</span><span class="p">.</span><span class="nf">new</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.css_compressor = Transformer.new
">Copy</button>
</div>
<h3 id="cambiar-la-ruta-de-assets"><a class="anchorlink" href="#cambiar-la-ruta-de-assets"><span>6.5</span> Cambiar la Ruta de <em>assets</em></a></h3><p>La ruta pública que Sprockets utiliza por defecto es <code>/assets</code>.</p><p>Esto puede cambiarse a otra cosa:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">prefix</span> <span class="o">=</span> <span class="s2">"/some_other_path"</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.prefix = "/some_other_path"
">Copy</button>
</div>
<p>Esta es una opción útil si estás actualizando un proyecto antiguo que no usaba el pipeline de activos y ya utiliza esta ruta o deseas usar esta ruta para un nuevo recurso.</p><h3 id="encabezados-x-sendfile"><a class="anchorlink" href="#encabezados-x-sendfile"><span>6.6</span> Encabezados X-Sendfile</a></h3><p>El encabezado X-Sendfile es una directiva para el servidor web para ignorar la respuesta de la aplicación y en su lugar servir un archivo específico desde el disco. Esta opción está desactivada por defecto, pero puede habilitarse si tu servidor la admite. Cuando está habilitada, esto pasa la responsabilidad de servir el archivo al servidor web, lo cual es más rápido. Echa un vistazo a <a href="https://edgeapi.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_file">send_file</a> para saber cómo usar esta función.</p><p>Apache y NGINX admiten esta opción, que puede habilitarse en <code>config/environments/production.rb</code>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="c1"># config.action_dispatch.x_sendfile_header = "X-Sendfile" # para Apache</span>
<span class="c1"># config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # para NGINX</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="# config.action_dispatch.x_sendfile_header = "X-Sendfile" # para Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # para NGINX
">Copy</button>
</div>
<p>ADVERTENCIA: Si estás actualizando una aplicación existente y planeas usar esta opción, ten cuidado de pegar esta opción de configuración solo en <code>production.rb</code> y en cualquier otro entorno que definas con comportamiento de producción (no en <code>application.rb</code>).</p><p>CONSEJO: Para obtener más detalles, consulta la documentación de tu servidor web de producción:</p>
<ul>
<li><a href="https://tn123.org/mod_xsendfile/">Apache</a></li>
<li><a href="https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/">NGINX</a></li>
</ul>
<h2 id="almacenamiento-en-caché-de-activos"><a class="anchorlink" href="#almacenamiento-en-caché-de-activos"><span>7</span> Almacenamiento en Caché de Activos</a></h2><p>Por defecto, Sprockets almacena en caché los activos en <code>tmp/cache/assets</code> en los entornos de desarrollo y producción. Esto puede cambiarse de la siguiente manera:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">env</span><span class="o">|</span>
<span class="n">env</span><span class="p">.</span><span class="nf">cache</span> <span class="o">=</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Cache</span><span class="p">.</span><span class="nf">lookup_store</span><span class="p">(</span><span class="ss">:memory_store</span><span class="p">,</span>
<span class="p">{</span> <span class="ss">size: </span><span class="mi">32</span><span class="p">.</span><span class="nf">megabytes</span> <span class="p">})</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.configure do |env|
env.cache = ActiveSupport::Cache.lookup_store(:memory_store,
{ size: 32.megabytes })
end
">Copy</button>
</div>
<p>Para desactivar el almacenamiento en caché de activos:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">env</span><span class="o">|</span>
<span class="n">env</span><span class="p">.</span><span class="nf">cache</span> <span class="o">=</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Cache</span><span class="p">.</span><span class="nf">lookup_store</span><span class="p">(</span><span class="ss">:null_store</span><span class="p">)</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.assets.configure do |env|
env.cache = ActiveSupport::Cache.lookup_store(:null_store)
end
">Copy</button>
</div>
<h2 id="agregar-activos-a-tus-gemas"><a class="anchorlink" href="#agregar-activos-a-tus-gemas"><span>8</span> Agregar Activos a tus Gemas</a></h2><p>Los activos también pueden provenir de fuentes externas en forma de gemas.</p><p>Un buen ejemplo de esto es la gema <code>jquery-rails</code>. Esta gema contiene una clase de motor que hereda de <code>Rails::Engine</code>. Al hacer esto, Rails es informado de que el directorio para esta gema puede contener activos y los directorios <code>app/assets</code>, <code>lib/assets</code> y <code>vendor/assets</code> de este motor se agregan a la ruta de búsqueda de Sprockets.</p><h2 id="hacer-que-tu-biblioteca-o-gema-sea-un-preprocesador"><a class="anchorlink" href="#hacer-que-tu-biblioteca-o-gema-sea-un-preprocesador"><span>9</span> Hacer que tu Biblioteca o Gema sea un Preprocesador</a></h2><p>Sprockets utiliza Procesadores, Transformadores, Compresores y Exportadores para extender la funcionalidad de Sprockets. Echa un vistazo a <a href="https://github.com/rails/sprockets/blob/main/guides/extending_sprockets.md">Extending Sprockets</a> para aprender más. Aquí registramos un preprocesador para agregar un comentario al final de los archivos text/css (<code>.css</code>).</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">module</span> <span class="nn">AddComment</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">call</span><span class="p">(</span><span class="n">input</span><span class="p">)</span>
<span class="p">{</span> <span class="ss">data: </span><span class="n">input</span><span class="p">[</span><span class="ss">:data</span><span class="p">]</span> <span class="o">+</span> <span class="s2">"/* Hola desde mi extensión de sprockets */"</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="module AddComment
def self.call(input)
{ data: input[:data] + "/* Hola desde mi extensión de sprockets */" }
end
end
">Copy</button>
</div>
<p>Ahora que tienes un módulo que modifica los datos de entrada, es hora de registrarlo como un preprocesador para tu tipo MIME.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="no">Sprockets</span><span class="p">.</span><span class="nf">register_preprocessor</span> <span class="s1">'text/css'</span><span class="p">,</span> <span class="no">AddComment</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="Sprockets.register_preprocessor 'text/css', AddComment
">Copy</button>
</div>
<h2 id="bibliotecas-alternativas"><a class="anchorlink" href="#bibliotecas-alternativas"><span>10</span> Bibliotecas Alternativas</a></h2><p>A lo largo de los años, ha habido múltiples enfoques predeterminados para manejar los activos. La web evolucionó y comenzamos a ver más y más aplicaciones pesadas en JavaScript. En The Rails Doctrine creemos que <a href="https://rubyonrails.org/doctrine#omakase">The Menu Is Omakase</a>, por lo que nos centramos en la configuración predeterminada: <strong>Sprockets con Import Maps</strong>.</p><p>Somos conscientes de que no hay soluciones de talla única para todos los diversos frameworks/extensiones de JavaScript y CSS disponibles. Hay otras bibliotecas de empaquetado en el ecosistema de Rails que deberían empoderarte en los casos donde la configuración predeterminada no es suficiente.</p><h3 id="jsbundling-rails"><a class="anchorlink" href="#jsbundling-rails"><span>10.1</span> jsbundling-rails</a></h3><p><a href="https://github.com/rails/jsbundling-rails"><code>jsbundling-rails</code></a> es una alternativa dependiente del tiempo de ejecución de JavaScript a la forma de <code>importmap-rails</code> de empaquetar JS con <a href="https://bun.sh">Bun</a>, <a href="https://esbuild.github.io/">esbuild</a>, <a href="https://rollupjs.org/">rollup.js</a> o <a href="https://webpack.js.org/">Webpack</a>.</p><p>La gema proporciona una tarea de construcción en <code>package.json</code> para observar cambios y generar automáticamente la salida en desarrollo. Para producción, engancha automáticamente la tarea <code>javascript:build</code> en la tarea <code>assets:precompile</code> para asegurarse de que todas tus dependencias de paquetes se hayan instalado y que el JavaScript se haya construido para todos los puntos de entrada.</p><p><strong>¿Cuándo usar en lugar de <code>importmap-rails</code>?</strong> Si tu código JavaScript depende de la transpilation, por lo que si estás usando <a href="https://babeljs.io/">Babel</a>, <a href="https://www.typescriptlang.org/">TypeScript</a> o el formato React JSX, entonces <code>jsbundling-rails</code> es la forma correcta de proceder.</p><h3 id="webpacker-shakapacker"><a class="anchorlink" href="#webpacker-shakapacker"><span>10.2</span> Webpacker/Shakapacker</a></h3><p><a href="https://github.com/rails/webpacker"><code>Webpacker</code></a> fue el preprocesador y empaquetador de JavaScript predeterminado para Rails 5 y 6. Ahora ha sido retirado. Un sucesor llamado <a href="https://github.com/shakacode/shakapacker"><code>shakapacker</code></a> existe, pero no es mantenido por el equipo o proyecto de Rails.</p><p>A diferencia de otras bibliotecas en esta lista, <code>webpacker</code>/<code>shakapacker</code> es completamente independiente de Sprockets y podría procesar tanto archivos JavaScript como CSS.</p><p>NOTA: Lee el documento <a href="https://github.com/rails/jsbundling-rails/blob/main/docs/comparison_with_webpacker.md">Comparison with Webpacker</a> para entender las diferencias entre <code>jsbundling-rails</code> y <code>webpacker</code>/<code>shakapacker</code>.</p><h3 id="cssbundling-rails"><a class="anchorlink" href="#cssbundling-rails"><span>10.3</span> cssbundling-rails</a></h3><p><a href="https://github.com/rails/cssbundling-rails"><code>cssbundling-rails</code></a> permite empaquetar y procesar tu CSS usando <a href="https://tailwindcss.com/">Tailwind CSS</a>, <a href="https://getbootstrap.com/">Bootstrap</a>, <a href="https://bulma.io/">Bulma</a>, <a href="https://postcss.org/">PostCSS</a> o <a href="https://sass-lang.com/">Dart Sass</a>, y luego entrega el CSS a través del pipeline de activos.</p><p>Funciona de manera similar a <code>jsbundling-rails</code>, por lo que agrega la dependencia de Node.js a tu aplicación con el proceso <code>yarn build:css --watch</code> para regenerar tus hojas de estilo en desarrollo y engancha en la tarea <code>assets:precompile</code> en producción.</p><p><strong>¿Cuál es la diferencia con Sprockets?</strong> Sprockets por sí solo no puede transpilar el Sass en CSS, se requiere Node.js para generar los archivos <code>.css</code> a partir de tus archivos <code>.sass</code>. Una vez que los archivos <code>.css</code> se generan, entonces Sprockets puede entregarlos a tus clientes.</p><p>NOTA: <code>cssbundling-rails</code> depende de Node para procesar el CSS. Las gemas <code>dartsass-rails</code> y <code>tailwindcss-rails</code> usan versiones independientes de Tailwind CSS y Dart Sass, lo que significa que no hay dependencia de Node. Si estás usando <code>importmap-rails</code> para manejar tus JavaScripts y <code>dartsass-rails</code> o <code>tailwindcss-rails</code> para CSS, podrías evitar completamente la dependencia de Node, lo que resulta en una solución menos compleja.</p><h3 id="dartsass-rails"><a class="anchorlink" href="#dartsass-rails"><span>10.4</span> dartsass-rails</a></h3><p>Si deseas usar <a href="https://sass-lang.com/">Sass</a> en tu aplicación, <a href="https://github.com/rails/dartsass-rails"><code>dartsass-rails</code></a> viene como un reemplazo para la gema <code>sassc-rails</code> heredada. <code>dartsass-rails</code> utiliza la implementación de Dart Sass en favor de <a href="https://sass-lang.com/blog/libsass-is-deprecated">LibSass</a> que fue deprecada en 2020 y utilizada por <code>sassc-rails</code>.</p><p>A diferencia de <code>sassc-rails</code>, la nueva gema no está directamente integrada con Sprockets. Consulta la <a href="https://github.com/rails/dartsass-rails">página de inicio de la gema</a> para obtener instrucciones de instalación/migración.</p><p>ADVERTENCIA: La popular gema <code>sassc-rails</code> no se mantiene desde 2019.</p><h3 id="tailwindcss-rails"><a class="anchorlink" href="#tailwindcss-rails"><span>10.5</span> tailwindcss-rails</a></h3><p><a href="https://github.com/rails/tailwindcss-rails"><code>tailwindcss-rails</code></a> es una gema envolvente para <a href="https://tailwindcss.com/blog/standalone-cli">la versión ejecutable independiente</a> del framework Tailwind CSS v3. Se utiliza para nuevas aplicaciones cuando se proporciona <code>--css tailwind</code> al comando <code>rails new</code>. Proporciona un proceso <code>watch</code> para generar automáticamente la salida de Tailwind en desarrollo. Para producción, se engancha en la tarea <code>assets:precompile</code>.</p>
<hr>
<h3>Comentarios</h3>
<p>
Se te anima a ayudar a mejorar la calidad de esta guía.
</p>
<p>
Por favor contribuye si ves algún error tipográfico o errores fácticos.
Para comenzar, puedes leer nuestra sección de <a href="https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation">contribuciones a la documentación</a>.
</p>
<p>
También puedes encontrar contenido incompleto o cosas que no están actualizadas.
Por favor agrega cualquier documentación faltante para main. Asegúrate de revisar
<a href="https://edgeguides.rubyonrails.org">Guías Edge</a> primero para verificar
si los problemas ya están resueltos o no en la rama principal.
Revisa las <a href="ruby_on_rails_guides_guidelines.html">Guías de Ruby on Rails</a>
para estilo y convenciones.
</p>
<p>
Si por alguna razón detectas algo que corregir pero no puedes hacerlo tú mismo, por favor
<a href="https://github.com/rails/rails/issues">abre un issue</a>.
</p>
<p>Y por último, pero no menos importante, cualquier tipo de discusión sobre la
documentación de Ruby on Rails es muy bienvenida en el <a href="https://discuss.rubyonrails.org/c/rubyonrails-docs">Foro oficial de Ruby on Rails</a>.
</p>
</div>
</div>
</main>
<hr class="hide" />
<footer id="page_footer">
<div class="wrapper">
<p>Este trabajo está bajo una <a href="https://creativecommons.org/licenses/by-sa/4.0/">Licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional</a></p>
<p>"Rails", "Ruby on Rails" y el logotipo de Rails son marcas registradas de David Heinemeier Hansson. Todos los derechos reservados.</p>
<p> Esta traducción fue generada por openAi e <a href="http://latinadeveloper.com/">Isis Harris.</a></p>
</div>
</footer>
</body>
</html>