Ottimizzazione del Codice in WordPress: Best Practices per la Performance

Ottimizzazione del Codice in WordPress Best Practices per la Performance

Le performance si un sito WordPress sono essenziali per l’esperienza utente e per la SEO. Più il sito è veloce, e meglio è per tutti.

In questo articolo vorrei esplorare le best practices per ottimizzare il codice in WordPress e migliorarne così le performance.

Ottimizzare le Query al Database

Le query al database sono uno degli aspetti più critici per quanto riguarda le performance di un sito WordPress.

Poiché WordPress si basa su un database MySQL per memorizzare e recuperare contenuti dinamici, l’efficienza delle query può fare una grande differenza nel tempo di caricamento delle pagine.

Ecco come possiamo procedere per ottimizzare il codice lato query:

  • Monitoraggio e ottimizzazione: utilizzare strumenti come Query Monitor per identificare colli di bottiglia nelle query.
  • Query efficienti: utilizzare query più rapide ed evitare chiamate ridondanti al database.
  • Caching delle query: implementare sistemi di caching per ridurre il carico sul database.

Monitoraggio ed ottimizzazione

Ho già parlato del debugging WordPress, e ti consiglio di avere almeno uno di questi strumenti sempre attivo mentre sviluppi codice per plugin o temi.

query monitor

Usando il plugin Query Monitor, andando nella sezione Queries → Queries by Component, puoi facilmente capire nella colonna Time il tempo di caricamento aggiunto da ciascun plugin e dal tema.

Nella colonna Select puoi vedere quante queries SELECT (servono per ottenere i dati dalle tabelle MySQL) sono state fatte.

È un ottimo punto di partenza per capire quali plugin andare a controllare, o per capire come impatta il codice che hai creato sul sito WordPress.

Come vedere il tempo di esecuzione di una query?

Per prima cosa attiva i log di WordPress, inserendo nel file wp-config.php che si trova nella root dell’installazione WP, questo codice alla fine del file:

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);

Ora tutti i log verranno stampati nel file debug.log che si trova nella cartella wp-content.

Per vedere il tempo di esecuzione di una query basta aggiungere questo codice prima della query:

$start_time = microtime( true );

E questo codice dopo la query:

$end_time = microtime(true);
$sql_query_time = $end_time - $start_time;
error_log('SQL Query Execution Time: ' . $sql_query_time . ' seconds');

In pratica, microtime(true) è una funzione php che ritorna lo Unix timestamp (il numero di secondi trascorsi dal 1 Gennaio 1970) in formato float che contiene anche i microsecondi.

Come puoi facilmente vedere, il codice per salvare il tempo della query non fa altro che salvare in $start_time lo Unix timestamp, e poi ne salva un altro al termine della query in $end_time.

Facendo una semplice sottrazione otterremo $sql_query_time = $end_time - $start_time, ovvero il tempo di esecuzione della query in secondi.

Ecco un sempio di risultato dell’error_log di sopra: SQL Query Execution Time: 0.00078105926513672 seconds

Ho creato un mini plugin su github giusto per stampare queste queries nel file di log.

Usa query efficienti

Una query inefficiente può aumentare notevolmente i tempi di risposta del database.

Ecco alcuni suggerimenti per migliorare l’efficienza delle query.

Ottimizzare le selezioni

Al posto di usare SELECT * per recuperare tutte le colonne di una tabella, recupera solamente le colonne che ti interessano.

Ad esempio, se vuoi sapere solamente ID e post_title dei post pubblicati, usa una query ottimizzata:

  • Query ottimizzata: SELECT ID, post_title FROM wp_posts WHERE post_status = 'publish';
  • Query inefficiente: SELECT * FROM wp_posts WHERE post_status = 'publish';

Ho fatto un piccolo test di queste queries con soli 12 post in un ambiente in locale:

