Entwicklung einfacher, moderner JavaScript-Anwendungen mit Webpack und fortschrittlichen Webtechnologien

Haben Sie bei der Entwicklung Ihres nĂ€chsten Webprojekts darĂŒber nachgedacht, die einfachste vorhandene Technologie zu verwenden? Wenn dem so ist, dann ist das Material, dessen Übersetzung wir heute veröffentlichen, speziell fĂŒr Sie geschrieben.

Es gibt JavaScript-Frameworks, mit denen wir Anwendungen mit Àhnlichen Funktionen mithilfe eines generischen Ansatzes erstellen können. Viele Anwendungen benötigen jedoch nicht die gesamte Leistung, die Frameworks bieten. Die Verwendung eines Frameworks in einem kleinen oder mittleren Projekt, das bestimmte spezifische Anforderungen stellt, kann durchaus eine unnötige Zeit- und Energieverschwendung sein.

Bild

In diesem Artikel werden wir ĂŒber den Einsatz moderner Technologien bei der Entwicklung von Webanwendungen sprechen, deren Funktionen nicht durch die Funktionen von Frameworks eingeschrĂ€nkt sind. Übrigens, wenn Sie dies benötigen, können Sie mit den hier beschriebenen Technologien Ihr eigenes hochspezialisiertes Framework erstellen. Mit reinem JavaScript und anderen grundlegenden Webtechnologien können Entwickler das tun, was sie benötigen, ohne sich auf den Umfang der von ihnen verwendeten Tools zu beschrĂ€nken.

RĂŒckblick


Bevor wir zur Sache kommen, besprechen wir die Tools, die wir benötigen.

▍ Anwendungsarchitektur


Um eine hohe Geschwindigkeit beim Laden der Anwendung und Benutzerfreundlichkeit sicherzustellen, verwenden wir die folgenden Entwurfsmuster:

  • Architektur-App-Shell.
  • PRPL-Muster (Push, Rendering, Pre-Cache, Lazy Loading).

▍ Projekterstellungssystem


In unserem Projekt benötigen wir ein hochwertiges Montagesystem, das auf unsere BedĂŒrfnisse zugeschnitten ist. Hier verwenden wir Webpack und stellen die folgenden Anforderungen fĂŒr das Projektbuildsystem vor:

  • UnterstĂŒtzung fĂŒr ES6 und dynamische Ressourcenimportfunktionen.
  • UnterstĂŒtzung fĂŒr SASS und CSS.
  • Separate Konfiguration der Entwicklungsmodi und der eigentlichen Arbeit der Anwendung.
  • Möglichkeit zur automatischen Konfiguration von Servicemitarbeitern.

▍ Erweiterte JavaScript-Funktionen


Wir werden den Mindestsatz an modernen JavaScript-Funktionen verwenden, mit denen wir das entwickeln können, was wir brauchen. Hier sind die fraglichen Funktionen:

  • Module
  • Verschiedene Möglichkeiten zum Erstellen von Objekten (Objektliterale, Klassen).
  • Dynamischer Import von Ressourcen.
  • Pfeilfunktionen.
  • Vorlagenliterale.

Nachdem wir eine allgemeine Vorstellung davon haben, was wir brauchen, sind wir bereit, mit der Entwicklung unseres Projekts zu beginnen.

Anwendungsarchitektur


Das Aufkommen von Progressive Web Application (PWA) hat zur EinfĂŒhrung neuer Architekturlösungen in der Webentwicklung beigetragen. Dadurch konnten Webanwendungen schneller geladen und angezeigt werden. Die Kombination der App Shell-Architektur und des PRPL-Musters kann dazu fĂŒhren, dass die Webanwendung schnell und reaktionsschnell ist, Ă€hnlich wie bei einer regulĂ€ren Anwendung.

▍ Was ist App Shell und PRPL?


App Shell ist ein Architekturmuster, das zum Entwickeln von PWA verwendet wird. Bei Verwendung wird eine minimale Menge an Ressourcen, die fĂŒr den Betrieb der Site kritisch sind, beim Laden der Site an den Browser des Benutzers gesendet. Die Zusammensetzung dieser Materialien umfasst normalerweise alle Ressourcen, die fĂŒr die erste Anzeige der Anwendung erforderlich sind. Solche Ressourcen können auch mithilfe eines Servicemitarbeiters zwischengespeichert werden.

