Por qué algunos módulos cargan CSS y JS en todas las páginas

387 vistas

La causa raíz: cómo el sistema de hooks de PrestaShop gestiona los recursos

Si alguna vez has inspeccionado el código fuente de tu tienda PrestaShop y te has preguntado por qué un módulo que solo muestra un widget en las páginas de producto está cargando su CSS y JavaScript en tu página de inicio, tus páginas de categoría e incluso tu checkout, no estás solo. Este es uno de los problemas de rendimiento más comunes en el ecosistema PrestaShop, y se origina en cómo funciona el sistema de hooks combinado con prácticas de desarrollo descuidadas.

PrestaShop utiliza una arquitectura basada en hooks para permitir que los módulos inyecten contenido, recursos y lógica en diferentes partes de una página. Cuando un módulo necesita añadir archivos CSS o JavaScript a la página, típicamente lo hace a través de uno de dos hooks: displayHeader o actionFrontControllerSetMedia. Ambos hooks se ejecutan en todas las páginas del front office sin excepción. No existe un mecanismo integrado en el propio sistema de hooks para restringir una llamada de hook a tipos de página específicos. Es enteramente responsabilidad del desarrollador del módulo verificar qué página se está cargando y decidir si añadir recursos o no.

El problema es que muchos desarrolladores de módulos toman atajos. En lugar de escribir lógica condicional que verifique el controlador actual, simplemente registran sus recursos en cada llamada de hook. El razonamiento es sencillo: si los recursos se cargan siempre, el módulo funcionará siempre sin importar dónde aparezca su contenido. Este enfoque de "disparar y olvidar" garantiza que el módulo funcione correctamente, pero también garantiza un rendimiento deficiente.

Cómo el abuso del hook displayHeader crea sobrecarga en las páginas

El hook displayHeader es el mecanismo principal por el cual los módulos añaden contenido a la sección head del HTML de cada página. Cuando un módulo implementa el método hookDisplayHeader, PrestaShop llama a ese método en cada carga de página del front office. Dentro de este método, los módulos típicamente llaman a $this->context->controller->addCSS() y $this->context->controller->addJS() para registrar sus archivos de recursos.

Esto es lo que sucede en una tienda típica con 25 módulos instalados. Quince de esos módulos tienen un método hookDisplayHeader. Cada uno añade entre uno y cuatro archivos CSS y JavaScript. Eso significa que cada página de tu tienda carga de 15 a 60 peticiones HTTP adicionales solo de módulos, sin importar si esos módulos muestran algo en la página actual.

Consideremos un ejemplo concreto. Un módulo que añade un popup de guía de tallas a las páginas de producto necesita un archivo CSS para el estilo del popup y un archivo JavaScript para el comportamiento del popup. Si el desarrollador registra estos recursos en hookDisplayHeader sin ninguna verificación condicional, esos dos archivos se cargan en la página de inicio, en cada página de categoría, en la página del carrito, en la página de checkout y en cada página CMS. La guía de tallas nunca aparecerá en ninguna de esas páginas, pero el navegador igualmente descarga, analiza y procesa los archivos CSS y JavaScript.

Multiplica esto por cada módulo que sigue el mismo patrón, y empezarás a entender por qué algunas tiendas PrestaShop cargan 2 MB o más de recursos de módulos en páginas donde la mayoría de esos recursos son completamente innecesarios.

El hook actionFrontControllerSetMedia

PrestaShop 1.7 introdujo el hook actionFrontControllerSetMedia como una alternativa más moderna a displayHeader para el registro de recursos. Este hook fue diseñado específicamente para registrar archivos CSS y JavaScript usando los nuevos métodos registerStylesheet y registerJavascript. Estos métodos ofrecen ventajas sobre las funciones antiguas addCSS y addJS, incluyendo la capacidad de establecer prioridad de carga, especificar atributos async o defer y controlar la posición (head o bottom) de las etiquetas de los archivos.

Sin embargo, actionFrontControllerSetMedia sufre exactamente el mismo problema fundamental que displayHeader: se ejecuta en todas las páginas. Un módulo que registra recursos en hookActionFrontControllerSetMedia sin verificar el controlador actual carga esos recursos en todas partes, igual que el enfoque antiguo.