// 4. Optimized Query: Fetch only required fields (ID and title).
	$start_time    = microtime( true );
	$sql_optimized = "SELECT ID, post_title FROM {$wpdb->posts} WHERE post_status = 'publish'";
	$wpdb->get_results( $sql_optimized );
	$end_time             = microtime( true );
	$optimized_query_time = $end_time - $start_time;
	error_log( '4. Optimized SQL Query Execution Time: ' . $optimized_query_time . ' seconds' );

// 5. Inefficient Query: Fetch all columns for published posts.
	$start_time      = microtime( true );
	$sql_inefficient = "SELECT * FROM {$wpdb->posts} WHERE post_status = 'publish'";
	$wpdb->get_results( $sql_inefficient );
	$end_time               = microtime( true );
	$inefficient_query_time = $end_time - $start_time;
	error_log( '5. Inefficient SQL Query Execution Time: ' . $inefficient_query_time . ' seconds' );

// 6. Perform the first query using WP_Query and log the execution time.
	$start_time    = microtime( true );
	$wp_query      = new WP_Query(
		array(
			'posts_per_page' => -1,
			'orderby'        => 'date',
			'order'          => 'DESC',
		)
	);
	$end_time      = microtime( true );
	$wp_query_time = $end_time - $start_time;
	error_log( '6. WP_Query Execution Time: ' . $wp_query_time . ' seconds' );

// 7. Perform the first query using WP_Query to get only id and title and log the execution time.
	$start_time = microtime( true );

	$wp_query_optimized = new WP_Query(
		array(
			'post_status'    => 'publish',
			'posts_per_page' => -1, // Recupera tutti i post pubblicati
			'fields'         => 'ids', // Ottimizzazione: recupera solo gli ID
		)
	);

	$post_data = array();

	if ( $wp_query_optimized->have_posts() ) {
		foreach ( $wp_query_optimized->posts as $post_id ) {
			$post_data[] = array(
				'ID'    => $post_id,
				'title' => get_the_title( $post_id ),
			);
		}
	}

	$end_time             = microtime( true );
	$optimized_query_time = $end_time - $start_time;

	error_log( '7. WP_Query Execution Time: ' . $wp_query_time . ' seconds' );

Ecco i risultati:

4. Optimized SQL Query Execution Time: 0.0011448860168457 seconds
5. Inefficient SQL Query Execution Time: 0.0022962093353271 seconds
6. WP_Query Execution Time: 0.0033149719238281 seconds
7. WP_Query Execution Time: 0.0033149719238281 seconds

Evita le query troppo complesse

Query con troppi join, sotto-query o calcoli complessi possono rallentare significativamente il database.

Se possibile, cerca di semplificare o suddividere le query in più fasi.

Ecco un esempio di query complessa con JOIN multipli:

SELECT wp_posts.ID, wp_posts.post_title, wp_users.user_login, COUNT(wp_comments.comment_ID) as comment_count
FROM wp_posts
INNER JOIN wp_users ON wp_posts.post_author = wp_users.ID
LEFT JOIN wp_comments ON wp_posts.ID = wp_comments.comment_post_ID
WHERE wp_posts.post_status = 'publish'
GROUP BY wp_posts.ID, wp_users.ID
ORDER BY wp_posts.post_date DESC;

Questa query esegue diversi JOIN (tra la tabella dei post, quella degli utenti e quella dei commenti) e un COUNT sui commenti, tutto in un’unica operazione.

Il database deve eseguire tutti i calcoli e le operazioni di join per ogni post, e soprattutto se la tabella wp_comments è molto grande o se ci sono molti post potrebbe causare dei rallentamenti.

Per semplificare la query si potrebbe spezzare in due fasi:

  • Fase 1: recuperare i post con il loro autore.
  • Fase 2: recuperare il numero di commenti per ciascun post separatamente.

Fase 1, recuperare i post e gli autori:

SELECT wp_posts.ID, wp_posts.post_title, wp_users.user_login
FROM wp_posts
INNER JOIN wp_users ON wp_posts.post_author = wp_users.ID
WHERE wp_posts.post_status = 'publish'
ORDER BY wp_posts.post_date DESC;

