DevTips – So stellen Sie Ihre Assets effizient zusammen

Veröffentlicht: 2022-08-25

Eines unserer Ziele für dieses Jahr war es, unsere beiden Flaggschiff-Plug-ins (Nelio Content und Nelio A/B Testing) auf TypeScript und React Hooks umzugestalten. Nun, wir sind erst ein halbes Jahr durch und können bereits sagen, dass dieses Ziel ein voller Erfolg war. Allerdings muss ich zugeben: Der Weg war etwas komplizierter als erwartet… vor allem, wenn wir das bedenken, nachdem wir TypeScript eingeführt haben, unser Plugin-Build-Zeiten gingen von ein paar Sekunden auf über zwei Minuten! Irgendetwas stimmte nicht und wir wussten nicht was.

Nun, im heutigen Beitrag möchte ich Ihnen etwas über diese Erfahrung erzählen und was wir getan haben, um sie zu beheben. Schließlich wissen wir alle, dass TypeScript den Build-Prozess immer etwas verlangsamt (Typprüfung hat ihren Preis), aber es sollte nicht so viel sein! Nun, Spoiler-Alarm: Das Problem war nie TypeScript … es war meine Konfiguration. TypeScript machte es nur „offensichtlich“. Also fangen wir an, sollen wir?

Spielzeugprojekt

Um Ihnen zu helfen, das Problem zu verstehen, auf das wir vor ein paar Wochen gestoßen sind, und wie wir es behoben haben, ist das Beste, was wir tun können, ein sehr einfaches Beispiel zu erstellen, dem Sie folgen können. Lassen Sie uns ein einfaches WordPress-Plugin erstellen, das TypeScript verwendet, und wie eine Fehlkonfiguration zu extrem langsamen Kompilierungszeiten führen kann. Wenn Sie Hilfe beim Einstieg benötigen, lesen Sie diesen Beitrag zu WordPress-Entwicklungsumgebungen.

Plugin-Erstellung

Das erste, was Sie tun sollten, ist, einen neuen Ordner mit dem Namen Ihres Plugins (z. B. nelio ) in Ihrem WordPress-Verzeichnis /wp-content/plugins zu erstellen. Fügen Sie dann die Hauptdatei ( nelio.php ) mit folgendem Inhalt hinzu:

 <?php /** * Plugin Name: Nelio * Description: This is a test plugin. * Version: 0.0.1 * * Author: Nelio Software * Author URI: https://neliosoftware.com * License: GPL-2.0+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt * * Text Domain: nelio */ if ( ! defined( 'ABSPATH' ) ) { exit; }

Wenn Sie es richtig gemacht haben, werden Sie sehen, dass Sie das Plugin jetzt in WordPress aktivieren können:

Screenshot unseres Spielzeug-Plugins in der Plugin-Liste
Screenshot unseres Spielzeug-Plugins in der Plugin-Liste.

Sicher, das Plugin tut noch nichts… aber zumindest wird es angezeigt

Typoskript

Lassen Sie uns etwas TypeScript-Code hinzufügen! Als erstes werden wir npm in unserem Plugin-Ordner initialisieren. Führen Sie dies aus:

 npm init

und befolgen Sie die Anweisungen auf dem Bildschirm. Installieren Sie dann die Abhängigkeiten:

 npm add -D @wordpress/scripts @wordpress/i18n

und bearbeiten Sie die Datei package.json , um die von @wordpress/scripts benötigten Build-Skripts hinzuzufügen:

 { ... "scripts": { "build": "wp-scripts build", "start": "wp-scripts start", }, ... }

Sobald npm bereit ist, passen wir TypeScript an, indem wir eine tsconfig.json -Datei hinzufügen:

 { "compilerOptions": { "target": "es5", "module": "esnext", "moduleResolution": "node", "outDir": "build", "lib": [ "es7", "dom" ] }, "exclude": [ "node_modules" ] }

Lassen Sie uns abschließend etwas TS-Code schreiben. Wir möchten, dass dies sehr einfach, aber „nah genug“ an dem ist, was wir in Nelio A/B Testing und Nelio Content hatten, also erstellen Sie einen src -Ordner in unserem Projekt mit ein paar TypeScript-Dateien darin: index.ts und utils/index.ts .

