Panoramica
L'utilizzo di Closure Compiler con un compilation_level
di ADVANCED_OPTIMIZATIONS
offre tassi di compressione migliori
rispetto alla compilazione con SIMPLE_OPTIMIZATIONS
o WHITESPACE_ONLY
. La compilazione
con ADVANCED_OPTIMIZATIONS
ottiene una compressione aggiuntiva
grazie a un approccio più aggressivo nella trasformazione del codice e nella ridenominazione
dei simboli. Tuttavia, questo approccio più aggressivo significa che devi
prestare maggiore attenzione quando utilizzi ADVANCED_OPTIMIZATIONS
per assicurarti che il codice di output funzioni allo stesso modo del
codice di input.
Questo tutorial illustra la funzione del livello di compilazione ADVANCED_OPTIMIZATIONS
e cosa puoi fare per assicurarti che il tuo codice funzioni dopo la compilazione con ADVANCED_OPTIMIZATIONS
. Introduce
anche il concetto di extern: un simbolo
definito in un codice esterno a quello elaborato dal compilatore.
Prima di leggere questo tutorial, devi avere familiarità con la procedura di compilazione di JavaScript con uno degli strumenti Closure Compiler, ad esempio l'applicazione di compilazione basata su Java.
Una nota sulla terminologia: il flag della riga di comando --compilation_level
supporta le abbreviazioni più comunemente utilizzate ADVANCED
e
SIMPLE
, nonché quelle più precise
ADVANCED_OPTIMIZATIONS
e SIMPLE_OPTIMIZATIONS
.
Questo documento utilizza la forma più lunga, ma i nomi possono essere utilizzati in modo intercambiabile nella
riga di comando.
- Compressione ancora migliore
- Come attivare ADVANCED_OPTIMIZATIONS
- Aspetti da considerare quando utilizzi ADVANCED_OPTIMIZATIONS
Compressione ancora migliore
Con il livello di compilazione predefinito
SIMPLE_OPTIMIZATIONS
, Closure Compiler riduce le dimensioni
di JavaScript rinominando le variabili locali. Esistono simboli
diversi dalle variabili locali che possono essere abbreviati, tuttavia, e
ci sono modi per ridurre il codice diversi dal cambio di nome dei simboli. La compilazione
con ADVANCED_OPTIMIZATIONS
sfrutta l'intera gamma di
possibilità di riduzione del codice.
Confronta gli output per SIMPLE_OPTIMIZATIONS
e ADVANCED_OPTIMIZATIONS
per il seguente
codice:
function unusedFunction(note) { alert(note['text']); } function displayNoteTitle(note) { alert(note['title']); } var flowerNote = {}; flowerNote['title'] = "Flowers"; displayNoteTitle(flowerNote);
La compilazione con SIMPLE_OPTIMIZATIONS
abbrevia il
codice in questo modo:
function unusedFunction(a){alert(a.text)}function displayNoteTitle(a){alert(a.title)}var flowerNote={};flowerNote.title="Flowers";displayNoteTitle(flowerNote);
La compilazione con ADVANCED_OPTIMIZATIONS
abbrevia completamente
il codice in questo modo:
alert("Flowers");
Entrambi gli script producono un avviso con il testo "Flowers"
,
ma il secondo script è molto più piccolo.
Il livello ADVANCED_OPTIMIZATIONS
va oltre il semplice
accorciamento dei nomi delle variabili in diversi modi, tra cui:
- Rinomina più aggressiva:
La compilazione con
SIMPLE_OPTIMIZATIONS
rinomina solo i parametrinote
delle funzionidisplayNoteTitle()
eunusedFunction()
, perché sono le uniche variabili nello script locali a una funzione.ADVANCED_OPTIMIZATIONS
rinomina anche la variabile globaleflowerNote
. - Rimozione del codice inutilizzato:
La compilazione con
ADVANCED_OPTIMIZATIONS
rimuove completamente la funzioneunusedFunction()
, perché non viene mai chiamata nel codice. - Inlining delle funzioni:
La compilazione con
ADVANCED_OPTIMIZATIONS
sostituisce la chiamata adisplayNoteTitle()
con il singoloalert()
che compone il corpo della funzione. Questa sostituzione di una chiamata di funzione con il corpo della funzione è nota come "inlining". Se la funzione fosse più lunga o più complicata, l'incorporamento potrebbe modificare il comportamento del codice, ma Closure Compiler determina che in questo caso l'incorporamento è sicuro e consente di risparmiare spazio. La compilazione conADVANCED_OPTIMIZATIONS
incorpora anche le costanti e alcune variabili quando determina di poterlo fare in modo sicuro.
Questo elenco è solo un campione delle trasformazioni che riducono le dimensioni
che la compilazione di ADVANCED_OPTIMIZATIONS
può eseguire.
Come attivare ADVANCED_OPTIMIZATIONS
Per abilitare ADVANCED_OPTIMIZATIONS
per l'applicazione Closure
Compiler, includi il flag della riga di comando
--compilation_level ADVANCED_OPTIMIZATIONS
, come nel
seguente comando:
java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js hello.js
Aspetti a cui prestare attenzione quando utilizzi ADVANCED_OPTIMIZATIONS
Di seguito sono elencati alcuni effetti indesiderati comuni di ADVANCED_OPTIMIZATIONS e i passaggi che puoi seguire per evitarli.
Rimozione del codice che vuoi conservare
Se compili solo la funzione riportata di seguito
con ADVANCED_OPTIMIZATIONS
, Closure Compiler produce
un output vuoto:
function displayNoteTitle(note) { alert(note['myTitle']); }
Poiché la funzione non viene mai chiamata nel codice JavaScript che passi al compilatore, Closure Compiler presuppone che questo codice non sia necessario.
In molti casi, questo comportamento è esattamente quello che vuoi. Ad esempio, se compili il codice insieme a una libreria di grandi dimensioni, Closure Compiler può determinare quali funzioni della libreria utilizzi effettivamente ed eliminare quelle che non utilizzi.
Se, tuttavia, noti che Closure Compiler rimuove funzioni che vuoi conservare, esistono due modi per impedirlo:
- Sposta le chiamate di funzione nel codice elaborato da Closure Compiler.
- Includi gli extern per le funzioni che vuoi esporre.
Le sezioni successive descrivono in dettaglio ciascuna opzione.
Soluzione: sposta le chiamate di funzione nel codice elaborato da Closure Compiler
Potresti riscontrare la rimozione indesiderata del codice se compili solo una parte
del codice con Closure Compiler. Ad esempio, potresti avere un file di libreria che
contiene solo definizioni di funzioni e un file HTML che include la
libreria e che contiene il codice che chiama queste funzioni. In questo
caso, se compili il file della libreria
con ADVANCED_OPTIMIZATIONS
, Closure Compiler rimuove
tutte le funzioni della libreria.
La soluzione più semplice a questo problema è compilare le funzioni
insieme alla parte del programma che le chiama.
Ad esempio, Closure Compiler non rimuoverà displayNoteTitle()
quando compila il seguente programma:
function displayNoteTitle(note) { alert(note['myTitle']); } displayNoteTitle({'myTitle': 'Flowers'});
La funzione displayNoteTitle()
non viene rimossa in questo caso perché Closure Compiler rileva che viene chiamata.
In altre parole, puoi impedire la rimozione di codice indesiderato includendo il punto di ingresso del tuo programma nel codice che passi a Closure Compiler. Il punto di ingresso di un programma è il punto del codice in cui inizia l'esecuzione del programma. Ad esempio, nel programma di note sui fiori della sezione precedente, le ultime tre righe vengono eseguite non appena il codice JavaScript viene caricato nel browser. Questo è il punto di ingresso per questo programma. Per determinare il codice da conservare, Closure Compiler inizia da questo punto di ingresso e traccia il flusso di controllo del programma in avanti da qui.
Soluzione: includi gli extern per le funzioni che vuoi esporre
Ulteriori informazioni su questa soluzione sono disponibili di seguito e nella pagina relativa a esterni ed esportazioni.
Nomi proprietà incoerenti
La compilazione di Closure Compiler non modifica mai i valori letterali stringa nel codice, indipendentemente dal livello di compilazione utilizzato. Ciò significa che la compilazione
con ADVANCED_OPTIMIZATIONS
tratta le proprietà in modo diverso
a seconda che il codice acceda a queste proprietà con una stringa. Se combini
riferimenti di stringa a una proprietà con riferimenti con sintassi punto, Closure Compiler
rinomina alcuni dei riferimenti a quella proprietà, ma non altri. Di conseguenza, il codice probabilmente non verrà eseguito correttamente.
Ad esempio, prendi in considerazione il seguente codice:
function displayNoteTitle(note) { alert(note['myTitle']); } var flowerNote = {}; flowerNote.myTitle = 'Flowers'; alert(flowerNote.myTitle); displayNoteTitle(flowerNote);
Le ultime due istruzioni di questo codice sorgente fanno esattamente la stessa
cosa. Tuttavia, quando comprimi il codice
con ADVANCED_OPTIMIZATIONS
, ottieni questo:
var a={};a.a="Flowers";alert(a.a);alert(a.myTitle);
L'ultima istruzione del codice compresso genera un errore. Il riferimento diretto alla proprietà myTitle
è stato rinominato in a
, ma il riferimento tra virgolette a myTitle
all'interno della funzione displayNoteTitle
non è stato rinominato. Di conseguenza, l'ultima affermazione si riferisce
a una proprietà myTitle
che non esiste più.
Soluzione: utilizza nomi coerenti per le proprietà
Questa soluzione è piuttosto semplice. Per un determinato tipo o oggetto, utilizza esclusivamente la sintassi con il punto o le stringhe tra virgolette. Non mescolare le sintassi, soprattutto in riferimento alla stessa proprietà.
Inoltre, se possibile, preferisci utilizzare la sintassi con il punto, in quanto supporta controlli e ottimizzazioni migliori. Utilizza l'accesso alle proprietà delle stringhe tra virgolette solo quando non vuoi che Closure Compiler esegua la ridenominazione, ad esempio quando il nome proviene da una fonte esterna, come JSON decodificato.
Compilazione separata di due porzioni di codice
Se dividi l'applicazione in diversi blocchi di codice, potresti voler compilare i blocchi separatamente. Tuttavia, se due blocchi di codice interagiscono, ciò potrebbe causare difficoltà. Anche se riesci, l'output delle due esecuzioni di Closure Compiler non sarà compatibile.
Ad esempio, supponiamo che un'applicazione sia divisa in due parti: una che recupera i dati e una che li visualizza.
Ecco il codice per recuperare i dati:
function getData() { // In an actual project, this data would be retrieved from the server. return {title: 'Flower Care', text: 'Flowers need water.'}; }
Ecco il codice per visualizzare i dati:
var displayElement = document.getElementById('display'); function displayData(parent, data) { var textElement = document.createTextNode(data.text); parent.appendChild(textElement); } displayData(displayElement, getData());
Se provi a compilare questi due blocchi di codice separatamente, riscontrerai
diversi problemi. Innanzitutto, Closure Compiler rimuove
la funzione getData()
per i motivi descritti
in Rimozione del codice che vuoi conservare. In secondo luogo,
Closure Compiler genera un errore irreversibile durante l'elaborazione del codice
che mostra i dati.
input:6: ERROR - variable getData is undefined displayData(displayElement, getData());
Poiché il compilatore non ha accesso alla funzione getData()
quando compila il codice che mostra i dati, considera
getData
come non definito.
Soluzione: compila tutto il codice per una pagina
Per garantire una compilazione corretta, compila tutto il codice di una pagina insieme in un'unica esecuzione di compilazione. Closure Compiler può accettare più file JavaScript e stringhe JavaScript come input, in modo da poter trasferire il codice della libreria e altro codice insieme in un'unica richiesta di compilazione.
Nota: questo approccio non funziona se devi combinare codice compilato e non compilato. Consulta la sezione Riferimenti interrotti tra codice compilato e non compilato per suggerimenti su come gestire questa situazione.
Riferimenti interrotti tra codice compilato e non compilato
La ridenominazione dei simboli in ADVANCED_OPTIMIZATIONS
interromperà
la comunicazione tra il codice elaborato da Closure Compiler e qualsiasi
altro codice. La compilazione rinomina le funzioni definite nel codice sorgente. Qualsiasi codice esterno che chiama le tue funzioni non funzionerà dopo la compilazione, perché fa ancora riferimento al vecchio nome della funzione. Allo stesso modo,
i riferimenti nel codice compilato a simboli definiti esternamente potrebbero essere
modificati da Closure Compiler.
Tieni presente che il "codice non compilato" include qualsiasi codice passato alla funzione eval()
come stringa. Closure Compiler non modifica mai
i valori letterali stringa nel codice, quindi non cambia le stringhe passate
alle istruzioni eval()
.
Tieni presente che si tratta di problemi correlati ma distinti: mantenere la comunicazione compilata-esterna e mantenere la comunicazione esterna-compilata. Questi problemi separati hanno una soluzione comune, ma ci sono sfumature per ogni aspetto. Per ottenere il massimo da Closure Compiler è importante capire di quale caso si tratta.
Prima di continuare, ti consigliamo di acquisire familiarità con extern e esportazioni.
Soluzione per chiamare codice esterno da codice compilato: compilazione con extern
Se utilizzi il codice fornito nella pagina da un altro script, devi assicurarti che Closure Compiler non rinomini i riferimenti ai simboli definiti in quella libreria esterna. A tal fine, includi un file contenente gli extern per la libreria esterna nella compilazione. In questo modo, Closure Compiler saprà quali nomi non controlli e che quindi non possono essere modificati. Il codice deve utilizzare gli stessi nomi utilizzati dal file esterno.
Esempi comuni sono le API come l'API OpenSocial e l'API Google Maps. Ad esempio, se
il tuo codice chiama la funzione OpenSocial opensocial.newDataRequest()
,
senza gli extern appropriati, Closure Compiler trasformerà
questa chiamata in a.b()
.
Soluzione per chiamare codice compilato da codice esterno: implementazione di extern
Se hai codice JavaScript che riutilizzi come libreria, potresti voler utilizzare Closure Compiler per ridurre solo la libreria, consentendo comunque al codice non compilato di chiamare le funzioni nella libreria.
La soluzione in questa situazione è implementare un insieme di extern che definiscono l'API pubblica della tua libreria. Il tuo codice fornirà definizioni per i simboli dichiarati in questi extern. Ciò significa qualsiasi classe o funzione menzionata dai tuoi esterni. Potrebbe anche significare che le tue classi implementano interfacce dichiarate negli extern.
Questi esterni sono utili anche per gli altri, non solo per te. I consumatori della tua libreria dovranno includerli se compilano il codice, poiché la tua libreria rappresenta uno script esterno dal loro punto di vista. Pensa agli esterni come al contratto tra te e i tuoi consumatori, entrambi avete bisogno di una copia.
A questo scopo, assicurati che durante la compilazione del codice vengano inclusi anche gli extern. Potrebbe sembrare insolito, dato che spesso pensiamo agli extern come "provenienti da un'altra posizione", ma è necessario comunicare a Closure Compiler quali simboli stai esponendo, in modo che non vengano rinominati.
Un avvertimento importante è che potresti ricevere diagnostica di "definizione duplicata" relativa al codice che definisce i simboli extern. Closure Compiler presuppone che qualsiasi simbolo negli extern sia fornito da una libreria esterna e al momento non riesce a capire che stai fornendo intenzionalmente una definizione. Questi messaggi diagnostici possono essere eliminati e l'eliminazione può essere considerata una conferma che stai effettivamente soddisfacendo la tua API.
Inoltre, Closure Compiler può verificare il tipo delle definizioni in modo che corrispondano ai tipi delle dichiarazioni extern. In questo modo, avrai un'ulteriore conferma che le tue definizioni sono corrette.