Seleziona solo i dati dei post e dei rispettivi autori senza fare alcun calcolo sui commenti.

Fase 2, recuperare il numero di commenti per ciascun post:

SELECT wp_comments.comment_post_ID, COUNT(wp_comments.comment_ID) as comment_count
FROM wp_comments
GROUP BY wp_comments.comment_post_ID;

Questa query recupera il conteggio dei commenti, ma solo per ciascun post separatamente. Non è necessario fare alcun join o aggregazione con altre tabelle, rendendo questa parte più leggera.

Unire i dati a livello di applicazione.

Una volta che hai eseguito entrambe le query separatamente, puoi unire i dati a livello di applicazione (ad esempio, nel tuo codice PHP). Puoi fare qualcosa del genere:

// Esegui la Fase 1
$post_data = $wpdb->get_results( "SELECT wp_posts.ID, wp_posts.post_title, wp_users.user_login FROM wp_posts INNER JOIN wp_users ON wp_posts.post_author = wp_users.ID WHERE wp_posts.post_status = 'publish' ORDER BY wp_posts.post_date DESC;" );

// Esegui la Fase 2
$comment_counts = $wpdb->get_results( "SELECT wp_comments.comment_post_ID, COUNT(wp_comments.comment_ID) as comment_count FROM wp_comments GROUP BY wp_comments.comment_post_ID;" );

// Unisci i risultati a livello di applicazione
foreach ($post_data as $post) {
    $post->comment_count = 0; // Imposta un valore di default per i commenti
    foreach ($comment_counts as $count) {
        if ($count->comment_post_ID == $post->ID) {
            $post->comment_count = $count->comment_count;
            break;
        }
    }
}

// Ora puoi usare $post_data, che include i post con i loro autori e il conteggio dei commenti.

Utilizzare gli indici

Un indice di un database è una struttura che migliora la velocità di accesso ai dati contenuti nelle tabelle. È un po’ come l’indice di un libro.

Vengono utilizzati per ottimizzare le operazioni di ricerca, ordinamento e filtro dei dati. Quando esegui una query che cerca, ordina o filtra i dati, il database può utilizzare un indice per ridurre il tempo necessario per trovare i risultati.

Assicurati che le colonne che vengono frequentemente interrogate (come quelle utilizzate nelle clausole WHERE o ORDER BY) siano indicizzate.

Ad esempio se esegui frequentemente query basate sul campo post_date della tabella wp_posts, aggiungere un indice a questa colonna velocizzerà le operazioni di ricerca.

CREATE INDEX idx_post_date ON wp_posts(post_date);

Questo comando SQL crea un indice chiamato idx_post_date sulla colonna post_date della tabella wp_posts.

Dopo aver creato l’indice, MySQL utilizzerà questa struttura per accedere più rapidamente ai dati basati sulla data del post.

Se le tue query filtrano i post non solo per post_date, ma anche per post_status (ad esempio, per recuperare solo i post pubblicati), potresti voler creare un indice composto su entrambe le colonne:

CREATE INDEX idx_post_date_status ON wp_posts(post_date, post_status);

Questo tipo di indice è utile quando esegui frequentemente query che cercano una combinazione di più colonne, come:

SELECT * FROM wp_posts WHERE post_status = 'publish' ORDER BY post_date DESC;

Caricare i file solo quando sono necessari

È sempre una buona pratica caricare solamente gli script necessari per ogni pagina.

n WordPress, puoi caricare gli script in modo condizionale utilizzando la funzione wp_enqueue_script() nel file functions.php del tuo tema o in un plugin personalizzato.

WordPress mette a disposizione molte funzione che ti aiuteranno per questo scopo, come:

  • is_page(): determina se la query riguarda una singola pagina esistente.
  • is_single(): determina se la query riguarda un singolo post esistente. Funziona per tutti i tipi di post tranne che per pagina e allegati.
  • is_singular(): determina se la query riguarda un singolo post esistente di qualsiasi tipo (post, allegato, pagina, tipi di post personalizzati).