La única diferencia es el método de registro, no el alcance de la carga. Ya sea que un desarrollador use addCSS en displayHeader o registerStylesheet en actionFrontControllerSetMedia, el resultado es el mismo si no añade lógica condicional. Los recursos se cargan globalmente.

Carga condicional adecuada: lo que hacen los buenos módulos

Un módulo bien desarrollado verifica qué página está viendo el cliente antes de cargar cualquier recurso. La forma estándar de hacerlo en PrestaShop es verificar el nombre del controlador. Cada página del front office es servida por un controlador específico: IndexController para la página de inicio, CategoryController para las páginas de categoría, ProductController para las páginas de producto, CartController para el carrito, y así sucesivamente.

Dentro del método hookDisplayHeader o hookActionFrontControllerSetMedia, el desarrollador puede acceder al controlador actual a través de $this->context->controller. Verificando el nombre de la clase o la propiedad php_self del controlador, el módulo puede decidir si cargar sus recursos. Un módulo de reseñas de productos, por ejemplo, debería verificar si la página actual es una página de producto y solo cargar su CSS y JavaScript en ese caso.

Este enfoque condicional no es difícil de implementar. Añade quizás de cinco a diez líneas de código al método del hook. Sin embargo, un número sorprendente de módulos, incluyendo módulos de pago populares en el marketplace de PrestaShop Addons y módulos gratuitos muy conocidos, omiten esta verificación por completo. La razón es una combinación de comodidad del desarrollador y el hecho de que PrestaShop no exige ni siquiera promueve la carga condicional en su documentación de desarrollo de módulos.

Algunos desarrolladores argumentan que cargar recursos globalmente asegura la compatibilidad con temas personalizados o configuraciones de página inusuales donde el contenido del módulo podría aparecer inesperadamente. Aunque este argumento tiene algo de verdad, no justifica el coste de rendimiento. Un mejor enfoque es cargar recursos condicionalmente por defecto y proporcionar una opción de configuración para habilitar la carga global en las tiendas que lo necesiten.

El método setMedia: mejores prácticas para desarrolladores de módulos

Para los desarrolladores de módulos que lean este artículo, estas son las mejores prácticas para la carga de recursos que respetan el rendimiento del sitio de sus usuarios.

Primero, usa siempre el hook actionFrontControllerSetMedia en lugar de displayHeader para el registro de recursos en PrestaShop 1.7 y posteriores. El hook más nuevo proporciona mejor control sobre el comportamiento de carga de recursos y mantiene tu registro de recursos separado de la salida HTML.

Segundo, verifica siempre el controlador actual antes de registrar recursos. Usa un enfoque de lista blanca: define la lista de controladores donde tu módulo muestra contenido y solo registra recursos cuando el controlador actual está en esa lista. Esto es más mantenible que un enfoque de lista negra porque los nuevos tipos de página añadidos en futuras versiones de PrestaShop no cargarán accidentalmente tus recursos.

Tercero, usa registerStylesheet y registerJavascript en lugar de addCSS y addJS. Los métodos más nuevos te permiten especificar un id para cada recurso, lo que hace posible que los temas y otros módulos anulen o eliminen tus recursos de forma limpia. También soportan ajustes de prioridad que controlan el orden en que se cargan los recursos.

Cuarto, considera si tu JavaScript realmente necesita cargarse en el head o si puede cargarse en la parte inferior de la página con el atributo defer. El JavaScript en el head bloquea el renderizado, lo que significa que el navegador no puede mostrar ningún contenido hasta que tu archivo termine de descargarse y analizarse. Mover los archivos a la parte inferior o añadir defer elimina este comportamiento de bloqueo del renderizado.

Quinto, minimiza el tamaño de tus archivos de recursos. Minifica tus archivos CSS y JavaScript antes de distribuir tu módulo. Elimina las reglas CSS no utilizadas. Evita incluir bibliotecas completas como jQuery UI o Bootstrap cuando solo usas una pequeña parte de su funcionalidad. Cada kilobyte cuenta cuando los recursos de tu módulo se cargan en miles de visitas de página al día.

Midiendo el impacto en el rendimiento de los recursos globales

