Perché alcuni moduli caricano CSS e JS su ogni pagina

384 visualizzazioni

La causa principale: come il sistema di hook di PrestaShop gestisce le risorse

Se hai mai ispezionato il codice sorgente del tuo negozio PrestaShop e ti sei chiesto perché un modulo che mostra un widget solo sulle pagine prodotto sta caricando i suoi CSS e JavaScript sulla homepage, sulle pagine categoria e persino al checkout, non sei il solo. Questo è uno dei problemi di prestazioni più comuni nell'ecosistema PrestaShop, e deriva dal funzionamento del sistema di hook combinato con pratiche di sviluppo poco attente.

PrestaShop utilizza un'architettura basata su hook per consentire ai moduli di iniettare contenuti, risorse e logica in diverse parti di una pagina. Quando un modulo deve aggiungere file CSS o JavaScript alla pagina, lo fa generalmente attraverso uno dei due hook: displayHeader o actionFrontControllerSetMedia. Entrambi questi hook vengono eseguiti su ogni pagina del front office senza eccezione. Non esiste un meccanismo integrato nel sistema di hook per limitare la chiamata di un hook a specifici tipi di pagina. È interamente responsabilità dello sviluppatore del modulo verificare quale pagina viene caricata e decidere se aggiungere le risorse o meno.

Il problema è che molti sviluppatori di moduli prendono scorciatoie. Invece di scrivere una logica condizionale che controlla il controller corrente, registrano semplicemente le loro risorse in ogni chiamata hook. Il ragionamento è semplice: se le risorse sono sempre caricate, il modulo funzionerà sempre indipendentemente da dove appare il suo contenuto. Questo approccio "carica e dimentica" garantisce che il modulo funzioni correttamente, ma garantisce anche scarse prestazioni.

Come l'abuso dell'hook displayHeader crea il bloat delle pagine

L'hook displayHeader è il meccanismo principale attraverso cui i moduli aggiungono contenuti alla sezione head dell'HTML di ogni pagina. Quando un modulo implementa il metodo hookDisplayHeader, PrestaShop chiama quel metodo a ogni caricamento di pagina del front office. All'interno di questo metodo, i moduli tipicamente chiamano $this->context->controller->addCSS() e $this->context->controller->addJS() per registrare i loro file di risorse.

Ecco cosa succede in un negozio tipico con 25 moduli installati. Quindici di quei moduli hanno un metodo hookDisplayHeader. Ciascuno aggiunge tra uno e quattro file CSS e JavaScript. Questo significa che ogni singola pagina del tuo negozio carica da 15 a 60 richieste HTTP aggiuntive solo dai moduli, indipendentemente dal fatto che quei moduli visualizzino qualcosa sulla pagina corrente.

Considera un esempio concreto. Un modulo che aggiunge un popup guida taglie alle pagine prodotto ha bisogno di un file CSS per lo stile del popup e un file JavaScript per il comportamento del popup. Se lo sviluppatore registra queste risorse in hookDisplayHeader senza alcun controllo condizionale, quei due file si caricano sulla homepage, su ogni pagina categoria, sulla pagina carrello, sulla pagina checkout e su ogni pagina CMS. La guida taglie non apparirà mai su nessuna di quelle pagine, ma il browser scarica, analizza e processa comunque i file CSS e JavaScript.

Moltiplica questo per ogni modulo che segue lo stesso schema e inizierai a capire perché alcuni negozi PrestaShop caricano 2 MB o più di risorse dei moduli su pagine dove la maggior parte di quelle risorse è completamente inutile.

L'hook actionFrontControllerSetMedia

PrestaShop 1.7 ha introdotto l'hook actionFrontControllerSetMedia come alternativa più moderna a displayHeader per la registrazione delle risorse. Questo hook è stato progettato specificamente per registrare file CSS e JavaScript utilizzando i nuovi metodi registerStylesheet e registerJavascript. Questi metodi offrono vantaggi rispetto alle vecchie funzioni addCSS e addJS, inclusa la possibilità di impostare la priorità di caricamento, specificare attributi async o defer e controllare la posizione (head o bottom) dei tag script.

Tuttavia, actionFrontControllerSetMedia soffre dello stesso identico problema fondamentale di displayHeader: viene eseguito su ogni pagina. Un modulo che registra risorse in hookActionFrontControllerSetMedia senza verificare il controller corrente carica quelle risorse ovunque, proprio come l'approccio precedente.

