Ancora più efficienti con il Code Splitting

Nella ricerca di un’applicazione web dalle perfette prestazioni, abbiamo avuto modo di testare varie tecnologie che si sono rivelate di grande aiuto.

Tra queste Vue Router che, grazie al suo uso intelligente dei componenti Vue per la manipolazione dell’URL di pagina, permette di creare una Single Page Application con prestazioni elevate e con il feeling di un sito multipagina.
L’aumento delle performances è stato talmente significativo che, fin da subito, abbiamo focalizzato l’attenzione su quali altri interventi avremmo potuto effettuare per continuare a migliorarle: l’ispirazione è arrivata da Nuxt.

Uno dei punti forti di Nuxt è l’utilizzo del Code Splitting per Javascript, ovvero la suddivisione intelligente del codice in piccoli file .js, che saranno poi caricati dinamicamente nell’applicazione solo quando effettivamente necessario.

Integrazione con Laravel

Nel nostro precedente articolo Come abbiamo scelto Laravel, vi abbiamo raccontato perché adottiamo Laravel come nostro principale framework per applicazioni che richiedono un backend custom; in questo vi mostreremo come mettere in pratica la tecnica del Code Splitting in un progetto che integra Vue Router in Laravel.

Il boilerplate di Laravel, di fatto, possiede già quasi tutte le risorse necessarie per cominciare da subito con la nuova tecnica, con un unico tweak da fare a Babel: Code Splitting è basato interamente sulla funzionalità di Dynamic Import introdotta da ES2015, che attualmente non è nativamente supportata da Babel.

Un semplice plugin risolve il problema.

Installiamo quindi la libreria syntax-dynamic-import tra le nostre devDependencies:

$ npm install --save-dev babel-plugin-syntax-dynamic-import

# Oppure, utilizzando yarn

$ yarn add -D babel-plugin-syntax-dynamic-import

E diciamo a Babel di usare il plugin modificando il file .babelrc:

{
"plugins": ["syntax-dynamic-import"]
}

Via col Code Splitting!

Immaginiamo la cartella dei nostri file JavaScript del progetto così strutturata:

/src/js
+-- routes.js
+-- components
| +-- Dashboard.vue
| +-- Profile.vue
+-- ...

Tutte le nostre routes vengono definite all’interno di routes.js, ed è proprio qui che avviene la magia.

Normalmente, per importare un file con ES2015, si dovrebbe utilizzare la scrittura:

import Dashboard from './components/Dashboard.vue'
import Profile from './components/Profile.vue'

Per fare Code Splitting, dobbiamo trasformare gli import in funzioni che ritornano delle Promise contenenti il codice JavaScript necessario:

// Usiamo le arrow function di ES2015
// e la sintassi import viene trattata
// come se fosse una funzione
const Dashboard = () => import('./components/Dashboar.vue')
const Profile = () => import('./components/Profile.vue')

Utilizzando la sintassi precedente otteniamo degli import dinamici, ovvero che richiamano il codice solo quando strettamente necessario.

Manca ancora il passaggio fondamentale: esportare il codice in vari file separati.

Giunti a questo punto, Webpack arriva in nostro soccorso: andiamo ad inserire il commento /* webpackChunckName "js/nomefile" */ all’interno degli import per estrarre il JavaScript necessario:

const Dashboard = () => import(/* webpackChunckName "js/dashboard" */ './components/Dashboard.vue')
const Profile = () => import(/* webpackChunckName "js/profile" */ './components/Profile.vue')

Et Voilà! Il codice è pronto. Non c’è alcuna modifica da fare, e le nostre routes sono definite nella maniera classica definita da Vue Router:

export default {
{
path: '/dashboard',
component: Dashboard
},
{
path: '/profile',
component: Profile
}
}

Cosa succede?

Lanciando la compilazione con yarn dev, Webpack genererà i file js/dashboard.js e js/profile.js oltre ai necessari vendor.js, manifest.js e app.js.

Immaginate un utente che visita per la prima volta il nostro sito all’indirizzo https://www.example.com/dashboard.

Analizzando la Network Tab dei Dev Tools, possiamo notare che saranno scaricati i file vendor.js, app.js, manifest.js e dashboard.js. Di profile.js non c’è traccia.

Quando l’utente si sposta su /profile tramite un router-link, viene scaricato il file profile.js che è diventato fondamentale per la visualizzazione corretta della pagina.

Con questo approccio non viene scaricato nulla che non sia necessario, alleviando il carico al primo caricamento del sito.

Vantaggi per l’HTTP2

Un vantaggio enorme si ottiene utilizzando la tecnica del Code Splitting in un’applicazione che utilizza HTTP2.

Utilizzando il plugin BundleAnalyzer per Webpack, possiamo generare una mappa interattiva con FoamTree, una pratica che riteniamo oramai fondamentale per capire esattamente quanto pesano le dipendenze di ogni singolo file JavaScript.

vendor.js spicca sempre tra tutti come il più pesante. Facendo un’analisi è possibile individuare i contenuti più pesanti ed estrarli in singoli file, sfruttando così le abilità di download Multiplex di HTTP2 e l’utente non dovrà più scaricare un unico file da 2Mb, ma ad esempio 5 file da 400Kb o 10 file da 200Kb contemporaneamente.

Attenzione alle dipendenze in comune!

Abbiamo analizzato i file compilati con BundleAnalyzer e abbiamo notato un problema: le dipendenze in comune nelle varie pagine, se non estratte, devono essere copiate in ogni singolo file per poter funzionare.

Nel nostro caso, si tratta di un componente Vue che abbiamo scelto di non includere nel file vendor.js.

Per risolvere il problema, abbiamo registrato il componente globalmente con un import classico all’interno del file .js globale.

Happy Splitting!