In dem vorerst letzten Teil der Serie geht es um die Verarbeitung von Typescript und React. Desweiteren zeige ich wie man Bibliotheken in dem globalen Kontext registriert (z.B. jQuery unter dem Namen $ und jQuery) und wie man Bibliotheken extern einbindet um sie von einem CDN zu laden.

Verarbeitung von Typescript

TypeScript LogoTypeScript ist eine durch Microsoft entwickelte Programmiersprache, die auf den Vorschlägen zum ECMAScript 6 Standard basiert. Sprachkonstrukte von Typescript, wie Klassen, Vererbung, Module, anonyme Funktionen und Generics wurden auch in ECMAScript 6 übernommen.

Um TypeScript in webpack zu integrieren wird der TypeScript Compiler und ein zugehöriger webpack-Loader installiert.

$ yarn add -D typescript ts-loader 

Um mit TypeScript zu starten, wird das Startskript auf index.ts oder index.tsx (TypeScript mit React Syntax) geändert.

webpack.config.js:
    // application entry point
    const entryName = './src/index.ts';

Ein simples TypeScript Beispiel das den Typ der übergebenen msg-Variable prüft.

src/index.ts:
const displayMessage = (msg : string) : void => {
  console.log(msg);
}

displayMessage("Hello World!");
// displayMessage(1234);  // Übergabe einer Zahl erzeugt einen Fehler

Die gemeinsame Konfigurationsdatei wird um die TypeScript Dateien erweitert.

config/webpack.common.js:
/* begin neuer code */
            extensions: [ '.js', '.jsx', '.ts', '.tsx', '.json', '.scss', '.css', '.jpeg', '.jpg', '.gif', '.png', '.svg' ], // Automatically resolve certain extensions
/* ende neuer code */

[...]

        module: {
            rules: [
                // JavaScript Dateien
                {
                    test: /\.jsx?$/,
                    exclude: /node_modules/,
                    loader: "babel-loader"
                },
/* begin neuer code */

                // Typescript Support
                {
                    test: /\.tsx?$/,
                    exclude: /node_modules/,
                    use: [
                        { 
                            loader : "babel-loader"
                        },
                        {
                            loader : "ts-loader"
                        }
                    ]
                },
/* ende neuer code */

Als nächstes wird eine Konfiguration für den TypeScript Compiler angelegt.

tsconfig.json:
{
  "compilerOptions": {
    "target": "es6",
    "lib": ["es6", "dom"],
    "sourceMap": true,
    "allowJs": true,
    "jsx": "preserve",
    "moduleResolution": "node",
    "rootDir": "src",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": false,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true
  },
  "exclude": [
    "node_modules",
    "config",    
    "dist"
  ]
}

Sobald man das Projekt mittels yarn dev oder yarn prod erstellt, wird der TypeScript Compiler genutzt um die Beispiel index.ts Datei zu übersetzen.

Bibliothek im globalen Kontext registrieren

jQuery LogoUm jQuery im globalen Kontext unter den Namen $ und jQuery hinzuzufügen, wird das ProvidePlugin verwendet. Dafür muss das jQuery-Modul installiert werden und die Zuordnung in der webpack-Konfiguration eingefügt werden. Sobald ein Skript die Bibliothek importiert, wird diese dem Applikationsbundle hinzugefügt und im globalen Kontext registriert.

$ yarn add jquery
config/webpack.common.js:
        plugins : [
            new HtmlPlugin({
                template: path.resolve(__dirname, '../src/assets/index.template.html')
/* begin neuer code */
            }),
            new webpack.ProvidePlugin({     // insert jquery in globals, if it is imported
                $: "jquery",
                jQuery: "jquery"
            })
/* ende neuer code */
        ],

Um zu testen, ob jQuery wirklich erreichbar ist, ersetzen wir in der webpack.config.js erneut den entryName durch index.js und erstellen ein Beispielprogramm das die Version der jQuery Bibliothek ausgibt:

src/index.js:
import 'jquery';

alert($.fn.jquery);

React Unterstützung

Um die JSX bzw. TSX Dateien verarbeiten zu können, wird ein Preset für Babel benötigt. Dieses übersetzt die JSX-Tags in JavaScript.

Der folgende Beispiel-Code in JSX

ReactDOM.render(
  <a href="https://xkcd.com/221/">
    <img src="https://imgs.xkcd.com/comics/random_number.png" />
  </a>,
  document.getElementById('app')
)

wird übersetzt nach

ReactDOM.render(
  React.createElement('a', { href: "https://xkcd.com/221/" },
    React.createElement('img', {
      src: "https://imgs.xkcd.com/comics/random_number.png"
    })
  ),
  document.getElementById('app')
)

Dies passiet vor weiteren Verarbeitung der JavaScript Dateien.

Installation des Presets und die Anpassung der Babel Konfiguration
$ yarn add -D babel-preset-react
.babelrc:
{
  "presets" : [
    "env", "react"
  ]
}

Die react-Bibliotheken kann man entweder mit in das Applikationsbundle aufnehmen. Dafür müssen sie installiert und von den Quelldateien importiert werden.

$ yarn add react react-dom

Alternativ kann man die Bibliotheken von einem CDN laden.

Bibliotheken extern von einem CDN laden

Anstatt eine Bibliothek mit dem Applikationsbundle zu verteilen, definiert man sie in dem externals Abschnitt der webpack-Konfiguration.

Dadurch geht webpack davon aus, das die Bibliothek vorhanden ist. Dies hat den Vorteil, das das Applikationsbundle kleiner wird und sich sehr beliebte Bibliotheken bereits im Cache des Nutzers befinden.

Man muss nur darauf achten, das man in der HTML-Vorlage die CDN URL mittels SCRIPT-Tag einfügt.

Am Beispiel von jQuery und ReactJS werden die Bibliotheken react, react-dom und jquery von einem CDN geladen.

webpack.config.js:
        webpackConfig.devtool = 'inline-source-map';  
    }

/* begin neuer code */          
    // use external version from cdn (index.template.html)
    webpackConfig.externals =  {
        "react": "React",
        "react-dom": "ReactDOM",
        "jquery" : "jquery",
        "jquery" : "$"
    }
/* ende neuer code */          

    // console.log("Webpack-Config: " + JSON.stringify(webpackConfig, null, 4));
    return webpackConfig;
src/assets/index.template.html:
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <link rel="icon" type="image/x-icon" href="favicon.ico" />
  <title>WebApp</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- external versions of libraries from cdn -->
  <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>

Zum testen kann man das folgende kleine React-Beispiel nutzen:

src/index.js:
import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <div>
      <h1>Hello React!</h1>
  </div>, document.getElementById('root'));

