Sådan trinvist skiftes til webpakke

Dette er den anden af ​​en todelt serie om, hvorfor og hvordan vi skiftede vores JavaScript-bundlingssystem fra et ad hoc-system med Grunt-opgaver og PHP, til en erklærende webpack-konfiguration. Klik her for at gå til Why We Switched to webpack for at finde ud af, hvorfor vi skiftede til webpack.

Denne artikel er til dig, hvis din codebase's JavaScript build-system har problemer, der ligner vores i Hvorfor vi skiftede til webpack, og du vil migrere til webpack på en trinvis måde ved at dele den op i mindre trin. Nogle dele af denne artikel er specifikke for AngularJS 1.x, men resten kan anvendes til enhver ramme.

For at gøre migreringen så smertefri som muligt skal du opdele den i disse trin:

  1. Introducer webpack sammen med dit eksisterende build-system uden at ændre din applikationskode.
  2. Brug webpakke under udvikling i en periode, og løs problemer, når de dukker op. Afskriv brugen af ​​det gamle build-system, men fortsæt med at bruge det i produktionen.
  3. Fjern det forældede build-system, og lad kun webpack være.
  4. Opdater kodebasen med forbedringer, der nu er mulige med webpack.

Ved at bryde migrationen ned i disse trin undgår du at bruge den oprindelige tid, der ville have været brugt på at teste alt og dække alle kantsager for at sikre, at det nye build-system fungerer i produktion; fortsæt blot med at bruge dit eksisterende build-system til produktionsinstallationer.

Under udvikling drager du straks fordel af visse webpack-funktioner, såsom hurtige genopbygningstider og sourcemap-baseret debugging, og hvis der er et problem med det nye build-trin, kan du altid vende tilbage til din gamle.

1. Introducer webpack sammen med dit eksisterende build-system

Start med at gentage dit gamle build-systems kernefunktioner, der er nødvendige for at opbygge din applikation:

  • Oprettelse af et JavaScript-bundt
  • Oprettelse af et CSS-bundt
  • Gengivelse af JS / CSS aktivstier til din HTML-skabelon

Bemærk, at du kan ekskludere mere avancerede krav fra denne liste, såsom minificering, bundling til karma-tests eller bundling af oversatte sprogstrenge. Disse kan håndteres i trin 2.

Oprettelse af et JavaScript-bundt

Dit nuværende build-system involverer sandsynligvis et sammenkædningstrin til at kombinere mange scripts til et, f.eks. Med grynt-bidrag-konkat:

grunt.initConfig ({
  konkat: {
    js: {
      filer: [{
        src: [
          'Forhandler / lodash.js',
          'forhandler / jquery.js',
          'Forhandler / angular.js',
          'Forhandler / kantet-cookies.js',
          'app / scripts / ** / *. js'
        ],
        dest: 'build / js /'
      }]
    }
  }
});

Heldigvis er webpack fleksibel nok til at gentage denne opførsel ved hjælp af import-loader, eksport-loader og kontekst:

For at få din webpack JS-bundt til at opføre sig identisk med dit gamle script-sammenhængende bundt, skal du oprette en ny .js-fil, der skal fungere som dit webpack-indgangspunkt, og bruge import-loader og eksport-loader til at importere afhængigheder og eksportværdier for dine leverandørskripts, f.eks

// app / app.js
// Importer ældre leverandørskripts i den rigtige rækkefølge
windows._ = kræver (
  '../Vendor/lodash'
);
vindue. $ = windows.jQuery = kræver (
  '../Vendor/jquery'
);
windows.angular = kræver (
  'Eksport? Window.angular! ../ leverandør / kantet'
);
kræve(
  'Import? Kantet => window.angular!' +
  ' ../vendor/angular-cookies'
);
// ... applikationsmanuskripter