L'unica differenza è il metodo di registrazione, non l'ambito di caricamento. Che uno sviluppatore usi addCSS in displayHeader o registerStylesheet in actionFrontControllerSetMedia, il risultato è lo stesso se non aggiunge logica condizionale. Le risorse si caricano globalmente.

Caricamento condizionale corretto: cosa fanno i moduli ben sviluppati

Un modulo ben sviluppato verifica quale pagina sta visualizzando il cliente prima di caricare qualsiasi risorsa. Il modo standard per farlo in PrestaShop è controllare il nome del controller. Ogni pagina del front office è servita da un controller specifico: IndexController per la homepage, CategoryController per le pagine categoria, ProductController per le pagine prodotto, CartController per il carrello e così via.

All'interno del metodo hookDisplayHeader o hookActionFrontControllerSetMedia, lo sviluppatore può accedere al controller corrente attraverso $this->context->controller. Controllando il nome della classe o la proprietà php_self del controller, il modulo può decidere se caricare le sue risorse. Un modulo di recensioni prodotto, ad esempio, dovrebbe verificare se la pagina corrente è una pagina prodotto e caricare i suoi CSS e JavaScript solo in quel caso.

Questo approccio condizionale non è difficile da implementare. Aggiunge forse da cinque a dieci righe di codice al metodo hook. Eppure un numero sorprendente di moduli, inclusi moduli a pagamento popolari sul marketplace PrestaShop Addons e moduli gratuiti ben noti, saltano completamente questo controllo. La ragione è una combinazione di comodità per lo sviluppatore e il fatto che PrestaShop non impone né incoraggia il caricamento condizionale nella sua documentazione per lo sviluppo di moduli.

Alcuni sviluppatori sostengono che il caricamento globale delle risorse garantisce la compatibilità con temi personalizzati o configurazioni di pagina insolite dove il contenuto del modulo potrebbe apparire inaspettatamente. Anche se questo argomento ha un fondo di verità, non giustifica il costo in termini di prestazioni. Un approccio migliore è caricare le risorse in modo condizionale per impostazione predefinita e fornire un'opzione di configurazione per abilitare il caricamento globale per i negozi che ne hanno bisogno.

Il metodo setMedia: best practice per gli sviluppatori di moduli

Per gli sviluppatori di moduli che leggono questo articolo, ecco le best practice per il caricamento delle risorse che rispettano le prestazioni del sito dei vostri utenti.

Primo, utilizzate sempre l'hook actionFrontControllerSetMedia invece di displayHeader per la registrazione delle risorse in PrestaShop 1.7 e versioni successive. L'hook più recente fornisce un controllo migliore sul comportamento di caricamento delle risorse e mantiene la registrazione delle risorse separata dall'output HTML.

Secondo, controllate sempre il controller corrente prima di registrare le risorse. Usate un approccio whitelist: definite l'elenco dei controller dove il vostro modulo visualizza contenuti e registrate le risorse solo quando il controller corrente è in quell'elenco. Questo è più manutenibile di un approccio blacklist perché i nuovi tipi di pagina aggiunti nelle future versioni di PrestaShop non caricheranno accidentalmente le vostre risorse.

Terzo, utilizzate registerStylesheet e registerJavascript invece di addCSS e addJS. I metodi più recenti consentono di specificare un id per ogni risorsa, il che rende possibile per i temi e altri moduli sovrascrivere o rimuovere le vostre risorse in modo pulito. Supportano anche impostazioni di priorità che controllano l'ordine in cui le risorse vengono caricate.

Quarto, considerate se il vostro JavaScript deve veramente caricarsi nell'head o se può caricarsi alla fine della pagina con l'attributo defer. Il JavaScript nell'head blocca il rendering, il che significa che il browser non può visualizzare alcun contenuto fino a quando il vostro script non ha terminato il download e il parsing. Spostare gli script in fondo alla pagina o aggiungere defer elimina questo comportamento di blocco del rendering.

Quinto, minimizzate le dimensioni dei vostri file di risorse. Minificate i vostri file CSS e JavaScript prima di distribuire il vostro modulo. Rimuovete le regole CSS inutilizzate. Evitate di includere intere librerie come jQuery UI o Bootstrap quando utilizzate solo una piccola parte delle loro funzionalità. Ogni kilobyte conta quando le risorse del vostro modulo si caricano su migliaia di visualizzazioni di pagina al giorno.

Misurare l'impatto sulle prestazioni del caricamento globale delle risorse