Ad esempio puoi verificare se l’utente si trova su una determinata pagina e caricare lo script solo in quel caso:

function carica_script_condizionale() {
    if (is_page('contatti')) { // Cambia 'contatti' con lo slug o ID della tua pagina
        wp_enqueue_script('script-contatti', plugin_dir_url(__FILE__) . 'js/contatti.js', array('jquery'), null, true);
    }
}
add_action('wp_enqueue_scripts', 'carica_script_condizionale');

Se vuoi caricare uno script solo in un tipo di post personalizzato, usa is_singular()

function carica_script_per_custom_post() {
    if (is_singular('portfolio')) { // Cambia 'portfolio' con il tuo Custom Post Type
        wp_enqueue_script('script-portfolio', plugin_dir_url(__FILE__) . 'js/portfolio.js', array('jquery'), null, true);
    }
}
add_action('wp_enqueue_scripts', 'carica_script_per_custom_post');

Se vuoi caricare lo script solo per una pagina specifica basandoti sull’ID, puoi fare così:

function script_solo_per_pagina_id() {
    if (is_page(42)) { // Cambia 42 con l'ID della tua pagina
        wp_enqueue_script('script-custom', plugin_dir_url(__FILE__) . 'js/custom.js', array('jquery'), null, true);
    }
}
add_action('wp_enqueue_scripts', 'script_solo_per_pagina_id');

Se vuoi caricare uno script solo nella homepage:

function script_homepage() {
    if (is_front_page()) {
        wp_enqueue_script('script-home', plugin_dir_url(__FILE__) . 'js/home.js', array(), null, true);
    }
}
add_action('wp_enqueue_scripts', 'script_homepage');

Se vuoi caricare uno script solo quando un shortcode è presente in una pagina:

function carica_script_se_shortcode_presente() {
    global $post;
    if (isset($post) && has_shortcode($post->post_content, 'nome_shortcode')) {
        wp_enqueue_script('script-shortcode', plugin_dir_url(__FILE__) . 'js/shortcode.js', array('jquery'), null, true);
    }
}
add_action('wp_enqueue_scripts', 'carica_script_se_shortcode_presente');

Se vuoi rimuovere script non necessari, usa wp_dequeue_script('nome-script').

Minimizzare e Comprimere CSS, JavaScript e HTML

Minimizzare il codice è la cosa più semplice ed immediata da fare. Minimizzare e comprimere i file CSS, JavaScript e HTML aiuta a ridurre il peso della pagina e migliorare le prestazioni del sito, riducendo il tempo di caricamento e il consumo di banda.

  • Minimizzazione (Minification): rimuove spazi vuoti, commenti e caratteri inutili dai file CSS, JS e HTML, riducendone la dimensione.
  • Compressione: riduce ulteriormente la dimensione dei file utilizzando algoritmi di compressione come Gzip o Brotli, riducendo i tempi di trasferimento tra server e browser.

ecco un esempio di minimizzazione di un file JavaScript:

// Originale
function saluto() {
    console.log("Ciao, benvenuto!");
}

// Minimizzato
function saluto(){console.log("Ciao, benvenuto!")}

Come minimizzare e comprimere in WordPress?

Il metodo più semplice è l’uso di plugin come:

Se vuoi fare a mano, puoi usare servizi gratuiti online come:

Dopo aver minimizzato i file, puoi comprimerli per migliorare il caricamento.

Se usi un server Apache, puoi attivare Gzip aggiungendo questo codice nel file .htaccess:

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json
</IfModule>

Ridurre e Gestire i Plugin

Anche i plugin giocano un ruolo importante nelle performance di un sito, e siccome in questo articolo vogliamo migliorare il codice per ottimizzare le performance di WordPress, dobbiamo controllare anche i plugin.

