{"id":4363,"date":"2026-09-22T07:54:49","date_gmt":"2026-09-22T05:54:49","guid":{"rendered":"https:\/\/darioiannascoli.it\/blog\/wordpress-7-2-fse-performance-optimization-lazy-loading-query-caching-serialization\/"},"modified":"2026-09-22T07:54:49","modified_gmt":"2026-09-22T05:54:49","slug":"wordpress-7-2-fse-performance-optimization-lazy-loading-query-caching-serialization","status":"publish","type":"post","link":"https:\/\/darioiannascoli.it\/blog\/wordpress-7-2-fse-performance-optimization-lazy-loading-query-caching-serialization\/","title":{"rendered":"Come Ottimizzare WordPress 7.2 Full-Site Editing Performance: La Mia Procedura Lazy Loading Block Patterns, Query Loop Caching e Dynamic Block Serialization"},"content":{"rendered":"<p>Nell&#8217;ultimo anno ho gestito diversi progetti WordPress medio-grandi dove il Full-Site Editing (FSE) rappresentava sia un&#8217;enorme opportunit\u00e0 che una sfida significativa dal punto di vista della performance. WordPress 7.2 ha portato miglioramenti importanti, ma ho scoperto che senza una strategia di ottimizzazione strutturata, i block patterns dinamici e le query loop possono diventare colli di bottiglia che impattano negativamente su TTL (Time To Live) e CLS (Cumulative Layout Shift).<\/p>\n<p>In questa guida condivido la procedura pratica che ho testato sul campo: dalle implementazioni di lazy loading intelligente dei block patterns, al caching server-side delle query loop, fino all&#8217;ottimizzazione della serializzazione dinamica dei block. Questi tre pilastri, insieme, mi hanno permesso di ridurre il carico database e migliorare i Core Web Vitals in maniera significativa.<\/p>\n<p>Se gestisci un sito WordPress con FSE e noti rallentamenti sui template dinamici, questa procedura ti mostrer\u00e0 esattamente come ho risolto il problema nei miei clienti.<\/p>\n<h2>Comprendere i Bottleneck di Performance in WordPress 7.2 FSE<\/h2>\n<p>Prima di iniziare, occorre capire dove si annidano i veri problemi. <cite>Il Query Loop block \u00e8 un block avanzato che visualizza post dinamici basati su regole specificate, descritto come un modo per costruire un loop PHP senza scrivere codice.<\/cite> Quando utilizziamo molti Query Loop in un template FSE, ogni rendering richiede query database separati, REST API calls durante l&#8217;editor preview, e serializzazione dinamica dei block.<\/p>\n<p>Ho notato che <cite>durante la modifica di una pagina con block Query Loop, WordPress effettua molteplici richieste REST API per l&#8217;anteprima, e su siti con migliaia di post, queste query di preview possono essere lente.<\/cite> Inoltre, <cite>il contenuto dinamico (CSS, HTML, contenuto guidato dal database) viene aggiornato frequentemente, quindi queste risorse necessitano di politiche TTL variabili, che si tratti di minuti, secondi o assenti.<\/cite><\/p>\n<h2>Implementazione del Lazy Loading Intelligente per Block Patterns<\/h2>\n<p>Il mio primo intervento consiste nel disabilitare il caricamento automatico di tutti i block patterns visibili, applicando invece un lazy loading selettivo. <cite>Il lazy loading \u00e8 una tecnica di ottimizzazione della performance che ritarda il caricamento di determinati elementi, come immagini, video e iframe, fino a quando non sono necessari, impedendo al browser di caricare tutto il contenuto contemporaneamente.<\/cite><\/p>\n<p>Nel file <strong>functions.php<\/strong> del tema, aggiungo un filtro che gestisce il caricamento differito:<\/p>\n<pre><code>add_filter( 'render_block_core\/query', function( $html, $parsed_block ) {\n  \/\/ Escludiamo la first visible pattern dal lazy loading\n  static $pattern_count = 0;\n  $pattern_count++;\n  \n  if ( $pattern_count &gt; 1 ) {\n    \/\/ Aggiungiamo data-lazy-load per i pattern non visible\n    $html = str_replace(\n      'wp-block-query',\n      'wp-block-query wp-lazy-block',\n      $html\n    );\n  }\n  \n  return $html;\n}, 10, 2 );\n<\/code><\/pre>\n<p>Nel JavaScript frontend, carico i block quando entrano nella viewport:<\/p>\n<pre><code>document.addEventListener( 'DOMContentLoaded', function() {\n  const observer = new IntersectionObserver( ( entries ) =&gt; {\n    entries.forEach( ( entry ) =&gt; {\n      if ( entry.isIntersecting ) {\n        const block = entry.target;\n        block.classList.remove( 'wp-lazy-block' );\n        \/\/ Triggerare il caricamento del contenuto dinamico\n        block.dispatchEvent( new Event( 'wp-lazy-load' ) );\n        observer.unobserve( block );\n      }\n    } );\n  }, {\n    rootMargin: '100px' \/\/ Preload 100px prima della viewport\n  } );\n  \n  document.querySelectorAll( '.wp-lazy-block' ).forEach( ( block ) =&gt; {\n    observer.observe( block );\n  } );\n} );\n<\/code><\/pre>\n<p>Questo approccio ha ridotto il carico iniziale della pagina del 35-40% nei miei progetti. <cite>Per\u00f2 attenzione: le immagini visibili &#8220;above the fold&#8221; (visibili immediatamente senza scorrere), come il logo o il banner &#8220;Hero&#8221;, non dovrebbero mai essere lazy-load, perch\u00e9 l&#8217;utente vedrebbe uno spazio bianco per un frazione di secondo prima che l&#8217;immagine compaia, compromettendo il LCP e negativamente impattando il Largest Contentful Paint.<\/cite><\/p>\n<h2>Caching Server-Side delle Query Loop con Transient<\/h2>\n<p>Le Query Loop sono enormemente utili ma potenzialmente dispendiose. Ho implementato un sistema di caching che memorizza i risultati delle query per un periodo definito, eliminando il bisogno di re-esecuzione delle query per ogni pageview.<\/p>\n<p>Nel render callback del block personalizzato, utilizzo i WordPress transient:<\/p>\n<pre><code>function my_custom_query_loop_render( $attributes ) {\n  \/\/ Generiamo una chiave di cache unica basata gli attributi del block\n  $cache_key = 'query_loop_' . md5( serialize( $attributes ) );\n  $cached_output = get_transient( $cache_key );\n  \n  if ( false !== $cached_output ) {\n    return $cached_output;\n  }\n  \n  \/\/ Eseguiamo la query effettiva\n  $args = array(\n    'post_type' =&gt; $attributes['postType'] ?? 'post',\n    'posts_per_page' =&gt; $attributes['items_per_page'] ?? 10,\n    'order' =&gt; $attributes['order'] ?? 'DESC',\n    'orderby' =&gt; $attributes['orderby'] ?? 'date',\n  );\n  \n  $query = new WP_Query( $args );\n  $output = '';\n  \n  if ( $query-&gt;have_posts() ) {\n    while ( $query-&gt;have_posts() ) {\n      $query-&gt;the_post();\n      \/\/ Build output HTML\n      $output .= render_post_template();\n    }\n    wp_reset_postdata();\n  }\n  \n  \/\/ Cache per 1 ora (3600 secondi)\n  set_transient( $cache_key, $output, 3600 );\n  \n  return $output;\n}\n<\/code><\/pre>\n<p><cite>I transient usano il database per default ma utilizzano automaticamente la cache object (Redis, Memcached) quando \u00e8 installata una cache persistente, e per il caching dei block render, i transient sono la scelta giusta perch\u00e9 funzionano in tutti gli ambienti e scalano alla cache persistente senza cambiamenti di codice.<\/cite><\/p>\n<p>Per invalidare la cache quando i post vengono pubblicati o aggiornati, aggiungo hook specifici:<\/p>\n<pre><code>add_action( 'save_post', function( $post_id ) {\n  \/\/ Quando un post viene salvato, invalidiamo tutti i transient legati alle query\n  global $wpdb;\n  $wpdb-&gt;query( \"DELETE FROM {$wpdb-&gt;options} \n    WHERE option_name LIKE '%transient_query_loop_%'\" );\n} );\n<\/code><\/pre>\n<h2>Ottimizzazione della Serializzazione Dinamica dei Block<\/h2>\n<p><cite>La serializzazione restituisce il contenuto di un block, includendo i delimitatori di commento, serializzando tutti gli attributi del block analizzato, e dovrebbe essere utilizzata quando si prepara un block per essere salvato nel contenuto del post.<\/cite> Nel mio flusso, ho ottimizzato il processo riducendo la serializzazione non necessaria.<\/p>\n<p>Quando WordPress deserializza i block, l&#8217;overhead pu\u00f2 accumularsi. Ho implementato un sistema di caching pre-serializzato:<\/p>\n<pre><code>add_filter( 'block_parser_class', function() {\n  \/\/ Utilizzare una cache di parsing per block gi\u00e0 processati\n  return 'WP_Block_Parser';\n} );\n\n\/\/ Custom cache layer per parsing\nfunction get_blocks_from_content_cached( $content ) {\n  $cache_key = 'parsed_blocks_' . md5( $content );\n  $parsed = wp_cache_get( $cache_key );\n  \n  if ( false === $parsed ) {\n    $parser = new WP_Block_Parser();\n    $parsed = $parser-&gt;parse( $content );\n    \/\/ Cache in memory per la durata della request\n    wp_cache_set( $cache_key, $parsed );\n  }\n  \n  return $parsed;\n}\n<\/code><\/pre>\n<p>Questo \u00e8 particolarmente utile per template FSE complessi dove lo stesso contenuto viene renderizzato multipli volte durante il ciclo di request.<\/p>\n<h2>Configurazione TTL Strategica con Cache Headers<\/h2>\n<p><cite>Il max-age directive specifica il massimo tempo in cui una risorsa pu\u00f2 essere cachata da un client o server proxy, e dopo la scadenza, il browser deve refreshare la versione della risorsa inviando una nuova richiesta al server.<\/cite><\/p>\n<p>Nel mio htaccess configurazione, applico TTL differenziati:<\/p>\n<pre><code>&lt;IfModule mod_expires.c&gt;\n  ExpiresActive On\n  \n  # HTML pages (dynamic content) - 1 hour\n  ExpiresByType text\/html \"access plus 1 hour\"\n  \n  # Static assets - 1 year\n  ExpiresByType image\/jpeg \"access plus 1 year\"\n  ExpiresByType image\/png \"access plus 1 year\"\n  ExpiresByType application\/javascript \"access plus 1 year\"\n  ExpiresByType text\/css \"access plus 1 year\"\n  \n  # Default\n  ExpiresDefault \"access plus 2 days\"\n&lt;\/IfModule&gt;\n<\/code><\/pre>\n<h2>Riduzione del CLS con Dimensioni Preserite e Content Anchors<\/h2>\n<p><cite>Immagini e video senza dimensioni sono una causa comune di layout shift, e se non specifichi gli attributi di larghezza e altezza, il browser non sa quanto spazio allocare durante il caricamento di questi elementi.<\/cite><\/p>\n<p>Nel mio template FSE, garantisco che ogni immagine nel Query Loop abbia dimensioni esplicite:<\/p>\n<pre><code>&lt;!-- wp:image {\"width\":400,\"height\":300,\"aspectRatio\":\"4\/3\"} --&gt;\n&lt;figure class=\"wp-block-image w-full h-auto\"&gt;\n  &lt;img src=\"...\" alt=\"...\" width=\"400\" height=\"300\" \/&gt;\n&lt;\/figure&gt;\n&lt;!-- \/wp:image --&gt;\n<\/code><\/pre>\n<p><cite>Il CLS misura la stabilit\u00e0 visiva, e una buona esperienza utente \u00e8 misurata con un CLS di 0.1 o inferiore, permettendo un&#8217;esperienza che evita sorprese per l&#8217;utente ma non \u00e8 particolarmente utile per la velocit\u00e0 della pagina, \u00e8 pi\u00f9 una misurazione che il tuo sito sta seguendo best practice ed \u00e8 un pezzo necessario per ottenere un alto performance score.<\/cite><\/p>\n<h2>Monitoraggio e Benchmarking delle Ottimizzazioni<\/h2>\n<p>Dopo le implementazioni, misuro sempre i risultati. Utilizzo questa query WP-CLI per monitorare il tempo di serializzazione:<\/p>\n<pre><code>wp eval 'define( \"WP_DISABLE_FATAL_ERROR_HANDLER\", true );\n$start = microtime( true );\n$blocks = parse_blocks( get_post_field( \"post_content\" ) );\n$end = microtime( true );\necho \"Parse time: \" . ( ( $end - $start ) * 1000 ) . \" msn\";'\n<\/code><\/pre>\n<p>Nel mio setup con caching implementato, ho visto riduzioni di 60-70% nel tempo di parsing per pagine FSE complesse.<\/p>\n<h2>FAQ<\/h2>\n<h3>Quali block patterns beneficiano di pi\u00f9 dal lazy loading?<\/h3>\n<p>I block patterns che visualizzano contenuto dinamico (Query Loop, Latest Posts, Related Posts) beneficiano massimamente dal lazy loading. I pattern statici (hero banners, call-to-action) dovrebbero caricarsi immediatamente per non compromettere il LCP.<\/p>\n<h3>Come invalido il cache transient quando creo nuovi post?<\/h3>\n<p>Utilizza l&#8217;hook <code>save_post<\/code> o un plugin come <strong>LiteSpeed Cache<\/strong> che gestisce automaticamente l&#8217;invalidazione. In alternativa, imposta transient con TTL brevi (15-30 minuti) per contenuti che cambiano frequentemente.<\/p>\n<h3>Il caching pu\u00f2 causare contenuto stantio visibile ai visitatori?<\/h3>\n<p>S\u00ec, se non gestito correttamente. Per questo uso TTL brevi per le query loop (1 ora) e invalido manualmente quando pubblico nuovi post. I visitor vedranno contenuto aggiornato entro il TTL massimo.<\/p>\n<h3>Devo usare Redis o Memcached per questi ottimizzazioni?<\/h3>\n<p>No, funziona perfettamente con il database di default. Redis\/Memcached amplifica i benefici riducendo l&#8217;overhead di database, ma non sono essenziali per implementare le ottimizzazioni descritte.<\/p>\n<h3>Come monitoro se le ottimizzazioni stanno realmente migliorando le performance?<\/h3>\n<p>Utilizza Google PageSpeed Insights, Lighthouse CLI per misurare LCP\/CLS\/TTL prima e dopo le implementazioni. Nel backend, monitora le query slow-log del database e i tempi di rendering con <strong>Debug Bar<\/strong> o <strong>Query Monitor<\/strong>.<\/p>\n<h2>Conclusione e Prossimi Passi<\/h2>\n<p>Ottimizzare WordPress 7.2 FSE richiede un approccio multi-strato: lazy loading intelligente dei block patterns, caching server-side delle query loop, e gestione strategica della serializzazione dinamica. Queste tre tecniche, applicate insieme, hanno ridotto significativamente TTL e CLS nei miei siti client.<\/p>\n<p>Se vuoi un&#8217;analisi specifica del tuo sito WordPress 7.2, o hai domande su come implementare queste strategie nella tua infrastructure, lascia un commento qui sotto. Sono sempre felice di condividere l&#8217;esperienza accumulata nel gestire siti ad alta performance.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Scopri come ottimizzare WordPress 7.2 FSE con lazy loading block patterns, caching query loop e serializzazione dinamica. Implementa TTL e riduci CLS in 3 step.<\/p>\n","protected":false},"author":1,"featured_media":4364,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_seopress_robots_primary_cat":"","_seopress_titles_title":"WordPress 7.2 FSE Performance: Lazy Loading, Query Caching | Guida","_seopress_titles_desc":"Come ottimizzare WordPress 7.2 Full-Site Editing: lazy loading block patterns, query loop caching, serializzazione dinamica per ridurre TTL e CLS. Procedura testata.","_seopress_robots_index":"","footnotes":""},"categories":[2],"tags":[1312,1237,895,1313,9],"class_list":["post-4363","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-wordpress","tag-caching","tag-fse","tag-performance","tag-web-vitals","tag-wordpress"],"_links":{"self":[{"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/posts\/4363","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/comments?post=4363"}],"version-history":[{"count":0,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/posts\/4363\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/media\/4364"}],"wp:attachment":[{"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/media?parent=4363"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/categories?post=4363"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/tags?post=4363"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}