Compilation avanzata

Panoramica

L'utilizzo di Closure Compiler con compilation_level di ADVANCED_OPTIMIZATIONS offre tassi di compressione migliori rispetto alla compilazione con SIMPLE_OPTIMIZATIONS o WHITESPACE_ONLY. La compilazione con ADVANCED_OPTIMIZATIONS raggiunge una maggiore compressione rendendole più aggressive nel modo in cui trasforma il codice e rinomina i 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 il funzionamento del livello di compilazione ADVANCED_OPTIMIZATIONS e cosa puoi fare per assicurarti che il tuo codice funzioni dopo la compilazione con ADVANCED_OPTIMIZATIONS. Inoltre, introduce il concetto di extern: un simbolo definito nel codice esterno al codice elaborato dal compilatore.

Prima di leggere questo tutorial, dovresti conoscere la procedura per la compilazione di JavaScript con uno degli strumenti di chiusura dei componenti (l'interfaccia utente del servizio di compilazione, l'API del servizio di compilazione o l'applicazione di compilazione).

Una nota sulla terminologia: il flag della riga di comando --compilation_level supporta le abbreviazioni più comunemente utilizzate ADVANCED e SIMPLE, nonché i parametri più precisi ADVANCED_OPTIMIZATIONS e SIMPLE_OPTIMIZATIONS. Questo documento utilizza il formato più lungo, ma i nomi possono essere utilizzati in modo intercambiabile dalla riga di comando.

  1. Compressione ancora migliore
  2. Come abilitare ADVANCED_OPTIMIZATIONS
  3. A cosa prestare attenzione quando si utilizzano le impostazioni ADVANCED_OPTIMIZATIONS
    1. Rimozione del codice che vuoi conservare
    2. Nomi di proprietà non coerenti
    3. Compilare due parti di codice separatamente
    4. Riferimenti non funzionanti tra il codice compilato e il codice non compilato

Compressione ancora migliore

Con il livello di compilazione predefinito SIMPLE_OPTIMIZATIONS, il compilatore Closure riduce le dimensioni di JavaScript rinominando le variabili locali. Tuttavia, esistono simboli diversi dalle variabili locali che possono essere abbreviati e esistono modi per ridurre il codice oltre ai simboli di ridenominazione. La compilazione con ADVANCED_OPTIMIZATIONS sfrutta tutta la gamma di possibilità di restringimento del codice.

Confronta gli output di 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 accorcia completamente il codice in questo modo:

alert("Flowers");

Entrambi questi script generano un avviso di lettura per "Flowers", ma il secondo è molto più piccolo.

Il livello ADVANCED_OPTIMIZATIONS va oltre la semplice abbreviazione dei nomi delle variabili in diversi modi, tra cui:

  • rinominazione più aggressiva:

    La compilazione con SIMPLE_OPTIMIZATIONS rinomina solo i parametri note delle funzioni displayNoteTitle() e unusedFunction(), perché queste sono le uniche variabili nello script che sono locali per una funzione. ADVANCED_OPTIMIZATIONS rinomina anche la variabile globale flowerNote.

  • rimozione di codici morti:

    La compilazione con ADVANCED_OPTIMIZATIONS rimuove completamente la funzione unusedFunction(), perché non viene mai chiamata nel codice.

  • della funzione:

    La compilazione con ADVANCED_OPTIMIZATIONS sostituisce la chiamata a displayNoteTitle() con il singolo alert() che compone il corpo della funzione. La sostituzione di una chiamata funzione con il corpo della funzione è nota come "in linea". Se la funzione era più lunga o più complicata, l'incorporamento potrebbe modificare il comportamento del codice, ma il compilatore Closure determina che in questo caso l'incorporamento è sicuro e consente di risparmiare spazio. La compilazione con ADVANCED_OPTIMIZATIONS include anche costanti e alcune variabili quando determina che può farlo in sicurezza.

Questo elenco è solo un esempio delle trasformazioni di riduzione delle dimensioni che possono essere eseguite dalla compilazione ADVANCED_OPTIMIZATIONS.

Come abilitare ADVANCED_OPTIMIZATIONS

L'interfaccia utente, l'API e l'applicazione del servizio Closure Compiler hanno tutti metodi diversi per impostare compilation_level su ADVANCED_OPTIMIZATIONS.

Come abilitare ADVANCED_OPTIMIZATIONS nell'interfaccia utente del servizio Closure Compiler

Per attivare ADVANCED_OPTIMIZATIONS per l'UI del servizio di chiusura dei suggerimenti, fai clic sul pulsante di opzione "Avanzate".

Come abilitare ADVANCED_OPTIMIZATIONS nell'API Closure Compiler Service

Per abilitare ADVANCED_OPTIMIZATIONS per l'API di servizio Closure Compiler, includi un parametro di richiesta denominato compilation_level con un valore ADVANCED_OPTIMIZATIONS, come nel seguente programma python:

#!/usr/bin/python2.4

import httplib, urllib, sys

params = urllib.urlencode([
    ('code_url', sys.argv[1]),
    ('compilation_level', 'ADVANCED_OPTIMIZATIONS'),
    ('output_format', 'text'),
    ('output_info', 'compiled_code'),
  ])

headers = { "Content-type": "application/x-www-form-urlencoded" }
conn = httplib.HTTPSConnection('closure-compiler.appspot.com')
conn.request('POST', '/compile', params, headers)
response = conn.getresponse()
data = response.read()
print data
conn.close()

Come attivare ADVANCED_OPTIMIZATIONS nell'applicazione Closure Compiler

Per abilitare ADVANCED_OPTIMIZATIONS per l'applicazione del componente Closure, includi il flag della riga di comando --compilation_level ADVANCED_OPTIMIZATIONS, nel seguente comando:

java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js hello.js

A cosa prestare attenzione quando si utilizzano ADVANCED_OPTIMIZATIONS

Di seguito sono elencati alcuni effetti indesiderati comuni di ADVANCED_OPTIMIZATIONS e i passaggi da 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 passato al componente di compilazione, il componente Closure suppone 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 vengono effettivamente utilizzate e rimuovere quelle che non utilizzi.

Se, tuttavia, ti accorgi che Closure Compiler rimuove le funzioni che vuoi conservare, puoi procedere in due modi:

  • Sposta le chiamate delle funzioni nel codice elaborato da Closure Compiler.
  • Includi le entità esterne per le funzioni che vuoi esporre.

Nelle sezioni successive vengono descritte nel dettaglio ciascuna opzione.

Soluzione: spostare le chiamate funzioni nel codice elaborato dal compilatore di chiusura

Potresti riscontrare la rimozione di codice indesiderato solo se compili parte del codice con Closure Compiler. Ad esempio, potresti avere un file libreria che contiene solo le definizioni delle 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é il componente Closure Compiler rileva che è stata chiamata.

In altre parole, puoi impedire la rimozione indesiderata del codice includendo il punto di ingresso del tuo programma nel codice passato al Closure Compiler. Il punto di ingresso di un programma è il punto del codice in cui inizia l'esecuzione del programma. Ad esempio, nel programma nota a fiori della sezione precedente, le ultime tre righe vengono eseguite non appena il codice JavaScript viene caricato nel browser. Questo è il punto di accesso a questo programma. Per determinare quale codice devi mantenere, Closure Compiler inizia da questo punto di ingresso e traccia il flusso di controllo del programma da lì.

Soluzione: includi gli elementi esterni nelle 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 del compilatore di chiusura non modifica mai i valori letterali della stringa nel codice, indipendentemente dal livello di compilazione utilizzato. Ciò significa che la compilazione con ADVANCED_OPTIMIZATIONS gestisce le proprietà in modo diverso a seconda che il codice vi acceda con una stringa. Se combini i riferimenti stringa a una proprietà con riferimenti a punti-sintassi, il componente Closure Compiler rinomina alcuni dei riferimenti a quella proprietà, ma non altri. Di conseguenza, probabilmente il tuo codice non verrà eseguito correttamente.

Ad esempio, prendi il seguente codice:

function displayNoteTitle(note) {
  alert(note['myTitle']);
}
var flowerNote = {};
flowerNote.myTitle = 'Flowers';

alert(flowerNote.myTitle);
displayNoteTitle(flowerNote);

Le ultime due istruzioni nel 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 nel codice compresso genera un errore. Il riferimento diretto alla proprietà myTitle è stato rinominato in a, ma il riferimento citato a myTitle all'interno della funzione displayNoteTitle non è stato rinominato. Di conseguenza, l'ultima istruzione si riferisce a una proprietà myTitle che non è più presente.

Soluzione: essere coerente nei nomi delle proprietà

Questa soluzione è molto semplice. Per un determinato tipo o oggetto, utilizza esclusivamente stringhe di sintesi vocale o stringhe tra virgolette. Non mescolare le sintassi, soprattutto in riferimento alla stessa proprietà.

Inoltre, quando possibile, preferisci utilizzare la sintesi vocale, poiché supporta controlli e ottimizzazioni migliori. Utilizza l'accesso alla proprietà della stringa tra virgolette solo quando non vuoi che il componente Closure Compiler esegua la ridenominazione, ad esempio quando il nome proviene da un'origine esterna, come JSON decodificato.

Compilare due porzioni di codice separatamente

Se suddividi la tua applicazione in blocchi di codice diversi, potresti volerli compilare separatamente. Tuttavia, se due blocchi di codice interagiscono, può causare difficoltà nel farlo. Anche se l'operazione ha esito positivo, l'output delle due esecuzioni del compilatore di chiusura non sarà compatibile.

Supponiamo, ad esempio, che un'applicazione sia suddivisa in due parti: una che recupera i dati e una che mostra i dati.

Ecco il codice per il recupero dei 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 tenti di compilare questi due blocchi di codice separatamente, riscontrerai diversi problemi. Innanzitutto, il compilatore chiude la funzione getData() per i motivi descritti nella sezione Rimozione del codice che vuoi conservare. In secondo luogo, il compilatore Closure 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: compilare tutto il codice per una pagina

Per garantire la compilazione corretta, compila tutto il codice di una pagina in un'unica esecuzione di compilazione. Il compilatore può accettare più file JavaScript e stringhe JavaScript come input, quindi puoi trasferire il codice della libreria e altri codici in una singola richiesta di compilazione.

Nota: questo approccio non funziona se devi combinare codice compilato e non compilato. Per suggerimenti su come gestire questa situazione, consulta la sezione Riferimenti non funzionanti tra codice compilato e non compilato.

Riferimenti non funzionanti tra compilato e codice non compilato

La ridenominazione del simbolo in ADVANCED_OPTIMIZATIONS comporterà l'interruzione della comunicazione tra codice elaborato dal componente Closure Compiler e qualsiasi altro codice. La compilazione rinomina le funzioni definite nel codice sorgente. Il codice esterno che chiama le tue funzioni verrà interrotto dopo la compilazione, perché fa ancora riferimento al nome della funzione precedente. Analogamente, i riferimenti nel codice compilato ai simboli definiti esternamente potrebbero essere alterati da Closure Compiler.

Tieni presente che "codice non compilato" include qualsiasi codice passato alla funzione eval() come stringa. Closure Compiler non modifica mai i valori letterali delle stringhe nel codice, quindi Closure Compiler non modifica le stringhe passate alle istruzioni eval().

Tieni presente che si tratta di problemi correlati ma distinti: il mantenimento della comunicazione compilata-esterna e la gestione della comunicazione da esterno a compilato. Questi problemi separati hanno una soluzione comune, ma hanno sfumature da ogni parte. Per ottenere il massimo da Closure Compiler, è importante capire quale sia la tua richiesta.

Prima di continuare, è utile acquisire familiarità con esteri ed esportazioni.

Soluzione per chiamare il codice esterno dal codice compilato: compilazione con esterni

Se utilizzi il codice fornito nella tua pagina da un altro script, devi assicurarti che il componente Closure Compiler non rinomini i riferimenti ai simboli definiti in quella libreria esterna. Per farlo, includi nella compilation un file contenente le estensioni per la libreria esterna. In questo modo indichi a Closure Compiler quali nomi non sono sotto il tuo controllo e quindi non è possibile modificarli. Il codice deve utilizzare gli stessi nomi utilizzati dal file esterno.

Alcuni esempi sono le API come l'API OpenSocial e l'API Google Maps. Ad esempio, se il codice chiama la funzione OpenSocial opensocial.newDataRequest(), senza le estensioni esterne appropriate, il componente Closure trasformerà questa chiamata in a.b().

Soluzione per chiamare il codice compilato da Codice esterno: implementare le entità esterne

Se hai codice JavaScript che riutilizzi come libreria, ti consigliamo di utilizzare il componente Closure Compiler per ridurre solo la libreria, pur consentendo il codice non compilato per chiamare le funzioni nella libreria.

La soluzione in questo caso è l'implementazione di un insieme di elementi esterni che definiscano l'API pubblica della tua libreria. Il codice fornirà le definizioni per i simboli dichiarati in questi elementi esterni. Questo significa che qualsiasi classe o funzione menzionata dai tuoi exteri. Potrebbe anche significare che le tue classi implementano le interfacce dichiarate negli elementi esterni.

Questi dettagli sono utili anche per gli altri, non solo per te. I consumatori della tua libreria dovranno includerli se compilano il proprio codice, poiché la tua libreria rappresenta uno script esterno dal loro punto di vista. Pensa agli esterni come a un contratto tra te e i tuoi consumatori, entrambi richiedono una copia.

A tal fine, assicurati di includere nel codice quando compili il codice. Questo può sembrare insolito, dal momento che spesso pensiamo che le sovrapposizioni siano "in arrivo da un'altra parte", ma è necessario comunicare a Closure Compiler i simboli che stai esponendo, quindi non vengono rinominati.

Un'importante precisazione da ricordare è che potreste ricevere diagnosi di "definizione duplicata" riguardanti il codice che definisce i simboli esterni. Il dispositivo di chiusura supponendo che qualsiasi simbolo negli esterni sia fornito da una libreria esterna e che al momento non sia in grado di capire che tu stia fornendo intenzionalmente una definizione. Questa diagnostica è sicura da sopprimere e puoi pensare alla soppressione come una conferma del completamento delle tue API.

Inoltre, il compilatore Closure potrebbe controllare che le definizioni corrispondano ai tipi di dichiarazioni esterne. Ciò fornisce un'ulteriore conferma che le definizioni siano corrette.