Nehmen wir einerseits an, dass utils/index.ts ein Dienstprogrammpaket ist. Das heißt, es enthält einige Funktionen, die andere Dateien in unserem Projekt möglicherweise benötigen. Nehmen wir zum Beispiel an, es bietet die klassischen min und max -Funktionen:

 export const min = ( a: number, b: number ): number => a < b ? a : b; export const max = ( a: number, b: number ): number => a > b ? a : b;

Schauen wir uns andererseits die Hauptdatei unserer App an: index.ts . Für unsere Testzwecke brauchen wir nur ein einfaches Skript, das unser Utility-Paket und eine WordPress-Abhängigkeit verwendet. Etwas wie das:

 import { __, sprintf } from '@wordpress/i18n'; import { min } from './utils'; const a = 2; const b = 3; const m = min( a, b ); console.log( sprintf( /* translators: 1 -> num, 2 -> num, 3 -> num */ __( 'Min between %1$s and %2$s is %3$s', 'nelio' ), a, b, m ) );

@wordpress/scripts Standardeinstellungen

Wenn wir das Projekt jetzt mit npm run build würden, würde alles sofort funktionieren. Und das liegt einfach daran, dass @wordpress/scripts (dh das zugrunde liegende Tool, das wir zum Erstellen unseres Projekts verwenden) so konzipiert wurde, dass es mit einer Codebasis wie der unseres Beispiels funktioniert. Das heißt, wenn wir eine index.ts -Datei im src -Ordner haben, wird eine index.js -Datei im build -Ordner zusammen mit einer index.asset.php -Abhängigkeitsdatei generiert:

 > ls build index.asset.php index.js

Warum zwei Dateien? Nun, die eine ist die kompilierte JavaScript-Datei (duh) und die andere eine Abhängigkeitsdatei mit einigen nützlichen Informationen über unser Skript. Insbesondere teilt es uns mit, auf welche JavaScript-Bibliotheken von den in WordPress enthaltenen Bibliotheken es sich stützt. Zum Beispiel verlässt sich unser index.ts auf das Paket @wordpress/i18n , um Strings zu internationalisieren, und das ist eine Bibliothek, die in WordPress enthalten ist, also … ja, wp-i18n wird in index.asset.php :

 build/index.asset.php <?php return array( 'dependencies' => array( 'wp-i18n' ), 'version' => 'c6131c7f24df4fa803b7', );

Leider ist die Standardkonfiguration nicht perfekt, wenn Sie mich fragen. Hier ist der Grund.

Wenn wir einen Fehler in Ihren Code einführen (lassen Sie uns z. B. die Funktion min mit einem string arg anstelle einer number aufrufen):

 const m = min( `${ a }`, b );

Dies sollte einen Fehler auslösen. Aber das tut es nicht. Es lässt sich ohne Probleme kompilieren.

Typprüfungen während der Kompilierung mit TypeScript

Um die oben erwähnte „Einschränkung“ zu lösen, müssen wir nur unsere eigene Webpack-Konfigurationsdatei erstellen und ihr sagen, dass sie tsc (den TypeScript-Compiler) verwenden soll, wenn sie auf TS-Code trifft. Mit anderen Worten, wir benötigen die folgende Datei webpack.config.json :

 const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); const config = { ...defaultConfig, module: { ...defaultConfig.module, rules: [ ...defaultConfig.module.rules, { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/, }, ], }, }; module.exports = { ...config, entry: './src/index', output: { path: __dirname + '/build', filename: 'index.js', }, };

Wie Sie sehen können, beginnt es mit dem Laden der Standard-Webpack-Konfiguration, die im @wordpress/scripts -Paket enthalten ist, und erweitert dann die defaultConfig , indem es allen .ts -Dateien einen ts-loader hinzufügt. Kinderleicht!

Und nun siehe:

 > npm run build ... ERROR in ...src/index.ts TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. webpack 5.74.0 compiled with 1 error in 4470 ms