Per capire quanto costa al tuo negozio il caricamento globale delle risorse, hai bisogno di misurazioni concrete. Apri Chrome DevTools sulla tua homepage e vai alla scheda Network. Ricarica la pagina e filtra le richieste per il percorso /modules/. Conta il numero totale di richieste e la loro dimensione combinata. Questo è l'overhead delle risorse dei moduli su una pagina dove la maggior parte dei moduli non visualizza alcun contenuto.

Ora fai lo stesso su una pagina prodotto, dove molti moduli hanno legittimamente bisogno delle loro risorse. Confronta i numeri. In un negozio ben ottimizzato, la pagina prodotto dovrebbe caricare significativamente più risorse dei moduli rispetto alla homepage perché è lì che la maggior parte dei moduli visualizza i propri contenuti. In un negozio scarsamente ottimizzato, i numeri sono quasi identici perché ogni modulo carica tutto ovunque.

Un benchmark concreto da audit reali: un negozio con 35 moduli installati caricava 1,8 MB di CSS e JavaScript dei moduli sulla homepage. Dopo aver implementato il caricamento condizionale su tutti i moduli, le risorse dei moduli sulla homepage sono scese a 340 KB. La pagina prodotto, dove la maggior parte dei moduli aveva legittimamente bisogno delle proprie risorse, è passata da 2,1 MB a 1,4 MB. Il tempo di caricamento della homepage è migliorato di 1,3 secondi e il punteggio prestazioni di Lighthouse è passato da 42 a 71.

Questi numeri non sono insoliti. Il caricamento globale delle risorse è una delle singole maggiori fonti di peso della pagina non necessario nei negozi PrestaShop, e correggerlo produce spesso i miglioramenti di prestazioni più significativi.

Come identificare i moduli problematici nel tuo negozio

Trovare quali moduli caricano risorse globalmente richiede un approccio sistematico. Inizia con la scheda Network nei DevTools come descritto sopra. Per ogni risorsa di modulo che trovi caricata sulla homepage, chiediti: questo modulo visualizza contenuti sulla homepage? Se la risposta è no, quel modulo sta caricando risorse inutilmente.

Un altro approccio è utilizzare la modalità di profiling debug di PrestaShop. Quando il profiling è abilitato (impostando _PS_DEBUG_PROFILING_ su true nel tuo config/defines.inc.php), PrestaShop mostra dati dettagliati sull'esecuzione degli hook nella parte inferiore di ogni pagina. Questi dati ti mostrano esattamente quali moduli vengono eseguiti su ogni hook e quanto tempo impiegano. Qualsiasi modulo che viene eseguito su displayHeader o actionFrontControllerSetMedia ma non viene eseguito su nessun hook di visualizzazione che produce output visibile sulla pagina corrente è un candidato per l'ottimizzazione.

Puoi anche verificare a livello di codice esaminando il codice sorgente di ogni modulo. Guarda i metodi hookDisplayHeader e hookActionFrontControllerSetMedia nel file PHP principale del modulo. Se il metodo contiene chiamate addCSS, addJS, registerStylesheet o registerJavascript senza alcun controllo condizionale sul nome del controller, quel modulo carica le risorse globalmente.

Per i negozi con molti moduli, questa revisione manuale può richiedere molto tempo. Una scorciatoia pratica è concentrarsi prima sulle risorse più grandi. Ordina le risorse dei moduli nella scheda Network per dimensione e inizia a indagare sui file più grandi. Un file JavaScript da 200 KB che si carica globalmente è un problema molto più grande di un file CSS da 3 KB, quindi dai priorità di conseguenza.

Richiedere correzioni agli sviluppatori dei moduli

Quando identifichi un modulo che carica risorse globalmente, il tuo primo passo dovrebbe essere contattare lo sviluppatore e richiedere una correzione. La maggior parte degli sviluppatori di moduli professionali è ricettiva alle richieste di miglioramento delle prestazioni, specialmente quando puoi fornire dati specifici sull'impatto.

Scrivi un messaggio chiaro e tecnico che spieghi il problema. Menziona che il metodo hookDisplayHeader del modulo carica CSS e JavaScript su ogni pagina senza controllare il controller corrente. Specifica quali pagine hanno effettivamente bisogno delle risorse e quali pagine le caricano inutilmente. Includi le dimensioni dei file e l'impatto stimato sulle prestazioni. Se hai punteggi Lighthouse che mostrano il prima e il dopo quando le risorse del modulo vengono bloccate, includili.