In linea generale:

  • Disabilita o elimina plugin non necessari: ogni plugin aggiunge peso e complessità.
  • Scegli plugin leggeri e ben codificati: controlla tramite Query Monitor quanto impattano le query dei vari plugin. Per quanto riguarda i file nel frontend, controlla tramite PageSpeed.
  • Disabilita i plugin su pagine in cui non sono necessari. Dai un’occhiata al plugin di Jose Mortellaro Freesoul Deactivate Plugins.

Ottimizzazione del Tema WordPress

Un tema WordPress ben ottimizzato è essenziale per garantire tempi di caricamento rapidi, un’esperienza utente fluida e per avere uno dei requisiti SEO soddisfatti.

Vediamo come ottimizzare il tuo tema seguendo tre principi chiave: semplicità, caricamento condizionale e pulizia del codice.

  • Semplicità del tema: utilizza temi leggeri e ben sviluppati.
  • Caricamento condizionale: carica solo gli script necessari per ogni pagina.
  • Evita il codice superfluo: rimuovi codice non utilizzato o ridondante dal tema.

Semplicità del tema

Molti temi WordPress sono pieni di funzioni inutili, codice complesso e risorse pesanti (JS, CSS, immagini non ottimizzate).

Per garantire un sito veloce, scegli un tema leggero e ottimizzato.

Non uno che vada bene per qualsiasi cosa con mille funzioni, piuttosto uno base dove poter aggiungere funzionalità a piacimento.

Partendo da un tema base, senza troppe funzionalità aggiuntive, avrai il pieno controllo sulla qualità e la quantità del codice da aggiungere in un secondo momento.

Ad esempio potresti scegliere uno dei temi di default WordPress.org, creare un child theme e iniziare a personalizzare quello.

Caricamento condizionale

Come per i plugin, anche per il tema dovresti caricare i file solamente quando sono necessari, quindi dovresti fare un caricamento condizionale con le funzioni che ho già risportato sopra, come is_single() e is_page().

Ecco come poter caricare script js in un tema in maniera condizionale:

function carica_script_condizionale() {
    if (is_page('contatti')) { // Cambia 'contatti' con lo slug o ID della tua pagina
        wp_enqueue_script('script-contatti', get_template_directory_uri() . '/js/contatti.js', array('jquery'), null, true);
    }
}
add_action('wp_enqueue_scripts', 'carica_script_condizionale');

O come poter caricare fogli di stile css in maniera condizionale:

function carica_css_condizionale() {
    if (is_single()) { // Carica solo nei post singoli
        wp_enqueue_style('style-post', get_template_directory_uri() . '/css/post.css');
    }
}
add_action('wp_enqueue_scripts', 'carica_css_condizionale');

Evita il codice superfluo

Alcuni temi potrebbero caricare del codice aggiuntivo che a te non servirà a nulla.

Cosa fare in questo caso?

Potresti provare a rimuovere questo codice nel tuo tema child se è stato aggiunto tramite actions, oppure, potresti valutare l’idea di cambiare tema e utilizzarne uno più semplice ma da personalizzare a mano (quindi ci metterai più tempo).

Ecco dei codici che potrebbero tornarti utili per rimuovere le actions o alcuni script indesiderati:

// Rimuove gli emoji di WordPress
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');

// Disattiva il feed RSS se non lo usi
remove_action('wp_head', 'feed_links_extra', 3);
remove_action('wp_head', 'feed_links', 2);

// Rimuove il blocco Gutenberg se non usi l’editor a blocchi
add_action('wp_enqueue_scripts', function() {
    wp_dequeue_style('wp-block-library');
    wp_dequeue_style('wp-block-library-theme');
}, 100);

Andare a modificare direttamente il codice del tema non è mai una buona idea, perchè in caso di aggiornamento del tema in futuro, dovrai ricordarti di riportare le stesse modifiche.

Sponsor

Vhosting
themeforest