Damit hat man eine Umgebung, um ein JS-/TS-Projekt zu starten. Bleibt nur noch die Vorlage aufzuräumen, eine fast leere index.template.html-Datei anzulegen und schon kann es losgehen.

$ yarn clean

Aufräumen der Vorlage

$ yarn remove markdown jquery
$ rm src/assets/{fonts,icons,images,media,stylesheets}/* src/*.ts src/*.js
$ echo 'console.log("Hello World");' > src/index.js
$ cat >src/assets/index.template.html << EOF
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- <link rel="icon" type="image/x-icon" href="favicon.ico" /> -->
    <title>WebApp</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- external versions of libraries from cdn -->    
    <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  </head>  
<body>
  <div id="root"></div>
</body>
</html>
EOF

Hiermit sind wir am Ende der Serie angekommen. Man kann die Vorlage noch erweitern, indem man ein Testframework einbindet. Dies werde ich in einem Folgebeitrag beschreiben, sobald ich mich damit eingehender beschäftigt habe.

Bis dahin viel Erfolg mit der Vorlage.

Der finale Stand befindet sich in meinem gitlab-Projekt unter https://gitlab.com/svenpaass/webpack-template/tree/chapter5

Update - 07.01.2018

Es hat sich ein kleiner Fehler bei der Behandlung der Bilder und Fonts eingeschlichen. Bilder im node_modules Verzeichnis werden ignoriert. Daher werden die include Befehle im Bilder und Schriftarten Bereich entfernt und die Test Statements angepasst.

config/webpack.common.js:
/* begin neuer code */
                // Bilder
                {
                    test: /\.(jpe?g|png|gif|svg)$/i,
/* ende neuer code */
                    use: [

[...]

/* begin neuer code */
                // Schriftarten 
                //
                // Hinweis: Es wird davon ausgegangen, das sich die Fonts in einem 
                // Verzeichnis mit dem Namen fonts befinden, da sonst font-svg Dateien 
                // in images/ (siehe Bilder) abgelegt würden.
                {
                    test: /fonts\/.*\.(svg|eot|ttf|woff|woff2)$/i,
/* ende neuer code */
                    use: [

[...]

Nächster Beitrag Vorheriger Beitrag