Die AbkĂŒrzung PRPL wird wie folgt entschlĂŒsselt:

  • Push - Senden kritischer Ressourcen an den Client fĂŒr die Quellroute (insbesondere ĂŒber HTTP / 2).
  • Rendern - Zeigt die ursprĂŒngliche Route an.
  • Pre-Cache - Zwischenspeichern der verbleibenden Routen oder Ressourcen im Voraus.
  • Lazy Load - "Lazy" Laden von Teilen der Anwendung, wenn sie erforderlich werden (insbesondere auf Anfrage des Benutzers).

▍ Implementierung von App Shell und PRPL in Code


App Shepp- und PRPL-Muster werden gemeinsam genutzt. Auf diese Weise können Sie erweiterte AnsĂ€tze fĂŒr die Entwicklung von Webprojekten implementieren. So sieht das App Shell-Muster im Code aus:

<!DOCTYPE html> <html lang="en"> <head>    <meta charset="utf-8" />    <meta name="viewport" content="width=device-width, initial-scale=1.0" />    <meta http-equiv="X-UA-Compatible" content="ie=edge" />    <!-- Critical Styles -->    <!--   №1 -->    <style>        html {            box-sizing: border-box;        }        *,        *:after,        *:before {            box-sizing: inherit;        }        body {            margin: 0;            padding: 0;            font: 18px 'Oxygen', Helvetica;            background: #ececec;        }        header {            height: 60px;            background: #512DA8;            color: #fff;            display: flex;            align-items: center;            padding: 0 40px;            box-shadow: 1px 2px 6px 0px #777;        }        h1 {            margin: 0;        }        .banner {            text-decoration: none;            color: #fff;            cursor: pointer;        }        main {            display: flex;            justify-content: center;            height: calc(100vh - 140px);            padding: 20px 40px;            overflow-y: auto;        }        button {            background: #512DA8;            border: 2px solid #512DA8;            cursor: pointer;            box-shadow: 1px 1px 3px 0px #777;            color: #fff;            padding: 10px 15px;            border-radius: 20px;        }        .button {            display: flex;            justify-content: center;        }        button:hover {            box-shadow: none;        }        footer {            height: 40px;            background: #2d3850;            color: #fff;            display: flex;            align-items: center;            padding: 40px;        }    </style>    <!--   №1 -->    <title>Vanilla Todos PWA</title> </head> <body>    <body>        <!-- Main Application Section -->        <!--   №2 -->        <header>            <h3><font color="#3AC1EF">▍<a class="banner"> Vanilla Todos PWA </a></font></h3>        </header>        <main id="app"></main>        <footer>            <span>© 2019 Anurag Majumdar - Vanilla Todos SPA</span>        </footer>        <!--   №2 -->             <!-- Critical Scripts -->        <!--   №3 -->        <script async src="<%= htmlWebpackPlugin.files.chunks.main.entry %>"></script>        <!--   №3 -->        <noscript>            This site uses JavaScript. Please enable JavaScript in your browser.        </noscript>    </body> </body> </html> 

Nachdem Sie diesen Code studiert haben, können Sie verstehen, dass die App Shell-Vorlage die Erstellung einer „Shell“ einer Anwendung vorsieht, bei der es sich um das „Skelett“ handelt, das ein Minimum an Markup enthĂ€lt. Lassen Sie uns diesen Code analysieren (im Folgenden werden die Codefragmente, auf die wir uns wĂ€hrend der Analyse beziehen, mit Kommentaren wie <!-- №1 --> markiert).

  • Fragment Nr. 1. Die wichtigsten Stile sind in das Markup integriert und werden nicht als separate Dateien dargestellt. Dies geschieht, damit der CSS-Code beim Laden der HTML-Seite direkt verarbeitet wird.
  • Fragment Nr. 2. Hier ist die "Shell" der Anwendung. Diese Bereiche werden spĂ€ter durch JavaScript-Code gesteuert. Dies gilt insbesondere fĂŒr das, was sich im main Tag mit der Bezeichner- app ( <main id="app"></main> ).
  • Fragment Nr. 3. Hier kommen Skripte ins Spiel. Mit dem async Attribut können Sie den Parser beim Laden des Skripts nicht blockieren.

Das obige „Skelett“ der Anwendung implementiert die Push- und Render-Schritte des PRPL-Musters. Dies geschieht, wenn der Browser den HTML-Code analysiert, um eine visuelle Darstellung der Seite zu erhalten. Gleichzeitig findet der Browser schnell die Ressourcen, die fĂŒr die Seitenausgabe wichtig sind. ZusĂ€tzlich werden hier Skripte vorgestellt (Fragment Nr. 3), die fĂŒr die Anzeige der ursprĂŒnglichen Route durch Manipulieren des DOM (im Renderschritt) verantwortlich sind.

Wenn wir den Service Worker jedoch nicht zum Zwischenspeichern der „Shell“ der Anwendung verwenden, erhalten wir beispielsweise beim erneuten Laden der Seite keinen Leistungsgewinn.

Der folgende Code zeigt einen Servicemitarbeiter, der das Skelett und alle statischen Ressourcen der Anwendung zwischenspeichert.

 var staticAssetsCacheName = 'todo-assets-v3'; var dynamicCacheName = 'todo-dynamic-v3'; //   №1 self.addEventListener('install', function (event) {   self.skipWaiting();   event.waitUntil(     caches.open(staticAssetsCacheName).then(function (cache) {       cache.addAll([           '/',           "chunks/todo.d41d8cd98f00b204e980.js","index.html","main.d41d8cd98f00b204e980.js"       ]       );     }).catch((error) => {       console.log('Error caching static assets:', error);     })   ); }); //   №1 //   №2 self.addEventListener('activate', function (event) {   if (self.clients && clients.claim) {     clients.claim();   }   event.waitUntil(     caches.keys().then(function (cacheNames) {       return Promise.all(         cacheNames.filter(function (cacheName) {           return (cacheName.startsWith('todo-')) && cacheName !== staticAssetsCacheName;         })         .map(function (cacheName) {           return caches.delete(cacheName);         })       ).catch((error) => {           console.log('Some error occurred while removing existing cache:', error);       });     }).catch((error) => {       console.log('Some error occurred while removing existing cache:', error);   })); }); //   №2 //   №3 self.addEventListener('fetch', (event) => {   event.respondWith(     caches.match(event.request).then((response) => {       return response || fetch(event.request)         .then((fetchResponse) => {             return cacheDynamicRequestData(dynamicCacheName, event.request.url, fetchResponse.clone());         }).catch((error) => {           console.log(error);         });     }).catch((error) => {       console.log(error);     })   ); }); //   №3 function cacheDynamicRequestData(dynamicCacheName, url, fetchResponse) {   return caches.open(dynamicCacheName)     .then((cache) => {       cache.put(url, fetchResponse.clone());       return fetchResponse;     }).catch((error) => {       console.log(error);     }); } 

Lassen Sie uns diesen Code analysieren.

  • Fragment Nr. 1. Durch die Behandlung des install eines Servicemitarbeiters können statische Ressourcen zwischengespeichert werden. Hier können Sie die Ressourcen des "Skeletts" der Anwendung (CSS, JavaScript, Bilder usw.) fĂŒr die erste Route (entsprechend dem Inhalt des "Skeletts") zwischenspeichern. DarĂŒber hinaus können Sie andere Anwendungsressourcen herunterladen, sodass diese ohne Internetverbindung funktionieren können. Das Zwischenspeichern von Ressourcen entspricht zusĂ€tzlich zum Skelett-Zwischenspeichern dem Vor-Cache-Schritt des PRPL-Musters.
  • Fragment Nr. 2. Durch die Verarbeitung eines activate nicht verwendete Caches geleert.
  • Fragment Nr. 3. Diese Codezeilen laden Ressourcen aus dem Cache, wenn sie vorhanden sind. Andernfalls werden Netzwerkanforderungen gestellt. Wenn eine Netzwerkanforderung zum Empfangen einer Ressource gestellt wird, bedeutet dies außerdem, dass diese Ressource noch nicht zwischengespeichert wurde. Eine solche Ressource wird in einem neuen separaten Cache abgelegt. Dieses Skript hilft beim Zwischenspeichern dynamischer Anwendungsdaten.

Bisher haben wir die meisten Architekturlösungen besprochen, die in unserer Anwendung verwendet werden. Das einzige, worĂŒber wir noch nicht gesprochen haben, ist der Lazy-Ladeschritt des PRPL-Musters. Wir werden spĂ€ter darauf zurĂŒckkommen, aber wir werden uns vorerst mit dem Projektmontagesystem befassen.

Projektbuildsystem


Eine gute Architektur allein ohne ein anstÀndiges Projekterstellungssystem reicht nicht aus, um eine hochwertige Anwendung zu erstellen. Hier bietet sich Webpack an. Es gibt andere Tools zum Erstellen von Projekten (Bundler), z. B. Parcel und Rollup. Was wir basierend auf Webpack implementieren werden, kann aber auch auf andere Weise erfolgen.

Hier sprechen wir darĂŒber, wie die Funktionen, an denen wir interessiert sind, mit Plugins fĂŒr Webpack zusammenhĂ€ngen. Auf diese Weise können Sie schnell die Essenz unseres Build-Systems erfassen. Die Auswahl der Plug-Ins fĂŒr den Bundler und die richtige Konfiguration ist der wichtigste Schritt in Richtung eines qualitativ hochwertigen Projekt-Build-Systems. Wenn Sie diese Prinzipien beherrschen, können Sie sie in Zukunft verwenden, wenn Sie an Ihren eigenen Anwendungen arbeiten.

Es ist nicht einfach, Tools wie Webpack von Grund auf neu zu optimieren. In solchen FĂ€llen ist es hilfreich, eine gute Hilfe zur Hand zu haben. Dieser Leitfaden, mit dem der entsprechende Teil dieses Materials geschrieben wurde, war dieser Artikel. Wenn Sie Probleme mit Webpack haben, wenden Sie sich an sie. Erinnern wir uns nun an die Anforderungen fĂŒr das Projektmontagesystem, ĂŒber die wir zu Beginn gesprochen haben, und implementieren sie.

▍UnterstĂŒtzung der Importfunktionen fĂŒr ES6 und dynamische Ressourcen


Um diese Funktionen zu implementieren, benötigen wir Babel, einen beliebten Transporter, mit dem Sie mit ES6-Funktionen geschriebenen Code in Code konvertieren können, der in ES5-Umgebungen ausgefĂŒhrt werden kann. Damit Babel mit Webpack funktioniert, können wir die folgenden Pakete verwenden:

  • @babel/core
  • @babel/plugin-syntax-dynamic-import
  • @babel/preset-env
  • babel-core
  • babel-loader
  • babel-preset-env

Hier ist ein Beispiel fĂŒr eine .babelrc Datei zur Verwendung mit Webpack:

 {   "presets": ["@babel/preset-env"],   "plugins": ["@babel/plugin-syntax-dynamic-import"] } 

Bei der Konfiguration von Babel wird die presets dieser Datei verwendet, um Babel fĂŒr die Kompilierung von ES6 zu ES5 und die plugins Zeile so zu konfigurieren, dass der dynamische Import in Webpack verwendet werden kann.

So wird Babel mit Webpack verwendet (hier ein Ausschnitt aus der Webpack-Einstellungsdatei - webpack.config.js ):

 module.exports = {   entry: {       //     },   output: {       //     },   module: {       rules: [           {               test: /\.js$/,               exclude: /node_modules/,               use: {                   loader: 'babel-loader'               }           }       ]   },   plugins: [       //    ] }; 

Im Abschnitt " rules " dieser Datei wird beschrieben, wie Sie mit dem babel-loader den Transpilationsprozess anpassen. Der Rest dieser Datei ist der KĂŒrze halber weggelassen.

▍SASS- und CSS-UnterstĂŒtzung


Zur UnterstĂŒtzung unseres SASS- und CSS-Projektassemblierungssystems benötigen wir die folgenden Plugins:

  • sass-loader
  • css-loader
  • style-loader
  • MiniCssExtractPlugin

So sieht die Webpack-Einstellungsdatei aus, in die Daten zu diesen Plugins eingegeben werden:

 module.exports = {   entry: {       //     },   output: {       //     },   module: {       rules: [           {               test: /\.js$/,               exclude: /node_modules/,               use: {                   loader: 'babel-loader'               }           },           {               test: /\.scss$/,               use: [                   'style-loader',                   MiniCssExtractPlugin.loader,                   'css-loader',                   'sass-loader'               ]           }       ]   },   plugins: [       new MiniCssExtractPlugin({           filename: '[name].css'       }),   ] }; 

Lader sind im rules registriert. Da wir das Plugin zum Extrahieren von CSS-Stilen verwenden, wird der entsprechende Eintrag im Abschnitt plugins eingegeben.

▍ Separate Einstellung der Entwicklungsmodi und der tatsĂ€chlichen Arbeit der Anwendung


Dies ist ein Ă€ußerst wichtiger Teil des Anwendungserstellungsprozesses. Jeder weiß, dass beim Erstellen einer Anwendung einige Einstellungen verwendet werden, um die Version zu erstellen, die wĂ€hrend der Entwicklung verwendet wird, wĂ€hrend andere fĂŒr die Produktionsversion verwendet werden. Hier ist eine Liste von Paketen, die hier nĂŒtzlich sind:

  • clean-webpack-plugin : um den Inhalt des dist Ordners zu bereinigen.
  • compression-webpack-plugin : Zum Komprimieren des Inhalts des dist Ordners.
  • copy-webpack-plugin : zum Kopieren statischer Ressourcen, z. B. Dateien, aus Ordnern mit den Anwendungsquelldaten in den dist Ordner.
  • html-webpack-plugin : Zum Erstellen der Datei index.html im Ordner dist .
  • webpack-md5-hash : zum Hashing von Anwendungsdateien im Ordner dist .
  • webpack-dev-server : Zum Starten des lokalen Servers, der wĂ€hrend der Entwicklung verwendet wird.

So sieht die resultierende Datei webpack.config.js aus:

 const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const WebpackMd5Hash = require('webpack-md5-hash'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const CompressionPlugin = require('compression-webpack-plugin'); module.exports = (env, argv) => ({   entry: {       main: './src/main.js'   },   devtool: argv.mode === 'production' ? false : 'source-map',   output: {       path: path.resolve(__dirname, 'dist'),       chunkFilename:           argv.mode === 'production'               ? 'chunks/[name].[chunkhash].js'               : 'chunks/[name].js',       filename:           argv.mode === 'production' ? '[name].[chunkhash].js' : '[name].js'   },   module: {       rules: [           {               test: /\.js$/,               exclude: /node_modules/,               use: {                   loader: 'babel-loader'               }           },           {               test: /\.scss$/,               use: [                   'style-loader',                   MiniCssExtractPlugin.loader,                   'css-loader',                   'sass-loader'               ]           }       ]   },   plugins: [       new CleanWebpackPlugin('dist', {}),       new MiniCssExtractPlugin({           filename:               argv.mode === 'production'                   ? '[name].[contenthash].css'                   : '[name].css'       }),       new HtmlWebpackPlugin({           inject: false,           hash: true,           template: './index.html',           filename: 'index.html'       }),       new WebpackMd5Hash(),       new CopyWebpackPlugin([           // {           // from: './src/assets',           // to: './assets'           // },           // {           // from: 'manifest.json',           // to: 'manifest.json'           // }       ]),       new CompressionPlugin({           algorithm: 'gzip'       })   ],   devServer: {       contentBase: 'dist',       watchContentBase: true,       port: 1000   } }); 

Die gesamte Webpack-Konfiguration wird als Funktion dargestellt, die zwei Argumente akzeptiert. Hier wird das Argument argv verwendet, das die Argumente darstellt, die an diese Funktion ĂŒbergeben werden, wenn die webpack oder webpack-dev-server webpack . So sieht die Beschreibung dieser Befehle in der package.json :

 "scripts": {   "build": "webpack --mode production && node build-sw",   "serve": "webpack-dev-server --mode=development --hot", }, 

Wenn wir den npm run build ausfĂŒhren, wird die Produktionsversion der Anwendung erstellt. Wenn Sie den Befehl npm run serve , wird der Entwicklungsserver gestartet und unterstĂŒtzt die Arbeit an der Anwendung.

Die plugins und devServer obigen Datei zeigen, wie Plugins und der Entwicklungsserver konfiguriert werden.

In dem Abschnitt, der mit dem new CopyWebpackPlugin beginnt, geben Sie die Ressourcen an, die Sie aus den Anwendungsquellmaterialien kopieren möchten.

▍Einrichten eines Servicemitarbeiters


Wir alle wissen, dass das manuelle Erstellen von Dateilisten, die beispielsweise zum Zwischenspeichern bestimmt sind, eine ziemlich langweilige Aufgabe ist. Daher verwenden wir hier ein spezielles Service Worker-Assembly-Skript, das die Dateien im Ordner dist und sie als Cache-Inhalt in die Service Worker-Vorlage einfĂŒgt. Danach wird die Service Worker-Datei in den dist Ordner geschrieben. Die Konzepte, ĂŒber die wir bei der Bewerbung bei Servicemitarbeitern gesprochen haben, Ă€ndern sich nicht. Hier ist der build-sw.js :

 const glob = require('glob'); const fs = require('fs'); const dest = 'dist/sw.js'; const staticAssetsCacheName = 'todo-assets-v1'; const dynamicCacheName = 'todo-dynamic-v1'; //   №1 let staticAssetsCacheFiles = glob   .sync('dist/**/*')   .map((path) => {       return path.slice(5);   })   .filter((file) => {       if (/\.gz$/.test(file)) return false;       if (/sw\.js$/.test(file)) return false;       if (!/\.+/.test(file)) return false;       return true;   }); //   №1 const stringFileCachesArray = JSON.stringify(staticAssetsCacheFiles); //   №2 const serviceWorkerScript = `var staticAssetsCacheName = '${staticAssetsCacheName}'; var dynamicCacheName = '${dynamicCacheName}'; self.addEventListener('install', function (event) {   self.skipWaiting();   event.waitUntil(     caches.open(staticAssetsCacheName).then(function (cache) {       cache.addAll([           '/',           ${stringFileCachesArray.slice(1, stringFileCachesArray.length - 1)}       ]       );     }).catch((error) => {       console.log('Error caching static assets:', error);     })   ); }); self.addEventListener('activate', function (event) {   if (self.clients && clients.claim) {     clients.claim();   }   event.waitUntil(     caches.keys().then(function (cacheNames) {       return Promise.all(         cacheNames.filter(function (cacheName) {           return (cacheName.startsWith('todo-')) && cacheName !== staticAssetsCacheName;         })         .map(function (cacheName) {           return caches.delete(cacheName);         })       ).catch((error) => {           console.log('Some error occurred while removing existing cache:', error);       });     }).catch((error) => {       console.log('Some error occurred while removing existing cache:', error);   })); }); self.addEventListener('fetch', (event) => {   event.respondWith(     caches.match(event.request).then((response) => {       return response || fetch(event.request)         .then((fetchResponse) => {             return cacheDynamicRequestData(dynamicCacheName, event.request.url, fetchResponse.clone());         }).catch((error) => {           console.log(error);         });     }).catch((error) => {       console.log(error);     })   ); }); function cacheDynamicRequestData(dynamicCacheName, url, fetchResponse) {   return caches.open(dynamicCacheName)     .then((cache) => {       cache.put(url, fetchResponse.clone());       return fetchResponse;     }).catch((error) => {       console.log(error);     }); } `; //   №2 //   №3 fs.writeFile(dest, serviceWorkerScript, function(error) {   if (error) return;   console.log('Service Worker Write success'); }); //   №3 

Lassen Sie uns diesen Code analysieren.

  • Fragment Nr. 1. Hier wird die Liste der Dateien aus dem Ordner dist im Array staticAssetsCacheFiles .
  • Fragment Nr. 2. Dies ist die Service-Worker-Vorlage, ĂŒber die wir gesprochen haben. Beim Generieren des fertigen Codes werden Variablen verwendet. Dies macht die Vorlage universell und ermöglicht es Ihnen, sie in Zukunft wĂ€hrend der Entwicklung des Projekts zu verwenden. Wir benötigen auch eine Vorlage, da wir Informationen zum Inhalt des dist Ordners hinzufĂŒgen, die sich im Laufe der Zeit Ă€ndern können. Hierzu wird die Konstante stringFileCachesArray .
  • Fragment Nr. 3. Hier wird der neu generierte Service Worker-Code, der in der serviceWorkerScript Konstante gespeichert ist, in die Datei unter dist/sw.js

Verwenden Sie zum AusfĂŒhren dieses Skripts den Befehl node build-sw . Es muss ausgefĂŒhrt werden, nachdem der webpack --mode production .

Das hier vorgestellte Skript zum Erstellen eines Servicemitarbeiters vereinfacht das Organisieren des Datei-Caching erheblich. Es ist zu beachten, dass dieses Skript bereits in einem realen Projekt Anwendung gefunden hat.

Wenn Sie eine spezielle Bibliothek verwenden möchten, um das Problem der Offline-Arbeit mit progressiven Webanwendungen zu lösen, werfen Sie einen Blick auf Workbox . Es hat sehr interessante anpassbare Funktionen.

▍ Übersicht der im Projekt verwendeten Pakete


Hier ist die Datei package.json unseres Projekts, in der Sie Informationen zu den in diesem Projekt verwendeten Paketen finden:

 { "name": "vanilla-todos-pwa", "version": "1.0.0", "description": "A simple todo application using ES6 and Webpack", "main": "src/main.js", "scripts": {   "build": "webpack --mode production && node build-sw",   "serve": "webpack-dev-server --mode=development --hot" }, "keywords": [], "author": "Anurag Majumdar", "license": "MIT", "devDependencies": {   "@babel/core": "^7.2.2",   "@babel/plugin-syntax-dynamic-import": "^7.2.0",   "@babel/preset-env": "^7.2.3",   "autoprefixer": "^9.4.5",   "babel-core": "^6.26.3",   "babel-loader": "^8.0.4",   "babel-preset-env": "^1.7.0",   "clean-webpack-plugin": "^1.0.0",   "compression-webpack-plugin": "^2.0.0",   "copy-webpack-plugin": "^4.6.0",   "css-loader": "^2.1.0",   "html-webpack-plugin": "^3.2.0",   "mini-css-extract-plugin": "^0.5.0",   "node-sass": "^4.11.0",   "sass-loader": "^7.1.0",   "style-loader": "^0.23.1",   "terser": "^3.14.1",   "webpack": "^4.28.4",   "webpack-cli": "^3.2.1",   "webpack-dev-server": "^3.1.14",   "webpack-md5-hash": "0.0.6" } } 

Wenn wir ĂŒber die UnterstĂŒtzung solcher Projekte sprechen, sollte beachtet werden, dass die Tools im Webpack-Ökosystem ziemlich oft aktualisiert werden. Es kommt hĂ€ufig vor, dass vorhandene Plugins durch neue ersetzt werden. Daher ist es wichtig, sich bei der Entscheidung, ob neuere Pakete anstelle einiger Pakete verwendet werden sollen, nicht auf die Pakete selbst zu konzentrieren, sondern auf die Funktionen, die sie implementieren sollten. Genau deshalb haben wir oben ĂŒber die Rolle gesprochen, die dieses oder jenes Paket spielt.

Moderne JavaScript-Funktionen


WĂ€hrend der Entwicklung einer Webanwendung hat der Programmierer die Wahl, ob er seine eigenen Implementierungen von Funktionen wie Änderungserkennung, Routing, Datenspeicherung schreiben oder vorhandene Pakete verwenden möchte.

Jetzt werden wir ĂŒber die Mindestanzahl von Technologien sprechen, die erforderlich sind, um den Betrieb unseres Projekts sicherzustellen. Bei Bedarf können diese Technologien mithilfe vorhandener Frameworks oder Pakete erweitert werden.

▍ Module


Wir werden die Funktionen von ES6 zum Importieren und Exportieren von Modulen verwenden, wobei jede Datei als ES6-Modul betrachtet wird. Diese Funktion wird hÀufig in gÀngigen Frameworks wie Angular und React verwendet. Die Verwendung ist sehr praktisch. Dank der Konfiguration von Webpack können wir den Ausdruck von Import- und Exportressourcen verwenden. So sieht es in der Datei app.js :

 import { appTemplate } from './app.template'; import { AppModel } from './app.model'; export const AppComponent = { //   App... }; 

▍Verschiedene Möglichkeiten zum Erstellen von Objekten


Die Erstellung von Komponenten ist ein wichtiger Bestandteil unseres Anwendungsentwicklungsprozesses. Hier ist es durchaus möglich, ein modernes Tool wie Webkomponenten zu verwenden. Um das Projekt jedoch nicht zu komplizieren, verwenden wir normale JavaScript-Objekte, die entweder mit Objektliteralen oder mit der im ES6-Standard angegebenen Klassensyntax erstellt werden können .

Die Besonderheit bei der Verwendung von Klassen zum Erstellen von Objekten besteht darin, dass Sie nach der Beschreibung der Klasse eine Instanz des Objekts auf ihrer Basis erstellen und dieses Objekt dann exportieren mĂŒssen. Um die Dinge noch stĂ€rker zu vereinfachen, werden wir hier gewöhnliche Objekte verwenden, die mit Objektliteralen erstellt wurden. Hier ist der Code fĂŒr die Datei app.js , in der Sie deren Anwendung sehen können.

 import { appTemplate } from './app.template'; import { AppModel } from './app.model'; export const AppComponent = {   init() {       this.appElement = document.querySelector('#app');       this.initEvents();       this.render();   },   initEvents() {       this.appElement.addEventListener('click', event => {           if (event.target.className === 'btn-todo') {               import( /* webpackChunkName: "todo" */ './todo/todo.module')                   .then(lazyModule => {                       lazyModule.TodoModule.init();                   })                   .catch(error => 'An error occurred while loading Module');           }       });       document.querySelector('.banner').addEventListener('click', event => {           event.preventDefault();           this.render();       });   },   render() {       this.appElement.innerHTML = appTemplate(AppModel);   } }; 

Hier bilden und exportieren wir die AppComponent Komponente, die Sie sofort in anderen Teilen der Anwendung verwenden können.

In solchen Situationen können Sie sehr gut ES6-Klassen oder Webkomponenten verwenden und ein Projekt in einem Stil entwickeln, der nÀher an der Deklaration liegt als der hier verwendete. Um das Schulungsprojekt nicht zu komplizieren, wird hier ein zwingender Ansatz verwendet.

▍Dynamischer Import von Ressourcen


Denken Sie daran, dass wir beim PRPL-Muster noch nicht herausgefunden haben, welcher Teil davon durch den Buchstaben L (Lazy Loading) dargestellt wird. Der dynamische Import von Ressourcen hilft uns dabei, das verzögerte Laden von Komponenten oder Modulen zu organisieren. Da wir die App Shell-Architektur und das PRPL-Muster verwenden, um das "Skelett" der Anwendung und ihrer Ressourcen zwischenzuspeichern, lÀdt der dynamische Importprozess Ressourcen aus dem Cache und nicht aus dem Netzwerk.

Beachten Sie, dass die verbleibenden Anwendungsressourcen, dh der Inhalt des chunks Ordners, nicht zwischengespeichert werden, wenn nur die App Shell-Architektur verwendet wird.

Ein Beispiel fĂŒr den dynamischen Ressourcenimport ist im obigen Codefragment der AppComponent Komponente zu sehen, insbesondere dort, wo das AppComponent der SchaltflĂ€che konfiguriert ist (es handelt sich um die Methode des initEvents() ). Wenn der Benutzer der Anwendung auf die btn-todo klickt, wird das Modul btn-todo geladen. Dieses Modul ist eine regulĂ€re JavaScript-Datei, die eine Reihe von Komponenten enthĂ€lt, die als Objekte dargestellt werden.

▍ Pfeilfunktionen und Vorlagenliterale


Pfeilfunktionen sind besonders nĂŒtzlich, wenn Sie das this in solchen Funktionen benötigen, um den Kontext anzugeben, in dem die Funktion deklariert ist. DarĂŒber hinaus können Sie mit Pfeilfunktionen Code schreiben, der kompakter ist als herkömmliche Funktionen. Hier ist ein Beispiel fĂŒr eine solche Funktion:

 export const appTemplate = model => `   <section class="app">       <h3><font color="#3AC1EF">▍ ${model.title} </font></h3>       <section class="button">           <button class="btn-todo"> Todo Module </button>       </section>   </section> `; 

Die appTemplate ein Modell ( model ) und gibt eine HTML-Zeichenfolge zurĂŒck, die Daten aus dem Modell enthĂ€lt. . , - .

, , . reduce() HTML-:

 const WorkModel = [   {       id: 1,       src: '',       alt: '',       designation: '',       period: '',       description: ''   },   {       id: 2,       src: '',       alt: '',       designation: '',       period: '',       description: ''   },   //... ]; const workCardTemplate = (cardModel) => ` <section id="${cardModel.id}" class="work-card">   <section class="work__image">       <img class="work__image-content" type="image/svg+xml" src="${           cardModel.src       }" alt="${cardModel.alt}" />   </section>   <section class="work__designation">${cardModel.designation}</section>   <section class="work__period">${cardModel.period}</section>   <section class="work__content">       <section class="work__content-text">           ${cardModel.description}       </section>   </section> </section> `; export const workTemplate = (model) => ` <section class="work__section">   <section class="work-text">       <header class="header-text">           <span class="work-text__header"> Work </span>       </header>       <section class="work-text__content content-text">           <p class="work-text__content-para">               This area signifies work experience           </p>       </section>   </section>   <section class="work-cards">       ${model.reduce((html, card) => html + workCardTemplate(card), '')}   </section> </section> `; 

, . . , , .

:

  • model . — , reduce() , .
  • model.reduce , HTML-, , . , , , — .

. , , — .


Todo-, . .




. . , , , .

-


-, , -, . , , . , JavaScript -, .

, .


-

. Lighthouse.




Zusammenfassung


, , JavaScript, . , , , .

Liebe Leser! -, ?

Source: https://habr.com/ru/post/de444342/


All Articles