Para entender cuánto cuesta a tu tienda la carga global de recursos, necesitas mediciones concretas. Abre Chrome DevTools en tu página de inicio y ve a la pestaña Red (Network). Recarga la página y filtra las peticiones por la ruta /modules/. Cuenta el número total de peticiones y su tamaño combinado. Esta es la sobrecarga de recursos de módulos en una página donde la mayoría de los módulos no muestran ningún contenido.

Ahora haz lo mismo en una página de producto, donde muchos módulos legítimamente necesitan sus recursos. Compara los números. En una tienda bien optimizada, la página de producto debería cargar significativamente más recursos de módulos que la página de inicio porque es donde la mayoría de los módulos muestran su contenido. En una tienda mal optimizada, los números son casi idénticos porque cada módulo carga todo en todas partes.

Un dato concreto de auditorías reales: una tienda con 35 módulos instalados cargaba 1,8 MB de CSS y JavaScript de módulos en la página de inicio. Después de implementar carga condicional en todos los módulos, los recursos de módulos en la página de inicio cayeron a 340 KB. La página de producto, donde la mayoría de los módulos legítimamente necesitaban sus recursos, pasó de 2,1 MB a 1,4 MB. El tiempo de carga de la página de inicio mejoró en 1,3 segundos y la puntuación de rendimiento de Lighthouse pasó de 42 a 71.

Estos números no son inusuales. La carga global de recursos es una de las mayores fuentes individuales de peso innecesario de página en las tiendas PrestaShop, y corregirla suele producir las mejoras de rendimiento más dramáticas.

Cómo identificar los módulos infractores en tu tienda

Encontrar qué módulos cargan recursos globalmente requiere un enfoque sistemático. Comienza con la pestaña Red en DevTools como se describió anteriormente. Para cada recurso de módulo que encuentres cargándose en la página de inicio, pregúntate: ¿este módulo muestra algún contenido en la página de inicio? Si la respuesta es no, ese módulo está cargando recursos innecesariamente.

Otro enfoque es usar el modo de perfilado de depuración de PrestaShop. Cuando el perfilado está activado (estableciendo _PS_DEBUG_PROFILING_ a true en tu config/defines.inc.php), PrestaShop muestra datos detallados de ejecución de hooks en la parte inferior de cada página. Estos datos te muestran exactamente qué módulos se ejecutan en cada hook y cuánto tardan. Cualquier módulo que se ejecuta en displayHeader o actionFrontControllerSetMedia pero no se ejecuta en ningún hook de visualización que produzca salida visible en la página actual es un candidato para optimización.

También puedes verificar programáticamente examinando el código fuente de cada módulo. Mira los métodos hookDisplayHeader y hookActionFrontControllerSetMedia en el archivo PHP principal del módulo. Si el método contiene llamadas a addCSS, addJS, registerStylesheet o registerJavascript sin ninguna verificación condicional del nombre del controlador, ese módulo carga recursos globalmente.

Para tiendas con muchos módulos, esta revisión manual puede ser lenta. Un atajo práctico es centrarse primero en los recursos más grandes. Ordena los recursos de módulos en la pestaña Red por tamaño y empieza investigando los archivos más grandes. Un archivo JavaScript de 200 KB cargándose globalmente es un problema mucho mayor que un archivo CSS de 3 KB, así que prioriza en consecuencia.

Solicitar correcciones a los desarrolladores de módulos

Cuando identifiques un módulo que carga recursos globalmente, tu primer paso debería ser contactar al desarrollador y solicitar una corrección. La mayoría de los desarrolladores profesionales de módulos son receptivos a las solicitudes de mejora de rendimiento, especialmente cuando puedes proporcionar datos específicos sobre el impacto.

Escribe un mensaje claro y técnico que explique el problema. Menciona que el método hookDisplayHeader del módulo carga CSS y JavaScript en todas las páginas sin verificar el controlador actual. Especifica qué páginas realmente necesitan los recursos y cuáles los están cargando innecesariamente. Incluye los tamaños de archivo y el impacto estimado en el rendimiento. Si tienes puntuaciones de Lighthouse mostrando el antes y el después cuando los recursos del módulo están bloqueados, inclúyelas.