Da hvert leverandørscript har forskellige afhængigheder og eksporterede værdier, skal hver inspiceres manuelt for at bestemme, om & hvordan import-loader og eksport-loader skal bruges. Sørg for at tildele globale variabler til `vindue 'for at sikre, at de er tilgængelige i din applikationskode.

Hvis din applikationskode også skal udføres i en bestemt rækkefølge, kan du blot "kræve" hver fil i rækkefølge, f.eks.

// app / app.js
// ... leverandør scripts
kræver ( './ scripts / moduleA');
kræver ( './ scripts / moduleB');
kræver ( './ scripts / moduleC');
// ...
kræver (' ./ scripts / main');

Eller, hvis din applikationskode kan udføres i en hvilken som helst rækkefølge (f.eks. Hvis du bruger AngularJS-moduler), kan du bruge webpack-kontekst til at "kræve" alle disse filer på én gang:

// app / app.js
// ... leverandør scripts
/ **
 * kræver alle moduler i den givne webpack-kontekst
 * /
funktion kræverAlle (kontekst) {
  context.keys () foreach (sammenhæng).;
}
// Saml alle vinkelmoduler
requireAll (require.context (
  ' ./scripts',
  / * Brug undermapper: * / true,
));

Til håndtering af dine JS-filer er det alt, hvad der er til det! Der var ikke behov for ændringer i leverandørens scripts eller applikationskode; du behøver kun at ændre webpakkens indgangspunkt. Det er vigtigt, at du ikke ændrer selve programkoden for at sikre, at du kan fortsætte med at bygge ved hjælp af dit tidligere build-system.

Oprettelse af et CSS-bundt

Dit CSS-opbygningstrin involverer sandsynligvis et forarbejdningstrin, f.eks. Med Stylus via grunt-contrib-stylus:

grunt.initConfig ({
  pekepinde: {
    udarbejde: {
      filer: {
        src: 'app / css / main.styl',
        dest: 'build / css / main.css'
      }
    }
  }
});

webpack tilbyder et sæt af læssere til at håndtere CSS-forarbejdning og output:

  • stylus-loader / sass-loader / less-loader: kører den givne forbehandler på dine CSS-filer (e) og returnerer den almindelige CSS
  • css-loader: løser `@ import` /` url (…) `og returnerer den resulterende CSS
  • url-loader / file-loader: inlines eller udsender filindhold i webpacks output, som font eller billede `url (…)` i CSS
  • extract-text-webpack-plugin: flytter din CSS til en separat outputfil i stedet for at indlive den i HTML

At konfigurere webpakke med disse læssere efterlades som en øvelse for læseren, men når den først er konfigureret, er det nemt at tilføje dit stilark til webpakkens build som at tilføje et `kræv (…) 'til dit indgangspunkt:

// app / app.js
kræve ( './ css / main.styl');
// ... leverandør scripts
// ... applikationsmanuskripter

Bemærk: CSS-opbygningstrinet behøver ikke nødvendigvis at være en del af webpack-opbygningen; for eksempel kunne vi have valgt at skifte til et simpelt npm-script, der påberåber sig 'stylus' -kommandoen. Men vi får nogle fordele ved at bruge webpack:

  • Kun webpack skal køres for at bygge både JS og CSS; ikke nødvendigt at køre en separat kommando for at oprette CSS.
  • Billed- / fontaktiver, der henvises til i CSS, inkluderes automatisk i webpacks output; ikke nødvendigt at kopiere dem til din outputmappe og administrere aktivstier via en anden metode.
  • fillæsser genererer automatisk hashede filnavne til langvarig cache af CSS-filen og inkluderede skrifttyper / billeder.

Gengivelse af JS / CSS aktivstier til din HTML-skabelon

Det sidste trin for at kunne køre både webpack og dit gamle build-system på den samme kodebase er at gengive den nye webpack JS / CSS-aktiver til dit websteds HTML-skabelon.

Du kan fange stierne for alle webpacks outputaktiver ved hjælp af objektet 'Stats', der returneres, når bygningen er afsluttet. En enkel måde at videregive disse data til din HTML-skabelon er via stats-webpack-plugin:

// webpack.config.js
const StatsPlugin = kræve ('stats-webpack-plugin');
module.exports = {
  // ...
  plugins: [
    // ...
    nye StatsPlugin ('webpack-stats.json', {
      bidder: falsk,
      moduler: falsk,
      børn: falsk,
      cache: falsk,
      grunde: falsk,
      kilde: falsk,
      errorDetails: falsk,
      chunkOrigins: falsk,
    })
  ]
};

For let at skifte frem og tilbage mellem webpack og dit gamle build-system, kan du ganske enkelt gengive webpacks aktivstier, hvis webpack-stats.json findes i din outputmappe, ellers falder tilbage til dit gamle build-systems aktivstier.

Brug f.eks. PHP:

Det er alle de brikker, der er nødvendige for at få en minimal webpakkeopbygning! På dette tidspunkt skal dit projekt være i denne tilstand:

  • Kør dit gamle build-trin (f.eks. 'Grunt build'), som vil oprette JS / CSS-aktiver i din build-mappe, hvilket får dit websteds HTML-skabelon til at gengive aktivstier på den gamle måde.
  • Kør 'webpack', som vil oprette JS / CSS-aktiver og webpack-stats.json i din build-mappe, hvilket får dit websteds HTML-skabelon til at gengive aktivstier ved hjælp af webpack-stats.json.

2. Brug webpack under udvikling og forkert brug af det gamle build-system

I denne fase skal du fortælle andre udviklere om projektet at bruge webpack i stedet for det gamle build-system. Hvis der opstår problemer (såsom en forkert konfigureret læsser), kan du løse dem sikkert uden at påvirke produktionen, da produktionen fortsætter med at bruge det gamle build-system i øjeblikket.

På dette trin skal du også konfigurere udskiftninger til alle andre opgaver, som det gamle build-system udfører, såsom aktivering af aktiver, karma-testudførelse eller sprogoversættelser.

Hvis du bruger en opgaveløber som Grunt eller Gulp, er npm-scripts ofte et enklere alternativ til Grunt / Gulp-opgaver.