Se lo sviluppatore è reattivo, tipicamente rilascerà un aggiornamento entro poche settimane che aggiunge il caricamento condizionale. Se lo sviluppatore non risponde o ignora la preoccupazione, hai diverse opzioni: implementare il caricamento condizionale tu stesso modificando il codice del modulo, utilizzare un modulo di ottimizzazione delle prestazioni che può bloccare selettivamente le risorse su pagine specifiche, oppure trovare un modulo alternativo che segua migliori pratiche di sviluppo.

Soluzioni alternative quando non puoi modificare il modulo

A volte non puoi modificare un modulo direttamente. Potrebbe essere criptato con IonCube, potrebbe essere un modulo di cui non vuoi mantenere un fork, oppure il modulo potrebbe sovrascrivere le tue modifiche a ogni aggiornamento. In questi casi, hai bisogno di soluzioni alternative.

Una soluzione alternativa efficace è creare un piccolo modulo personalizzato che sgancia il modulo problematico da displayHeader e actionFrontControllerSetMedia, poi riaggiunge le risorse solo sulle pagine dove sono necessarie. Questo modulo personalizzato utilizzerebbe l'hook actionFrontControllerSetMedia con una priorità alta (così viene eseguito dopo il modulo problematico) e chiamerebbe Media::unregisterStylesheet e Media::unregisterJavascript per rimuovere le risorse caricate globalmente. Poi, sulle pagine dove le risorse sono effettivamente necessarie, le ri-registrerebbe.

Un'altra soluzione alternativa è utilizzare il file Theme.yml per sovrascrivere le risorse dei moduli. In PrestaShop 1.7 e versioni successive, il file di configurazione theme.yml del tema può rimuovere risorse specifiche dei moduli dal caricamento. Questa è una soluzione a livello di tema che persiste tra gli aggiornamenti dei moduli. Tuttavia, rimuove le risorse da tutte le pagine, non selettivamente, quindi dovresti combinarla con la riaggiunta delle risorse su pagine specifiche attraverso un modulo personalizzato o un template del tema.

Una terza opzione, disponibile in PrestaShop 8.x, è utilizzare le funzionalità integrate di priorità e rimozione del sistema di gestione delle risorse. PrestaShop 8 ha migliorato la pipeline delle risorse per dare ai temi maggiore controllo su quali risorse dei moduli vengono caricate. Consulta la documentazione del tuo tema per istruzioni specifiche su come sfruttare queste funzionalità.

Indipendentemente dall'approccio scelto, testa sempre accuratamente dopo aver apportato le modifiche. Rimuovere il CSS di un modulo può comprometterne l'aspetto visivo, e rimuovere il suo JavaScript può compromettere le funzionalità interattive. Testa ogni tipo di pagina dove il modulo dovrebbe ancora funzionare correttamente.

Prevenzione: valutare i moduli prima dell'installazione

Il modo migliore per evitare problemi di caricamento globale delle risorse è valutare i moduli prima di installarli. Sebbene questo non sia sempre possibile, ci sono verifiche che puoi eseguire.

Se il modulo fornisce un negozio demo, visitalo e ispeziona la homepage con i DevTools. Controlla se le risorse del modulo si caricano su pagine dove il modulo non visualizza contenuti. Se le risorse si caricano globalmente nella demo, si caricheranno globalmente anche nel tuo negozio.

Se hai accesso al codice sorgente del modulo prima dell'acquisto (alcuni marketplace forniscono anteprime del codice), guarda i metodi hookDisplayHeader e hookActionFrontControllerSetMedia. Controlla la presenza di logica di caricamento condizionale. L'assenza di qualsiasi controllo sul controller è un segnale d'allarme.

Leggi le recensioni e il forum di supporto del modulo. Gli utenti attenti alle prestazioni segnalano spesso problemi di caricamento globale delle risorse nelle recensioni. Se più utenti si sono lamentati del fatto che il modulo rallenta il loro negozio, prendi sul serio quel feedback.

Infine, considera la qualità complessiva del codice dello sviluppatore. Gli sviluppatori che seguono le best practice in un'area tendono a seguirle anche nelle altre. Se il codice di un modulo è pulito, ben documentato e segue gli standard di codifica di PrestaShop, è più probabile che gestisca correttamente il caricamento delle risorse. Se il codice è disordinato e mal strutturato, il caricamento globale delle risorse è probabilmente solo uno dei tanti problemi.

Questa risposta ti è stata utile?

Hai ancora domande?

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

Loading...
Back to top