Webpack

18. Feber 2020

Bei Webpack handelt es um einen sogenannten „Modul-Packer“ im Web-Bereich. Ganz einfach ausgedrückt bedeutet dies, dass er mehrere JavaScript (oder auch TypeScript, …) eine eine Datei steckt.

Genaugenommen müssen dies aber nicht unbedingt JavaScript-Dateien sein, sondern kann auch json, html, css, … sein. Diese können entweder 1zu1 in das Paket übernommen werden, referenziert werden oder zuvor noch umgewandelt bzw. modifiziert werden. Dies ist z.B. bei TypeScript oder less notwendig, da der Browser anstatt TypeScript JavaScript und anstatt less css erwartet.

Das besondere an Webpack ist, dass hier die Abhängigkeiten geprüft werden und ein – nennen wir es Baum – erstellt wird. Damit weiß Webpack, welche Funktionen von welchen Funktionen verwendet und aufgerufen werden. Dadurch kann z.B. Tree-Shaking gemacht werden. Dies bedeutet, dass Code, der nicht verwendet wird, im Endergebnis nicht enthalten ist.

Ein weiteres Feature von Webpack ist Code-Splitting. Es macht vor allem bei größeren Anwendungen keinen Sinn, alles in eine Datei zu packen. Ev. benötigt ein Benutzer immer nur einen Teil der Funktionalität. Daher erstellt Webpack unterschiedliche Pakete und lädt diese im Falle einer Web-Anwendung erst dann, wenn das erste Mal Code aus dieser Datei benötigt wird.

Mit neueren Version von Webpack (vor allem 5+) ist es nicht mehr notwendig Konfigurationsdateien zu erstellen. Wenn keine vorhanden ist, dann werden „best practise“-Ansätze, die bereits gut erprobt sind, verwendet. Nichtsdestotrotz wird es immer mal wieder notwendig sein, eine Konfigurationsdatei zu erstellen. Deshalb nachfolgend die wichtigsten Bestandteile.

Entry

Es gibt ein oder mehrere Einstiegspunkte für Webpack. Ab diesen wird der Code analysiert und geprüft, was alles benötigt wird.

module.exports = {
  entry: {
    app: "./src/app.js",
    adminApp: "./src/adminApp.js"
  }
};

In diesem Beispiel gibt es zwei Entry-Einträge: app und adminApp mit jeweils einem Verweis auf die JavaScript-Datei.

Output

Im Output-Bereich wird definiert wohin das Ergebnis geschrieben werden und wie die Namensgebung der Dateien funktionieren soll.

module.exports = {
  entry: {
    app: "./src/app.js",
    search: "./src/search.js"
  },
  output: {
    filename: "[name].js",
    path: __dirname + "/dist"
  }
};

Die erstellten Pakete werden im Unterordner „dist“ gespeichert. Für den Namen gibt es Platzhalter wie z.B. [name], [chunkhash], [id], … Näheres dazu in der Dokumentation unter https://webpack.js.org/configuration/output/#outputfilename.

Eine weitere wichtige Eigenschaft ist „publicPath“. Wird die Anwendung nicht vom Root einer Domäne aufgerufen, dann muss dort der relative Pfad hinterlegt sein, andernfalls ein Slash.

Loader

Mit Hilfe von Loadern können Transformationen gemacht werden. Ein einfaches Beispiel dafür ist TypeScript. Wenn es sich um eine *.ts-Datei handelt, dann muss der ts-loader verwendet werden. Dieser wandelt dann TypeScript in JavaScript um.

module.exports = {
  entry: {
    app: "./src/app.js",
    search: "./src/search.js"
  },
  output: {
    filename: "[name].js",
    path: __dirname + "/dist"
  },
  module: {
    rules: [
      { test: /\.ts$/, loader: "ts-loader" }
    ]
  }
};

Anstatt der loader-Eigenschaft kann auch „use“ verwendet werden, um mehrere Loader hintereinander zu schalten (man beachte die Reihenfolge von unten nach oben!).

module.exports = {
  entry: {
    app: "./src/app.js",
    search: "./src/search.js"
  },
  output: {
    filename: "[name].js",
    path: __dirname + "/dist"
  },
  module: {
    rules: [{ 
      test: /\.sass$/, 
      use: [{
        loader: "style-loader",
      }, {
        loader: "css-loader",
      }, {
        loader: "sass-loader"
      }]
    }]
  }
};

Alternativ kann der Loader auch direkt beim import bzw. require angegeben werden, was für mein Verständnis aber nicht wirklich empfehlenswert ist.

Das Endergebnis einer Loader-Kette muss immer JavaScript sein!

Plugins

Plugins sind ähnlich den Loaders, werden aber nicht auf einzeln Input-Dateien angewendet, sondern auf die Pakete, die erstellt werden. Damit können z.B. Styles oder Texte aus dem Paket herausextrahiert werden oder das Ergebnis obfuskiert oder minimiert werden.