web-dev-qa-db-fra.com

Angular Application générée par CLI avec Web Workers

En lisant la documentation Angular, vous pouvez trouver plusieurs références à bootstrap votre ensemble Angular app dans un site Web) Worker , afin que votre interface utilisateur ne soit pas bloquée par une utilisation intensive de JS.

Cependant, en ce moment, il n'y a aucune information officielle sur la façon de le faire, et la seule information dans le Angular Doc. Est qu'il s'agit d'une fonctionnalité expérimentale.

Comment puis-je utiliser cette approche pour tirer parti des travailleurs Web dans Angular?

28
Enrique Oriol

Pour Angular 7, voir la réponse ci-dessous.

J'ai passé beaucoup de temps à comprendre comment le faire, alors j'espère cela peut aider quelqu'un .

Conditions préalables

Je suppose que vous avez un projet Angular (version 2 ou 4) généré avec Angular CLI 1.0 ou supérieur).

Il n'est pas obligatoire de générer le projet avec CLI pour suivre ces étapes, mais les instructions que je donnerai concernant le fichier webpack, sont basées sur la configuration du webpack CLI.

Pas

1. Extraire le fichier webpack

Depuis Angular CLI v1.0, il y a la fonction "éjecter", qui vous permet d'extraire le fichier de configuration du webpack et de le manipuler comme vous le souhaitez.

  • Courir ng eject so Angular CLI génère le fichier webpack.config.js .

  • Courir npm install pour que les nouvelles dépendances générées par CLI soient satisfaites

2. Installez webworker bootstrap dépendances

Courir npm install --save @angular/platform-webworker @angular/platform-webworker-dynamic

3. Modifications du bootstrap du thread d'interface utilisateur

3.1 Changements dans app.module.ts

Remplacez BrowserModule par WorkerAppModule dans le fichier app.module.ts. Vous devrez également mettre à jour la déclaration d'importation afin d'utiliser @angular/platform-webworker bibliothèque.

//src/app/app.module.ts

import { WorkerAppModule } from '@angular/platform-webworker';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
//...other imports...

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    WorkerAppModule,
    //...other modules...
  ],
  providers: [/*...providers...*/],
  bootstrap: [AppComponent]
})
export class AppModule { }

3.2 Changements dans src/main.ts

Remplacez bootstrap par: bootstrapWorkerUI (mettez également à jour l'importation).

Vous devrez transmettre une URL avec le fichier dans lequel le travailleur Web est défini. Utilisez un fichier appelé webworker.bundle.js, ne vous inquiétez pas, nous allons bientôt créer ce fichier.

//main.ts

import { enableProdMode } from '@angular/core';
import { bootstrapWorkerUi } from '@angular/platform-webworker';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

bootstrapWorkerUi('webworker.bundle.js');

3.3 Créer un fichier workerLoader.ts

  • Créez un nouveau fichier src/workerLoader.ts .
  • Comme votre Web Worker sera un fichier unique contenant tous les éléments requis, vous devez inclure polyfills.ts, @angular/core, et @angular/common paquets. Dans les étapes suivantes, vous mettrez à jour Webpack afin de transpiler et de créer un bundle avec le résultat.
  • Importer platformWorkerAppDynamic
  • Importez le AppModule (supprimez l'importation de main.ts ) et bootstrap it using this platformWorkerAppDynamic plateforme.
// workerLoader.ts

import 'polyfills.ts';
import '@angular/core';
import '@angular/common';

import { platformWorkerAppDynamic } from '@angular/platform-webworker-dynamic';
import { AppModule } from './app/app.module';

platformWorkerAppDynamic().bootstrapModule(AppModule);

4. Mettez à jour le webpack pour construire votre webworker

Le fichier de configuration généré automatiquement par webpack est assez long, mais vous n'aurez qu'à concentrer votre attention sur les éléments suivants:

  • Ajoutez un webworker point d'entrée pour notre workerLoader.ts fichier. Si vous regardez la sortie, vous verrez qu'elle attache un préfixe bundle.js à tous les morceaux. C’est pourquoi lors de l’étape bootstrap nous avons utilisé webworker.bundle.js

  • Accédez à HtmlWebpackPlugin et excluez le point d'entrée webworker, afin que le fichier Web Worker généré ne soit pas inclus dans le fichier index.html.

  • Accédez à CommonChunksPlugin , et pour le bloc commun inline, définissez explicitement les blocs d'entrée pour empêcher que webworker soit inclus .

  • Allez dans AotPlugin et définissez explicitement le entryModule

// webpack.config.js

//...some stuff...
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CommonsChunkPlugin } = require('webpack').optimize;
const { AotPlugin } = require('@ngtools/webpack');
//...some stuff...

module.exports = {
  //...some stuff...
  "entry": {
    "main": [
      "./src/main.ts"
    ],
    "polyfills": [
      "./src/polyfills.ts"
    ],
    "styles": [
      "./src/styles.css"
    ],
    "webworker": [
      "./src/workerLoader.ts"
    ]
  },
  "output": {
    "path": path.join(process.cwd(), "dist"),
    "filename": "[name].bundle.js",
    "chunkFilename": "[id].chunk.js"
  },
  "module": { /*...a lot of stuff...*/ },
  "plugins": [
    //...some stuff...
    new HtmlWebpackPlugin({
      //...some stuff...
      "excludeChunks": [
        "webworker"
      ],
      //...some more stuff...
    }),
    new BaseHrefWebpackPlugin({}),
    new CommonsChunkPlugin({
      "name": "inline",
      "minChunks": null,
      "chunks": [
        "main",
        "polyfills",
        "styles"
      ]
    }),
    //...some stuff...
    new AotPlugin({
      "mainPath": "main.ts",
      "entryModule": "app/app.module#AppModule",
      //...some stuff...
    })
  ],
  //...some more stuff...
};

Tu es prêt

Si vous avez correctement suivi les étapes précédentes, il ne vous reste plus qu'à compiler le code et essayer les résultats.

Courir npm start

Toute la logique de votre application Angular doit s'exécuter à l'intérieur d'un WebWorker, ce qui rend l'interface plus fluide.

Plus de notes

npm start exécute le serveur webpack-dev , et il a une sorte de problème avec les travailleurs Web qui lancent un message d'erreur dans le journal de la console. Quoi qu'il en soit, le webworker semble bien fonctionner. Si vous compilez l'application à l'aide de la commande webpack et la servez de n'importe quel serveur http comme simplehttpserver , l'erreur disparaît;)

