Ottimizzare la velocità di caricamento dei Social Widgets


Prendendo spunto da questo thread del Forum GT (Sito veloce nonostante social plugin e javascript, come fare?) ho colto l’occasione per scrivere questo articolo, analizzando l’impatto negativo che hanno i social widgets sulle performance del nostro sito e sulle tecniche da adottare per evitarlo.

WPO - Social Widgets

Oramai, quasi tutti i siti utilizzano social widgets che causano un rallentamento del caricamento della pagina.
Inoltre, tutti i widgets, essendo praticamente javascript possono essere causa di SPOF (illustrati da Andrea Pernici in Cosa sono i Single Point Of Failure).
Per di più, a loro volta, questi javascript richiedono a cascata altre risorse (altri javascript, css, immagini) che causano un aumento considerevole delle richieste (anche una cinquantina).

Velocizziamo i social widgets

Esistono diversi metodi applicabili affinché l’incidenza di questi widgets sia ridotta al minimo.
Ho creato delle pagine di esempio per analizzare le diverse tecniche ed ho utilizzato Speedoo per misurarne i tempi (il tool online, made in italy, per testare la velocità di un sito web targato GT).

Consideriamo i seguenti widgets:

  • Facebook Like button
  • Facebook comment box
  • Twitter button
  • Google+ button

Di seguito illustrerò tre tecniche diverse.

Senza Social widgets

Iniziamo analizzando una pagina web che non fa uso di widgets.
Pagina del test: Caricamento social widgets – Senza social widgets

Waterfall generato da Speedoo

wpo-social-nosocial

Test Speedoo completo: http://www.giorgiotave.it/speedoo/result/130615_MS_4/

Caricamento Asincrono

Il passo più semplice per integrare i social widgets e per evitare gli SPOF è adottare il caricamento asincrono.
Di seguito i codici ufficiali per ogni widget:

Pulsante Mi Piace Facebook

<div id="fb-root"></div>
<script>
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/it_IT/all.js#xfbml=1";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<div class="fb-like" data-href="http://www.andrea-cardinale.it/" data-send="false" data-width="450" data-show-faces="true" data-font="arial"></div>

Modulo commenti Facebook

<div id="fb-root"></div>
<script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/it_IT/all.js#xfbml=1";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<div class="fb-comments" data-href="http://example.com" data-width="470" data-num-posts="10"></div>

Pulsante tweet di Twitter

<a href="https://twitter.com/share" class="twitter-share-button" data-via="CardinaleAndrea" data-lang="it" data-related="CardinaleAndrea" data-hashtags="perfmatters">Tweet</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>

Pulsante +1 di Google+

<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
<g:plusone></g:plusone>
<script type="text/javascript">
(function() {
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
po.src = 'https://apis.google.com/js/plusone.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
})();
</script>
<g:plusone></g:plusone>

Pagina del test: Caricamento social widgets – Asincrono

Logicamente, nel caso di più widgets Facebook, occorre inserire il relativo codice javascript soltanto una volta.

Analizziamo il Waterfall generato da Speedoo

social widgets - caricamento asincrono

Test Speedoo completo: http://www.giorgiotave.it/speedoo/result/130614_FD_W/

Documento CompletatoCompletamente Caricato
Tempo CaricamentoPrimo ByteInizio RenderTempoRichiesteByteTempoRichiesteByte
Senza social widgets1.250s0.228s0.557s1.250s11150KB1.445s13152KB
Asincrono3.150s0.226s0.547s3.150s18565KB8.123s511,366KB

Innanzitutto notiamo che vi sono 38 richieste in più che rappresentano il 75% delle richieste totali.
Il tempo del completamento del documento è passato da 1,250 secondi3,150 secondi.
Il tempo del caricamento completo è balzato da 1,445 secondi a 8,123 secondi.
Il peso della pagine è aumentato da 152 KB a 1366 KB.

Un altro aspetto non immediato da constatare è che il tempo di caricamento delle due immagini (tigro.jpg e tigro-2.jpg) è aumentato passando dai circa 800 millisecondi ai circa 1700 millisecondi, poiché le risorse racchiuse nel primo riquadro (evidenziato in rosso) vengono scaricate in contemporanea alle due immagini.

Su un totale di 51 richieste, notiamo che 18 sono effettuate prima del completamento del documento e le restanti 33 dopo.
Nel primo riquadro ho racchiuso le richieste delle risorse, relative ai social widgets, che richiamiamo direttamente dalla nostra pagina.
Il secondo, invece, fa riferimento a tutte le risorse che vengono richiamate a loro volta dai widgets stessi.

Deferer

Questo metodo consiste nel ritardare il caricamento dei social widgets. Nel mio esempio effettuerò la chiamata degli script dopo che la pagina è completamente caricata e con “completamente” intendo dopo aver caricato immagini, css e javascript inclusi nel codice html.
Così facendo si evitano concorrenze di download tra le risorse “fondamentali” della nostra pagina e le risorse necessarie ai social widgets.
Per ottenere questo risultato è sufficiente affidarsi all’evento onload.

