Minificatore JavaScript
Comprimi il codice JavaScript rimuovendo commenti, spazi e caratteri inutili.
Informazioni sulla minificazione JavaScript
JavaScript è il tipo di risorsa più pesante sulla maggior parte delle pagine web, e non solo perché tende ad essere il file più grande. Il costo è in cinque fasi: download (sulla rete), parse (il motore legge i byte e costruisce un AST), compile (V8 o JavaScriptCore compila l'AST in bytecode), execute (il bytecode viene eseguito), e nelle visite successive, warm-start dal bytecode in cache se il motore l'ha conservato. Brotli sull'edge della CDN gestisce il costo del download. La minificazione aiuta anche con il download, ma aiuta anche con il costo di parse e compile su ogni dispositivo che scarica il tuo script, e su un telefono Android di fascia bassa, parse e compile possono richiedere più tempo del trasferimento di rete. Gli articoli Cost of JavaScript di Addy Osmani hanno ripetutamente mostrato che quello che sembra un problema di rete è spesso un problema di CPU, e che la riduzione del 20-40% dei byte della minificazione si traduce abbastanza direttamente in millisecondi rasati dal tempo di parse sulla coda lunga dei dispositivi lenti.
L'ottimizzazione lazy parsing di V8 rimanda il parsing completo di qualsiasi funzione fino a quando non viene effettivamente chiamata, il che significa che un grande script con molte funzioni non utilizzate costa meno di quanto il suo conteggio di byte suggerisca. La cache HTTP di Chrome memorizza anche il bytecode V8 (il "Code Cache") quindi le visite ripetute saltano completamente la fase di parse-and-compile. Niente di tutto questo cambia l'equazione di base: gli script più piccoli colpiscono la cache calda più velocemente, parsano più velocemente nel percorso freddo, e compilano più velocemente. La minificazione è il posto più economico nell'intero stack per recuperare millisecondi.
I quattro (o cinque) livelli di minificazione JavaScript
Non tutti i minificatori sono uguali, e il livello di cui hai bisogno dipende dalla tua codebase. Livello 1: rimozione di spazi bianchi e commenti. Il passaggio più semplice, rimuovere spazi, tab, newline e commenti // ... / /* ... */. Questo è ciò che un passaggio regex può fare in sicurezza (con cura intorno a stringhe, template literal e regex literal). Riduzione tipica: 20-40% in byte grezzi. Livello 2: rinomina dei simboli (mangling). Nomi di variabili locali, parametri di funzione e (con cura) nomi di funzione vengono riscritti in singole lettere: function calculateTotal(itemList) diventa function a(b). Questo richiede un Abstract Syntax Tree e un'analisi completa dello scope per sapere quali nomi sono sicuri da rinominare, nomi globali, esportazioni, riferimenti alle proprietà di this, e qualsiasi cosa raggiunta tramite accesso con chiave stringa (obj['name']) devono tutti rimanere intatti. Riduzione aggiuntiva tipica: 10-25%. Livello 3: eliminazione del codice morto (tree shaking). L'analisi statica a livello di modulo identifica importazioni e percorsi di codice che non vengono mai eseguiti e li rimuove. Richiede informazioni di tipo a livello di modulo e una chiara comprensione degli effetti collaterali. Riduzione aggiuntiva tipica: variabile, ma può essere enorme su librerie che spediscono molte funzionalità. Livello 4: inlining e constant folding. Espressioni semplici come 2 * 60 * 60 vengono valutate al build time in 7200; piccole funzioni chiamate una sola volta possono essere inlinizzate nel loro chiamante. Livello 5: property mangling. L'ottimizzazione più aggressiva, anche i nomi delle proprietà degli oggetti vengono riscritti. Rompe qualsiasi codice che usa chiavi stringa (obj['name'] vs obj.name) o che espone i nomi delle proprietà come parte del suo contratto pubblico. Quasi sempre opt-in e quasi sempre limitato a identificatori specifici tramite una regex --mangle-props.
Una breve storia degli strumenti di minificazione JavaScript
JSMin (2003). Douglas Crockford, sì, lo stesso Crockford che ha promosso JSON, ha scritto JSMin in C nel 2003. Era un piccolo programma a file singolo che faceva la rimozione base di spazi bianchi e commenti senza AST, senza analisi di scope, e con un approccio deliberatamente conservativo ai casi limite di ASI (Automatic Semicolon Insertion). Ha stabilito l'asticella per "la cosa più semplice che funziona" ed è l'antenato spirituale di ogni minificatore JS basato su regex da allora. YUI Compressor (2007). Julien Lecomte di Yahoo ha annunciato YUI Compressor l'11 agosto 2007. Usava il tokeniser di Rhino per fare rinomina sicura dei simboli, il primo strumento Java ampiamente usato a fare vero mangling basato su AST per JavaScript. Closure Compiler (2009). Google lo stava usando internamente dal 2005; lo hanno rilasciato come open source sotto Apache 2.0 il 5 novembre 2009. Closure era l'ottimizzatore più aggressivo dell'epoca, type-aware tramite annotazioni JSDoc, con una modalità "advanced" che poteva riscrivere i nomi delle proprietà basandosi sull'inferenza di tipo. Il compromesso era che dovevi scrivere il tuo codice in modo Closure-friendly; i fallimenti della modalità advanced erano famigerati.
UglifyJS (~2010-2012). UglifyJS di Mihai Bazon è stato il primo minificatore JavaScript-nativo, scritto in JS, eseguito su Node.js, ed è diventato il default npm per un decennio. UglifyJS 2 ha aggiunto il supporto source map e le funzionalità ES5; UglifyJS 3 ha seguito con continuo polish ES5 ma non ha mai pienamente ottenuto il supporto ES6+. Terser (agosto 2018). Fabricio Matté ha forkato UglifyJS in Terser specificamente per aggiungere il supporto ES6+ senza interrompere la API UglifyJS stabile da tempo. Terser è ora il minificatore JS predefinito in webpack 5, Rollup, Parcel 1, e le vecchie versioni di Next.js. swc (2017/2019). swc ("Speedy Web Compiler") di Donny "kdy1" Choi è un compilatore JavaScript/TypeScript basato su Rust con un minificatore integrato 20-70 volte più veloce di Terser. Next.js ha cambiato il suo minificatore predefinito da Terser a swc a partire dalla versione 12 nell'ottobre 2021. esbuild (inverno 2019-2020). Evan Wallace, il cofondatore di Figma, ha rilasciato esbuild durante le festività invernali 2019-2020. Scritto in Go, è 10-100 volte più veloce dei bundler basati su JavaScript del giorno e spedisce il proprio minificatore. esbuild è ora il minificatore sottostante in Vite, in tsup, e in molti template di framework. La direzione generale negli ultimi cinque anni è stata: parser scritto in un linguaggio di sistema veloce (Rust o Go), ottimizzazioni basate su AST, tree shaking ES module-aware intelligente. Il minificatore regex incollato nel browser, come questo strumento, si trova nella parte inferiore di quella scala, facendo il lavoro più semplice che è ancora utile.
Source map per JavaScript minificato
Una source map è un file sidecar JSON che mappa le posizioni nell'output minificato alle posizioni nella sorgente originale. La specifica Source Map V3 è stata redatta da John Lenz (Google) e Nick Fitzgerald (Mozilla) nel 2011, ed è stata adottata da TC39 come ECMA-426 nel giugno 2024, lo stesso formato source-map si applica sia a JavaScript che a CSS. I browser consumano la mappa tramite un commento finale nel file minificato: //# sourceMappingURL=app.js.map. Quando DevTools è aperto e la mappa viene recuperata, il pannello Sources mostra la sorgente originale, con breakpoint, errori della console e stack trace che si riferiscono tutti ad essa. I minificatori di produzione (Terser, swc, esbuild, Closure) emettono tutti source map su richiesta. Questo strumento no, per uno strumento one-shot in-browser che restituisce testo piuttosto che una coppia di file scaricabili, le source map aggiungono complessità significativa per un beneficio marginale. La rivelazione onesta è che questo strumento è un passaggio a senso unico; i minificatori di build-pipeline hanno un caso molto più forte per le source map perché le sorgenti originali si trovano su disco e lo sviluppatore ha bisogno di debuggare in runtime.
Default dei bundler moderni, la maggior parte delle persone ha già un minificatore
Se stai usando una pipeline di build moderna, il tuo minificatore è già in esecuzione. webpack 5 usa terser-webpack-plugin con Terser di default. Vite usa esbuild per la minificazione di default; Lightning CSS per il CSS. Parcel usa swc. Next.js è passato da Terser a swc in v12 (ottobre 2021), e da Babel a swc per l'intera pipeline di build nella stessa versione. Remix, Astro, SvelteKit, Nuxt, Rollup, esbuild standalone, tutti includono la minificazione nelle build di produzione senza intervento dello sviluppatore. Il risultato è che per chiunque usi una pipeline di build moderna, la minificazione JS avviene automaticamente con ottimizzazioni ben oltre quello che uno strumento regex a file singolo può fare. I casi in cui questo minificatore in-browser si guadagna il suo posto: pagine HTML scritte a mano con blocchi <script> inline; temi WordPress spediti senza una toolchain Node; generatori di siti statici che non includono la minificazione; frammenti una tantum incollati in un CMS o template email; esperimenti rapidi dove configurare una pipeline di build richiederebbe più tempo dello script stesso.
Ambito onesto: cosa fa e cosa non fa questo strumento
Questo strumento è un minificatore basato su regex, circa 30 righe di JavaScript. Tokenizza stringhe letterali, template literal e regex literal in placeholder così le trasformazioni successive non possono corrompere il loro contenuto; rimuove commenti // ... e /* ... */; collassa sequenze di spazi bianchi; rimuove spazi bianchi attorno alla punteggiatura che non ne ha bisogno; e ripristina i letterali tokenizzati. L'output tipico è del 20-40% più piccolo dell'input in byte grezzi. Cosa questo strumento non fa, e che i minificatori di produzione (Terser, swc, esbuild, Closure) gestiscono: non rinomina le variabili locali in singole lettere (nessun mangling scope-aware); non esegue l'eliminazione del codice morto o tree-shaking; non esegue il constant folding o la semplificazione delle espressioni; non emette source map; non capisce la sintassi TypeScript (incolla solo JavaScript puro); non fa tree-shake delle importazioni di moduli ES; non riscrive i nomi delle proprietà. L'inquadramento onesto: incolla il JavaScript che è uscito dal tuo editor o dalla tua mano, ottieni una versione stripped che è tipicamente del 20-40% più piccola in byte grezzi, e usala come artefatto di deploy rapido. Per progetti che hanno una pipeline di build, usa Terser, swc o esbuild in quella pipeline; le ottimizzazioni AST-aware sono la differenza tra la riduzione del 20-40% di questo strumento e il 60-80% di un minificatore di produzione.
Le insidie del minificatore regex
Un passaggio basato su regex che non capisce la grammatica JavaScript può corrompere il codice in modi sottili. Le trappole classiche: i template literal usano backtick e possono contenere espressioni interpolate (`Hello ${name}!`) che sembrano candidati per la rimozione dei commenti se il regex non è attento. I regex literal come /^\/\*/g contengono slash in avanti, sequenze simili a commenti, e contenuto simile a stringhe; gestirli male trasforma un regex in codice sintatticamente rotto. Stringhe contenenti testo simile a commenti (const url = "// example.com"), una rimozione ingenua dei commenti rimuoverebbe tutto dopo il //. Gotcha ASI, Automatic Semicolon Insertion è la funzionalità JS che ti permette di omettere i punti e virgola la maggior parte delle volte, ma interagisce male con la rimozione degli spazi bianchi quando il token successivo inizia con una parentesi, parentesi quadra o operatore ((/regex/), [arr], +1), un collasso degli spazi bianchi incauto può trasformare "due istruzioni" in "un'istruzione con un errore di parsing". La mitigazione in questo strumento è il passaggio di tokenizzazione dei letterali che gira per primo, sostituendo ogni stringa e regex con un placeholder unico, facendo il lavoro su commenti e spazi bianchi sul codice ripulito, poi ripristinando i placeholder. Non è perfetto, ma copre i casi comuni. Se sospetti che il minificatore abbia rotto il tuo codice, la prima cosa da controllare è un regex literal che contiene una sequenza di slash che il tokeniser ha mancato.
Privacy: perché il solo-browser conta qui
I minificatori JS lato server (strumenti online che POST il tuo codice a un server, lo eseguono attraverso Terser lì, e restituiscono il risultato) richiedono di caricare la tua sorgente. Per codice di libreria ordinario questo è innocuo. Per strumenti interni, codice di prodotto non rilasciato, JavaScript contenente chiavi API inline o credenziali di servizi di terze parti, o qualsiasi codice che rivela algoritmi proprietari o logica di business, non lo è. Un minificatore puramente basato su browser, JavaScript che gira nel tuo tab che non fa mai una richiesta di rete dopo il caricamento iniziale della pagina, aggira il problema. Puoi verificare aprendo il tab Network di DevTools, incollando il codice, cliccando Minify, e osservando qualsiasi richiesta in uscita. Meglio ancora, disconnettiti da internet (o abilita la modalità aereo) dopo che la pagina si carica e lo strumento funzionerà ancora, che è la prova empirica più forte che nulla viene caricato.
Domande frequenti
Quanto più piccolo sarà il mio codice?
Per codice formattato a mano con commenti e indentazione, aspettati una riduzione delle dimensioni del 20-40% in byte grezzi. I minificatori di produzione (Terser, swc, esbuild) con ottimizzazioni complete basate su AST raggiungono il 60-80%, il divario è la rinomina dei simboli, l'eliminazione del codice morto e il constant folding, nessuno dei quali uno strumento solo-regex può fare in sicurezza. Dopo la compressione Brotli sull'edge della CDN, il risparmio aggiuntivo dalla minificazione è più modesto (5-15% oltre Brotli sull'originale non minificato) ma non è zero, e su larga scala si somma.
L'output minificato romperà il mio codice?
Per la grande maggioranza del codice, no. I casi limite noti sono attorno ai regex literal con sequenze di slash (/foo\/bar/), template literal con interpolazioni incorporate che si estendono su più righe, e contesti di Automatic Semicolon Insertion dove rimuovere un newline cambia il parsing. Il passaggio di tokenizzazione dei letterali dello strumento gestisce i casi comuni, ma se il tuo codice è inusualmente pesante in uno qualsiasi di quei pattern, testa l'output minificato in un browser prima di fare deploy. Per una pipeline di build di produzione, usa Terser o swc, hanno consapevolezza completa dell'AST e gestiscono questi casi correttamente.
Questo strumento rinomina le variabili?
No. Il mangling delle variabili, trasformare function calculateTotal(itemList) in function a(b), richiede un'analisi completa dello scope su un AST per sapere quali nomi sono sicuri da rinominare. Un passaggio regex non può farlo in sicurezza. Per la rinomina dei simboli, usa Terser, UglifyJS o swc in una pipeline di build; implementano correttamente il mangling scope-aware. La riduzione aggiuntiva delle dimensioni del 10-25% che produce è reale e vale la pena fare per il codice di produzione.
Posso incollare TypeScript?
No, questo strumento è un minificatore solo JavaScript. TypeScript aggiunge annotazioni di tipo (function add(a: number, b: number): number), interfacce, enum, decoratori e altra sintassi che non è JavaScript valido. Compila il tuo TypeScript in JavaScript prima usando tsc, swc, esbuild o Babel, poi incolla l'output JavaScript qui. La maggior parte dei progetti TypeScript sta già passando attraverso uno di quei compilatori come parte del build, quindi il JavaScript esiste da qualche parte.
Dovrei usare questo se ho già una pipeline di build?
Probabilmente no, il tuo bundler lo sta facendo per te, con ottimizzazioni basate su AST ben oltre quello che uno strumento regex può offrire. webpack 5 spedisce terser-webpack-plugin con Terser; Vite usa esbuild per JS di default; Parcel usa swc; Next.js ha usato swc da v12 (ottobre 2021). Questo strumento è per i casi che la tua pipeline di build non copre: pagine HTML scritte a mano, temi WordPress spediti senza una toolchain Node, generatori di siti statici che non includono la minificazione, frammenti una tantum, o esperimenti rapidi dove configurare un build richiederebbe più tempo dello script stesso.
I miei file vengono caricati?
No. Il minificatore è JavaScript che gira nel tuo browser. Il codice che incolli non attraversa mai la rete, verifica nel tab Network di DevTools mentre clicchi Minify, o porta la pagina offline dopo che si carica e conferma che lo strumento funzioni ancora. Strumenti interni, codice di prodotto non rilasciato, script contenenti chiavi API inline o logica di business proprietaria restano sul tuo dispositivo.