Bedre træ ryster med dyb rækkevidde analyse

Her er mit projekt i GSoC 2018: Forbedre træ-ryster til webpack, en meget brugt JS-kodebunter.

Introduktion

Træ-ryster, en understøttende funktion til DCE (dea code eliminering) til at hjælpe DCE ved tværgående modulbrug, er en vigtig funktion for en bundler. Det gælder især JS. At reducere bundtstørrelsen betyder at reducere netværksomkostningerne, hver gang et bundt leveres til webapplikation.

Projekt om Github

中文 版

Uden dette plugin

Før implementeringen af ​​dette plugin bruger webpack en meget enkel løsning til DCE. For eksempel:

I ovenstående eksempel finder webpack referencerne til de importerede variabler. Naturligvis refereres isNumber ikke til i modulet. Som et resultat kan det fjernes i det sidste bundt, hvis det aldrig bruges i andre moduler.

Ovenstående eksempel er meget fjollet, fordi du ikke importerer noget, du ikke har brug for, medmindre du glemmer at fjerne dem. De moderne redigerings- og fnugværktøjer vil dog minde dig om at fjerne ubrugt import. Derfor har vi brug for en mere kraftfuld løsning.

Motivering

Ovenstående nummer illustrerer, at Webpack-trækrystningsmekanisme stadig har noget plads til forbedringer. Vi kan finde ud af forholdet mellem de eksporterede variabler og importerede variabler blandt moduler.

Hvis en eksporteret variabel ikke importeres af et andet modul, fjernes den sammen med dens "børn" (Andre variabler, der kun henvises til af det).

Billede fra ovenstående udgave

Funktion2 importeres ikke af posten, funktion2, og hele eksterne2 kan fjernes. Desværre kunne webpacks tidligere mekanisme ikke fange et sådant tilfælde før introduktionen af ​​dette nye plugin.

Feasibility

Tænk på webpakkens rolle: den gennemgår grafen for alle moduler fra indgangen og bundter dem sammen. Samtidig ved webpack, hvilken eksport der bruges. Hvad med at krydse alle scopes og bundle scopes sammen? Det er sådan, det kan fjerne ubrugte scopes og moduler. Faktisk kan vi betragte rækkevidde som en knude i grafen.

I ovenstående kode er deepEqual relateret til fun5, lige er relateret til fun6. Hvis fun6 ikke importeres af et andet modul, vil den importerede funktion lige blive elimineret.

Hvad er et omfang?

Ved computerprogrammering er omfanget af en navnebinding - en tilknytning af et navn til en enhed, såsom en variabel - regionen af ​​et computerprogram, hvor bindingen er gyldig: hvor navnet kan bruges til at henvise til enheden. En sådan region omtales som en omfangsblok. I andre dele af programmet kan navnet muligvis henvise til en anden enhed (det kan have en anden binding) eller overhovedet intet (det kan være ubundet). - Wikipedia

Hvor du kan føle rækkevidde, når du koder, er noget som blok i koden. Men det er ikke helt ækvivalent. Her er typer af scopes i ECMAScript:

For et ES6-modul betragtes modulomfang som et rodomfang. I modulet kan der kun eksporteres omfang af klasse og funktion, de er børn af modulomfang. Så ikke alle anvendelsesområder vil blive betragtet som en knude i grafen.

Sådan fungerer dette plugin

Dette plugin indeholder en omfangsanalysator, der kan udtrække alle scopes fra moduler. Analysatorens output vil derefter blive brugt til at finde ud af referencerne for variabler i modulet.

Nogle af scopes kunne være bundet til variabler, såsom klasse og funktion. Disse anvendelsesområder, der eksporteres, er atomet til krydsning.

Dette plugin analyserer forholdet mellem import og eksport.

Når webpack giver input om, hvilken eksport der bruges, returnerer dette plugin, hvilken import der kunne fjernes.

Implementeringsdetaljer

Hvis jeg har set yderligere, er det kun ved at stå på giganters skuldre. - Isaac Newton

Vi har brug for en omfangsanalyse for at finde referencerne mellem variabler. Et eksisterende værktøj som https://github.com/estools/escope kan hjælpe.

Denne omfangsanalyse er baseret på AST'erne. Plugin kobler webpacken til at erhverve AST'er af moduler og analyserer dem.

Derefter finder analysatoren ud af, at alle anvendelsesområder tilhører modulomfanget (rodomfanget) med referencereglerne for importen. Hvis vi kan vide, hvilke scopes der bruges af andre moduler, kan analysatoren krydse alle børnes anvendelsesområder og tagge alle variabler fra andre moduler. Importen, der ikke har et mærke, kan betragtes som ubrugt.

Kantsager

At forbedre er at ændre; at være perfekt er at ændre ofte. - Churchill

Der er mange kanter til JavaScript-analyse, nogle af dem er anført nedenfor:

Her er en simpel demo, du kan prøve:

https://vincentdchan.github.io/webpack-deep-scope-demo/

Lokale referencer efter modulomfang

Hvis der henvises til et omfang eller en importeret variabel i modulomfang, ville det ikke blive fjernet.

Tildel funktionen til en variabel igen

På grund af manglen på datastrømningsanalyse, ville enhver handling med at skrive en variabel (såsom tildeling af nye) få analysen til at springe denne variabel over. Derfor er isNull ikke elimineret i ovenstående eksempel.

Hvis et funktionsudtryk tildeles én gang, når variablen er deklareret, vil funktionen blive betragtet som et uafhængigt omfang, vil analysen fungere.

Ren funktion opkald

Hvis en anonym funktion er indpakket i et funktionsopkald, betragter analysatoren det ikke som et uafhængigt omfang, fordi funktionsopkaldet kan have bivirkninger. Men hvis funktionsopkaldet er mærket som rent, kan analysatoren betragte det som et omfang som struktur. Derfor, hvis allPass ikke bruges, vil alle variabler blive elimineret.

Praktiske råd

Der er flere praktiske råd til dette plugin:

ES6-modul er påkrævet

Faktisk er analysen baseret på ES6-modulet, der er kendt som import / eksport-syntaks. Det kan ikke fungere på andre modulsystemer som CommonJS / AMD ... Derfor kræves det, at projektet bruger ES6-modul. Brug for eksempel lodash i stedet for lodash. Ellers skal du fokusere på konfigurationen for babel-loader, TypeScript osv. Undgå, at de transpilerer din kode til ES5-stil, hvilket kan føre til analysefejl.

Brug PURE-annotering til ren funktionskald

Dette plugin kan ikke udlede, om en funktion indeholder bivirkninger. Så / * @ __ PURE __ * / kommentar er påkrævet for et eksportfunktionsopkald, der først introduceres af Uglify. Af årsagen kan du bruge nogle babel-plugins til at tilføje dem automatisk. Og vær opmærksom på, at du ikke lader en anden compiler, f.eks. Babel eller ts-loader, slette kommentarerne.

Resumé

Med dette plugin gør webpack bedre træ rysten. Det hjælper med at eliminere død kode og skrumpe størrelsen på bundtet. På den anden side kræver det, at programmererne skal skrive ren og passende kode, fordi selv den bedste compiler ikke kan optimere den værste kode. Nu har vi en bedre webpakke, har du en fantastisk kode, der skal udarbejdes? Indsend venligst før og efter brugshistorier, så vi bedre kan forstå, hvor effektiv dette er på dit projekt / ramme!