Exemple de code et démo

Vous pouvez obtenir le code complet (config webpack, app.module.ts, ...) à partir de ce dépôt .

Vous pouvez également regarder ici une démo en direct , pour vérifier les différences entre l'utilisation de Web Workers ou non

72
Enrique Oriol

Bonne nouvelle les gars, j'ai réussi à travailler avec Angular 7! ????????????

Exigence: npm install --save-dev @angular-builders/custom-webpack html-webpack-pluginAssurez-vous d'avoir production:true dans votre fichier env si vous voulez juste copier/coller le code ci-dessous.

Étape 1: Modifiez votre fichier angular.json de la manière suivante:

"architect": {
        "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {
            "customWebpackConfig": {
                "path": "./webpack.client.config.js",
                "replaceDuplicatePlugins": true
             },
            ...
          }

Vous modifiez uniquement la partie build parce que vous n'avez pas vraiment besoin de tout le travail dans le serveur de développement.

Étape 2: Créez webpack.client.config.js fichier à la racine de votre projet. Si vous n'utilisez pas SSR, vous pouvez supprimer exclude: ['./server.ts'],

const path = require('path');
const webpack = require('webpack');

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { AngularCompilerPlugin } = require('@ngtools/webpack');

module.exports = {
  entry: {
    webworker: [
      "./src/workerLoader.ts"
    ],
    main: [
      "./src/main.ts"
    ],
    polyfills: [
      "./src/polyfills.ts"
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      excludeChunks: [
        "webworker"
      ],
      chunksSortMode: "none"
    }),
    new AngularCompilerPlugin({
      mainPath: "./src/main.ts",
      entryModule: './src/app/app.module#AppModule',
      tsConfigPath: "src/tsconfig.app.json",
      exclude: ['./server.ts'],
      sourceMap: true,
      platform: 0
    }),
  ],
  optimization: {
    splitChunks: {
      name: "inline"
    }
  }
}

Étape: Modifiez votre AppModule:

import { BrowserModule } from '@angular/platform-browser'
import { WorkerAppModule } from '@angular/platform-webworker'
const AppBootstrap =
            environment.production
            ? WorkerAppModule
            : BrowserModule.withServerTransition({ appId: 'myApp' })
    imports: [
        ...
        AppBootstrap,
        ...
    ]

Étape 4: Modifiez votre fichier main.ts.

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { bootstrapWorkerUi } from '@angular/platform-webworker';

import {AppModule} from './app/app.module';
import {environment} from './environments/environment';

if (environment.production) {
  enableProdMode();
  bootstrapWorkerUi('webworker.bundle.js');
}

else {
  document.addEventListener('DOMContentLoaded', () => {
    platformBrowserDynamic().bootstrapModule(AppModule);
  });
}

Étape 5: Il se compilera très bien, mais vous pouvez avoir un problème d'exécution en raison de la manipulation DOM dans votre application. À ce stade, il vous suffit de supprimer toute manipulation DOM et de la remplacer par autre chose. Je suis toujours en train de trouver cette partie et je modifierai ma réponse plus tard pour donner des instructions sur ce problème.

Si vous ne faites pas de manipulation DOM sauvage, alors vous êtes prêt à utiliser un thread principal gratuit et l'audit de votre application à l'aide de lighthouse ne devrait pas afficher Minimize main-thread work plus, car la plupart de votre application, à l'exception de l'interface utilisateur, se charge dans un deuxième thread.

2
Guillaume Le Mière