Si el desarrollador es receptivo, típicamente publicará una actualización en pocas semanas que añada carga condicional. Si el desarrollador no responde o desestima la preocupación, tienes varias opciones: implementar la carga condicional tú mismo editando el código del módulo, usar un módulo de rendimiento que pueda bloquear selectivamente recursos en páginas específicas, o encontrar un módulo alternativo que siga mejores prácticas de desarrollo.

Soluciones alternativas cuando no puedes modificar el módulo

A veces no puedes modificar un módulo directamente. Puede estar cifrado con IonCube, puede ser un módulo del que no quieres mantener un fork, o el módulo podría sobrescribir tus cambios en cada actualización. En estos casos, necesitas soluciones alternativas.

Una solución alternativa efectiva es crear un pequeño módulo personalizado que desvinculle el módulo infractor de displayHeader y actionFrontControllerSetMedia, y luego vuelva a añadir los recursos solo en las páginas donde se necesitan. Este módulo personalizado usaría el hook actionFrontControllerSetMedia con una prioridad alta (para que se ejecute después del módulo infractor) y llamaría a Media::unregisterStylesheet y Media::unregisterJavascript para eliminar los recursos cargados globalmente. Luego, en las páginas donde los recursos son realmente necesarios, los volvería a registrar.

Otra solución alternativa es usar el archivo Theme.yml para anular los recursos de módulos. En PrestaShop 1.7 y posteriores, el archivo de configuración theme.yml del tema puede eliminar recursos específicos de módulos de la carga. Esta es una solución a nivel de tema que persiste a través de las actualizaciones de módulos. Sin embargo, elimina los recursos de todas las páginas, no selectivamente, por lo que necesitarías combinarlo con la re-adición de los recursos en páginas específicas mediante un módulo personalizado o una plantilla del tema.

Una tercera opción, disponible en PrestaShop 8.x, es usar las funciones integradas de prioridad y eliminación del sistema de gestión de recursos. PrestaShop 8 mejoró el pipeline de recursos para dar a los temas más control sobre qué recursos de módulos se cargan. Consulta la documentación de tu tema para instrucciones específicas sobre cómo aprovechar estas funcionalidades.

Independientemente del enfoque que elijas, siempre prueba exhaustivamente después de hacer cambios. Eliminar el CSS de un módulo puede romper su apariencia visual, y eliminar su JavaScript puede romper funciones interactivas. Prueba cada tipo de página donde el módulo debería seguir funcionando correctamente.

Prevención: evaluar los módulos antes de la instalación

La mejor forma de evitar los problemas de carga global de recursos es evaluar los módulos antes de instalarlos. Aunque esto no siempre es posible, hay verificaciones que puedes realizar.

Si el módulo proporciona una tienda de demostración, visítala e inspecciona la página de inicio con DevTools. Comprueba si los recursos del módulo se cargan en páginas donde el módulo no muestra contenido. Si los recursos se cargan globalmente en la demo, se cargarán globalmente en tu tienda también.

Si tienes acceso al código fuente del módulo antes de comprarlo (algunos marketplaces ofrecen previsualizaciones de código), mira los métodos hookDisplayHeader y hookActionFrontControllerSetMedia. Busca lógica de carga condicional. La ausencia de cualquier verificación de controlador es una señal de alarma.

Lee las reseñas y el foro de soporte del módulo. Los usuarios preocupados por el rendimiento a menudo informan sobre problemas de carga global de recursos en las reseñas. Si múltiples usuarios se han quejado de que el módulo ralentiza su tienda, toma esos comentarios en serio.

Finalmente, considera la calidad general del código del desarrollador. Los desarrolladores que siguen las mejores prácticas en un área tienden a seguirlas en otras. Si el código de un módulo es limpio, está bien documentado y sigue los estándares de codificación de PrestaShop, es más probable que gestione la carga de recursos correctamente. Si el código es desordenado y está mal estructurado, la carga global de recursos probablemente es solo uno de muchos problemas.

¿Le resultó útil esta respuesta?

¿Aún tiene preguntas?

Can't find what you're looking for? Send us your question and we'll get back to you quickly.

Cargando...
Volver arriba