Her er nogle Grunt-opgavealternativer, som vi bruger:

  • grunt-karma: Karmas kommandolinjegrænseflade (`karma start`)
  • grunt-vinkel-gettext: vinkel-gettext-cli + vinkel-gettext-loader
  • gryn-vinkel-skabeloner: ngtemplate-l0ader

I slutningen af ​​dette trin skal du have:

  • Et npm script til at erstatte hver build-opgave, f.eks. `npm run bundle: watch` til bundle & watch under udvikling,` npm run bundle: produktion` for at bundle & minify med sprogoversættelser, `npm run karma` for at køre Karma tests
  • Testet hele applikationen grundigt for at sikre, at den fungerer korrekt: pas på manglende scripts eller stilark, da disse kan få din app til at gå i stykker.

3. Fjern det forældede build-system, og lad kun webpakke være

Denne del er let: blot konfigurer dit CI build-script til at køre dine nye npm build-scripts (som `npm test & & npm run bundle: produktion`), slette din gamle task runner-konfiguration (Gruntfile.js / gulpfile.js / osv.) , og fjern nu ubrugte afhængigheder fra package.json.

Du ønsker også at slette koden i din HTML-skabelon, der falder tilbage til dit gamle build-system, hvis webpack-stats.json ikke findes.

Hvis du har testet din ansøgning grundigt i den forrige fase, bør dette trin gå uden problemer. Selvfølgelig skal du først implementere i et produktionslignende testmiljø først for at være sikker.

4. Opdater kodebasen med forbedringer aktiveret med webpack

Nu hvor webpack er det eneste build-system, kan vi løse de problemer, der oprindeligt blev beskrevet i Hvorfor vi skiftede til webpack:

Løsning af afhængigheder

Kan du huske, hvordan du opsætter webpakkens indgangspunkt for at importere scripts globalt i afhængighed-første rækkefølge?

// app / app.js
kræve ( './ css / main.styl');
// Importer ældre leverandørskripts i den rigtige rækkefølge
windows._ = kræver (
  '../Vendor/lodash'
);
// ...
kræver ( './ scripts / moduleA');
kræver ( './ scripts / moduleB');
kræver ( './ scripts / moduleC');
// ...
kræver (' ./ scripts / main');

Det er ikke ideelt at fortsætte med at importere dem på denne måde: at sikre, at ordren er korrekt, kan være fejlagtigt, og forurening af det globale navneområde kan være problematisk.

I stedet kan du nu importere afhængigheder i det modul, hvor de bruges, f.eks. hvis main.js afhænger af lodash.js, moduleA.js og moduleC.js:

// app / scripts / main.js
var _ = kræve ('../../ leverandør / lodash');
var A = kræver ('./ moduleA');
var C = kræver ('./ moduleC');
// ...

Og hvis moduleB.js kun afhænger af moduleA.js:

// app / scripts / moduleB.js
var A = kræver ('./ moduleA');
// ...

Når et modul først er importeret eksplicit af alle dets afhængige, kan det fjernes fra webpakkens indgangspunkt: skift et modul ad gangen, og til sidst bliver dit webpakkens indgangspunkt tilbage med bare:

// app / app.js
kræve ( './ css / main.styl');
kræver (' ./ scripts / main');

Brug af npm-pakker

Nu kan du begynde at bruge npm-pakker i stedet for at kopiere tredjepartsleverandørmoduler til depotet: opgradering af pakker bliver lettere, delte transitive afhængigheder reducerer kodeduplikation, og du behøver ikke længere at oprette brugerdefinerede UMD-bundter.

Det skal være så enkelt som at lave en `npm installation ` og derefter opdatere referencer til modulet, f.eks. ændre dette:

var _ = kræve ('../../ leverandør / lodash');

Til dette:

var _ = kræve ('lodash');

Nu, hvor afhængighederne er eksplicit, og vi bruger npm-pakker, har vi (i det mindste noget) opnået målet om at gøre kodebasen lettere at forstå. Og vi har forbedret dev-prod-paritet også, da webpack nu håndterer vores modulafhængigheder på samme måde i både udvikling og produktion: den eneste forskel er, at produktionsbygningen er minimeret.

Naturligvis gjorde vi også kodebasen klar til en fremtid, hvor det er lettere at begynde at bruge ES2015 + ved at tænde for babel-loader, og at bruge React og Redux bliver lettere med muligheden for at bruge JSX og npm-pakker.

Dette var den overgang, som EventMobis primære begivenhedsapp-kodebase for nylig har foretaget. Hvis dit projekt er i lignende form, håber jeg, at dette har givet en vis indsigt i, hvordan du forbedrer det!

[Hvis du kunne lide denne artikel, skal du klikke på det lille til venstre, så andre mennesker ved det. Hvis du ønsker flere indlæg som dette, skal du følge EventMobi-publikationen med knappen (Følg) til højre!

Endelig, hvis det at løse problemer som dette ser ud i din gyde, ansætter vi! Tjek vores nuværende åbne positioner på http://www.eventmobi.com/careers/]