Das Kompilieren unseres Projekts führt zu einem Fehler. Hurra! Sicher, es ist etwas langsamer, aber zumindest führen wir einige Sicherheitsprüfungen durch, bevor wir etwas in die Produktion hochladen.

Einreihen der Skripte

Nun, da Sie nun wissen, dass es ein Problem in Ihrem Code gibt, beheben Sie es und kompilieren Sie das Plugin erneut. Hat alles funktioniert? Kühl! Denn jetzt ist es an der Zeit, das Skript und seine Abhängigkeiten in PHP einzureihen, damit wir es in unserem Browser ausprobieren können.

Öffnen nelio.php und hängen Sie das folgende Snippet an:

 add_action( 'admin_enqueue_scripts', function() { $path = untrailingslashit( plugin_dir_path( __FILE__ ) ); $url = untrailingslashit( plugin_dir_url( __FILE__ ) ); $asset = require( $path . '/build/index.asset.php' ); wp_enqueue_script( 'nelio', $url . '/build/index.js', $asset['dependencies'], $asset['version'] ); } );

Gehen Sie als Nächstes zum WordPress-Dashboard (jede Seite reicht aus) und werfen Sie einen Blick auf die JavaScript-Konsole Ihres Browsers. Sie sollten den folgenden Text sehen:

 Min between 2 and 3 is 2

Nett!

Was ist mit MEINEN Abhängigkeiten?

Lassen Sie uns kurz über das Abhängigkeitsmanagement in JavaScript/Webpack/WordPress sprechen. @wordpress/scripts ist so konfiguriert, dass standardmäßig, wenn Ihr Projekt eine im WordPress-Kern gepackte Abhängigkeit verwendet, diese als solche in der .asset.php -Datei aufgeführt wird. Dies erklärt zum Beispiel, warum @wordpress/i18n in der Abhängigkeitsdatei unseres Skripts aufgeführt wurde.