Apro una piccola parentesi per chiarire la differenza tra l’evento onload e, per esempio, il $(document).ready di jQuery:
quest’ultimo si verifica quando il solo documento HTML è caricato, mentre l’onload si verifica quando tutte le risorse richiamate (css, js, immagini, etc…) dal documento sono state completate.
// Add a script element as a child of the body
function socialWidgetsLoad() {
  //alert("La pagina è caricata. Adesso tocca ai socials!");
  //Facebook
  (function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = "//connect.facebook.net/it_IT/all.js#xfbml=1";
  fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));

  //Twitter
  !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');

  //Google+
  (function() {
  var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
  po.src = 'https://apis.google.com/js/plusone.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
  })();
}

if (window.addEventListener)
  window.addEventListener("load", socialWidgetsLoad, false);
else if (window.attachEvent)
  window.attachEvent("onload", socialWidgetsLoad);
else
  window.onload = socialWidgetsLoad;

Pagina del test: Caricamento social widgets – Defer

Vediamo come cambia il waterfall generato da Speedoo

social widgets - defer

Test Speedoo completo: http://www.giorgiotave.it/speedoo/result/130614_W5_T/

Innanzitutto notiamo che il tempo di caricamento delle due immagini è tornato “alla normalità” (riquadro 1).
Dal secondo riquadro si evince come il caricamento del widgets avviene dopo il completamento del documento (rappresentato dalla linea blu verticale)

Documento CompletatoCompletamente Caricato
Tempo CaricamentoPrimo ByteInizio RenderTempoRichiesteByteTempoRichiesteByte
Senza social widgets1.250s0.228s0.557s1.237s11150KB1.445s13152KB
Asincrono3.150s0.226s0.547s3.150s18565KB8.123s511,366KB
Defer1.237s0.221s0.535s1.237s11151BK6.569s511,367KB

Come si evince dalla tabella, il tempo, le richieste ed i bytes relativi al completamento del documento, sono ritornate ai valori della pagina che non faceva uso di social widgets. Rimangono quasi invariati invece i valori che si riferiscono al completamento del documento. In pratica abbiamo aumentato la velocità percepita dall’utente.

Lazy loading

Il lazy loading (traducendo letteralmente: caricamento pigro) è una tecnica molto importante per aumentare le performance dei siti web.
Consiste nel caricare le risorse solamente quando sono necessarie e si può applicare a qualunque risorsa: javascript, css, immagini, etc…

Non esiste una metodologia standard, possiamo decidere a nostro gusto ed in base alle nostre esigenze come effettuare il lazy loading.

Di seguito illustro due esempi:

Social widgets subito visibili (p.e.: posizionati in testa ad un articolo)

Se i social widgets sono subito visibili nella nostra pagina possiamo scegliere di mostrare inizialmente delle icone statiche e di richiamare le funzioni per il caricamento dei social widgets al verificarsi dell’evento onmouseover dell’elemento che li contiene

document.getElementById("socialwidgets").onmouseover=function(){
  socialWidgetsLoad();
  document.getElementById("socialwidgets").onmouseover=null;
};

Pagina del test: Caricamento social widgets – Lazy load (onmouseover)

Se un utente passa con il mouse sopra l’elemento con id=socialwidgets verrà richiamata la funzione socialWidgetsLoad (la medesima del punto precedente).

Social widgets non visibili inizialmente (p.e.: posizionati in fondo ad un articolo)

In questo caso possiamo decidere di caricare i social widgets solo e soltanto se l’utente effettua lo scrolling della pagina fino a quando il div che li contiene entra nella zona visibile della finestra.

function inViewport(el) {
  var top = el.offsetTop;
  var left = el.offsetLeft;
  var width = el.offsetWidth;
  var height = el.offsetHeight;

  while(el.offsetParent) {
    el = el.offsetParent;
    top += el.offsetTop;
    left += el.offsetLeft;
  }

  return (
    top < (window.pageYOffset + window.innerHeight) &&
    left < (window.pageXOffset + window.innerWidth) &&
    (top + height) > window.pageYOffset &&
    (left + width) > window.pageXOffset
  );
}

window.onscroll = function (e) {
  if(inViewport(document.getElementById("socialwidgets"))) {
    socialWidgetsLoad();
    window.onscroll = null;
  }
}

Pagina del test: Caricamento social widgets – Lazy load (onscroll)

L’impatto sul waterfall generato da Speedoo è sorprendente

Test Speedoo completo: http://www.giorgiotave.it/speedoo/result/130614_4X_X/

Documento CompletatoCompletamente Caricato
Tempo CaricamentoPrimo ByteInizio RenderTempoRichiesteByteTempoRichiesteByte
Senza social widgets1.250s0.228s0.557s1.237s11150KB1.445s13152KB
Asincrono3.150s0.226s0.547s3.150s18565KB8.123s511,366KB
Defer1.237s0.221s0.535s1.237s11151KB6.569s511,367KB
Lazy load1.262s0.226s0.559s1.262s11151KB1.521s13153KB

Praticamente tutti i valori risultano uguali a quelli iniziali: avremo un sito veloce come se non facesse uso di social widgets.
Logicamente al verificarsi dell’evento da noi scelto verranno scaricate le 38 risorse necessarie al funzionamento dei widgets, ma solo quando sono realmente indispensabili al corretto funzionamento, evitando così all’utente inutili download di files.

Le tecniche adottate sono soltanto esempi didattici, migliorabili e adattabili alle proprie esigenze.
Esistono diversi script javascript, plugins jQuery, plugins WordPress per attuare il lazy loading del social widgets.

Risorse

 

Scritto il


Sei membro del forum? Vuoi scrivere anche tu su SEO Blog gt
Chiedilo a @giorgiotave