Aber was ist mit Abhängigkeiten zu „anderen“ Paketen? Was ist mit unserem utils -Paket passiert? Um es kurz zu machen: Standardmäßig kompiliert das Webpack alle Abhängigkeiten und fügt sie in das Ausgabeskript ein. Schauen Sie sich einfach die generierte JS-Datei an (kompilieren Sie sie mit npm run start , um die Minimierung zu deaktivieren):

 ... var __webpack_modules__ = ({ "./src/utils/index.ts": ((...) => ... var min = function (a, b) { return a < b ? a : b; }; var max = function (a, b) { return a > b ? a : b; }; }), ...

Sehen? Unser utils -Code ist genau dort, eingebettet in unser Ausgabeskript.

Was ist mit @wordpress/i18n ? Nun, es ist nur ein einfacher Verweis auf eine globale Variable:

 ... var __webpack_modules__ = ({ "./src/utils/index.ts": ..., "@wordpress/i18n": ((module)) => { module.exports = window["wp"]["i18n"]; }) ...

Wie ich bereits sagte, enthält @wordpress/scripts ein Plugin, Dependency Extraction Webpack Plugin , das bestimmte Abhängigkeiten vom Kompilierungsprozess „ausschließt“ und Code generiert, vorausgesetzt, dass sie im globalen Bereich verfügbar sind. In unserem Beispiel können wir zum Beispiel sehen, dass @wordpress/i18n in wp.i18n ist. Aus diesem Grund müssen wir beim Einreihen unseres Skripts auch seine Abhängigkeiten einreihen.

Benutzerdefinierte Konfiguration zum Generieren von zwei separaten Skripts

Nehmen wir an, wir wollen vor diesem Hintergrund dasselbe mit unserem utils -Paket erreichen. Das heißt, wir wollen nicht, dass sein Inhalt in index.js eingebettet wird, sondern er sollte in eine eigene .js -Datei kompiliert werden und als Abhängigkeit von index.asset.php erscheinen. Wie machen wir das?

Zuerst sollten wir die import -Anweisung in index.js umbenennen, sodass es wie ein echtes Paket aussieht . Mit anderen Worten, anstatt das Skript mit einem relativen Pfad ( ./utils ) zu importieren, wäre es schön, wenn wir einen Namen wie @nelio/utils verwenden könnten. Dazu müssen Sie lediglich die Datei package.json des Projekts bearbeiten, um eine neue Abhängigkeit in den dependencies hinzuzufügen:

 { ... "dependencies": { "@nelio/utils": "./src/utils" }, "devDependencies": { "@wordpress/i18n": ..., "@wordpress/scripts": ... }, ... }

Führen Sie npm install aus, um einen symbolischen Link in node_modules zu erstellen, der auf dieses „neue“ Paket zeigt, und führen Sie schließlich npm init in src/utils aus, sodass @nelio/utils aus Sicht von npm ein gültiges Paket ist.

Um dann @nelio/utils in ein eigenes Skript zu kompilieren, müssen wir unsere webpack.config.js Konfiguration bearbeiten und zwei Exporte definieren:

  • die wir bereits hatten ( ./src/index.ts )
  • ein weiterer Export, um ./src/utils in eine andere Datei zu kompilieren, wobei seine Exporte in einer globalen Variablen namens beispielsweise nelio.utils .

Mit anderen Worten, wir wollen Folgendes:

 module.exports = [ { ...config, entry: './src/index', output: { path: __dirname + '/build', filename: 'index.js', }, }, { ...config, entry: './src/utils', output: { path: __dirname + '/build', filename: 'utils.js', library: [ 'nelio', 'utils' ], libraryTarget: 'window', }, }, ];

Kompilieren Sie den Code erneut und werfen Sie einen Blick auf den Ordner ./build – Sie werden sehen, dass wir jetzt alle zwei Skripte haben. Werfen Sie einen Blick auf ./build/utils.js und Sie werden sehen, wie es nelio.utils wie erwartet definiert:

 ... var min = function (a, b) { return a < b ? a : b; }; var max = function (a, b) { return a > b ? a : b; }; (window.nelio = window.nelio || {}).utils = __webpack_exports__; ... ... var min = function (a, b) { return a < b ? a : b; }; var max = function (a, b) { return a > b ? a : b; }; (window.nelio = window.nelio || {}).utils = __webpack_exports__; ...

Leider sind wir erst bei der Hälfte. Wenn Sie sich auch ./build/index.js , werden Sie sehen, dass src/utils immer noch darin eingebettet ist … sollte es nicht eine „externe Abhängigkeit“ sein und die gerade definierte globale Variable verwenden?

Benutzerdefinierte Konfiguration zum Erstellen externer Abhängigkeiten

Um @nelio/utils in eine tatsächliche externe Abhängigkeit umzuwandeln, müssen wir unser Webpack weiter anpassen und das zuvor erwähnte Abhängigkeitsextraktions-Plugin nutzen. Öffnen Sie einfach die Datei webpack.config.js und ändern Sie die config wie folgt:

 const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' ); const config = { ...defaultConfig, module: { ...defaultConfig.module, rules: [ ...defaultConfig.module.rules, { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/, }, ], }, plugins: [ ...defaultConfig.plugins.filter( ( p ) => p.constructor.name !== 'DependencyExtractionWebpackPlugin' ), new DependencyExtractionWebpackPlugin( { requestToExternal: ( request ) => '@nelio/utils' === request ? [ 'nelio', 'utils' ] : undefined, requestToHandle: ( request ) => '@nelio/utils' === request ? 'nelio-utils' : undefined, outputFormat: 'php', } ), ], };

sodass alle Verweise auf @nelio/utils im Code als nelio.utils übersetzt werden und es eine Abhängigkeit zum Skript-Handler nelio-utils gibt. Wenn wir uns die Abhängigkeiten beider Skripte ansehen, sehen wir Folgendes:

 build/index.asset.php <?php return array( 'dependencies' => array('nelio-utils', 'wp-i18n')... ?> build/utils.asset.php <?php return array( 'dependencies' => array()... ?>

und wenn wir in ./build/index.js , bestätigen wir, dass die @nelio/utils Abhängigkeit jetzt tatsächlich extern ist:

 ... var __webpack_modules__ = ({ "@nelio/utils": ((module)) => { module.exports = window["nelio"]["utils"]; }), "@wordpress/i18n": ((module)) => { module.exports = window["wp"]["i18n"]; }) ...

Es gibt jedoch ein letztes Problem, das wir ansprechen müssen. Gehen Sie zu Ihrem Browser, aktualisieren Sie die Dashboard-Seite und sehen Sie sich die Konsole an. Es wird nichts angezeigt, oder? Wieso den? Nun, nelio hängt jetzt von nelio-utils ab, aber dieses Skript ist nicht in WordPress registriert … daher können seine Abhängigkeiten derzeit nicht erfüllt werden. Um dies zu beheben, bearbeiten nelio.php und registrieren Sie das neue Skript:

 add_action( 'admin_enqueue_scripts', function() { $path = untrailingslashit( plugin_dir_path( __FILE__ ) ); $url = untrailingslashit( plugin_dir_url( __FILE__ ) ); $asset = require( $path . '/build/utils.asset.php' ); wp_register_script( 'nelio-utils', $url . '/build/utils.js', $asset['dependencies'], $asset['version'] ); } );

So beschleunigen Sie den Build-Prozess

Wenn wir den Build-Prozess mehrmals ausführen und durchschnittlich die Dauer bis zum Abschluss ermitteln, sehen wir, dass der Build in etwa 10 Sekunden ausgeführt wird:

 > yarn run build ... ./src/index.ts + 2 modules ... webpack 5.74.0 compiled successfully in 5703 ms ... ./src/utils/index.ts ... webpack 5.74.0 compiled successfully in 5726 m Done in 10.22s.

Das mag nicht nach viel erscheinen, aber dies ist ein einfaches Spielzeugprojekt, und wie ich bereits sagte, brauchen echte Projekte wie Nelio Content oder Nelio A/B Testing Minuten zum Kompilieren.

Warum ist es „so langsam“ und was können wir tun, um es zu beschleunigen? Soweit ich das beurteilen konnte, lag das Problem in unserer Webpack-Konfiguration. Je mehr Exporte Sie in module.exports Ihres module.exports haben, desto langsamer wird die Kompilierungszeit. Ein einzelner Export ist jedoch viel, viel schneller.

Lassen Sie uns unser Projekt leicht umgestalten, um einen einzelnen Export zu verwenden. Erstellen Sie zunächst eine export.ts -Datei in src/utils mit folgendem Inhalt:

 export * as utils from './index';

Bearbeiten Sie dann Ihre webpack.config.js so, dass sie einen einzigen Export mit zwei Einträgen enthält:

 module.exports = { ...config, entry: { index: './src/index', utils: './src/utils/export', }, output: { path: __dirname + '/dist', filename: 'js/[name].js', library: { name: 'nelio', type: 'assign-properties', }, }, };

Erstellen Sie schließlich das Projekt erneut:

 > yarn run build ... built modules 522 bytes [built] ./src/index.ts + 2 modules ... ./src/utils/export.ts + 1 modules ... webpack 5.74.0 compiled successfully in 4339 ms Done in 6.02s.

Es dauerte nur 6 Sekunden, das ist fast die Hälfte der Zeit wie früher! Ziemlich ordentlich, oder?

Zusammenfassung

TypeScript hilft Ihnen, die Qualität des Codes zu verbessern, da Sie damit zur Kompilierzeit überprüfen können, ob die Typen korrekt sind und keine Inkonsistenzen vorhanden sind. Aber wie alles im Leben haben die Vorteile der Verwendung von TypeScript ihren Preis: Das Kompilieren des Codes wird etwas langsamer.

Im heutigen Beitrag haben wir gesehen, dass das Kompilieren Ihres Projekts abhängig von der Konfiguration Ihres Webpacks viel schneller (oder langsamer) sein kann. Der Sweet Spot erfordert einen einzigen Export … und darüber haben wir heute gesprochen.

Ich hoffe, dir hat der Beitrag gefallen. Wenn ja, teilen Sie es bitte. Wenn Sie andere Möglichkeiten zur Optimierung des Webpacks kennen, teilen Sie mir dies bitte im Kommentarbereich unten mit. Haben Sie einen guten Tag!

Vorgestelltes Bild von Saffu auf Unsplash.