Angular 使用 Web Worker 生成的 CLI 应用程序

Angular CLI generated app with Web Workers

阅读 Angular 文档,您可以找到几个 references to bootstrap your whole Angular app inside a Web Worker,因此您的 UI 不会因大量使用 JS 而受阻。

然而,目前还没有关于如何做到这一点的官方信息,而且只有 Angular 文档中的信息。这是一个实验性功能。

如何使用这种方法来利用 Angular 中的网络工作者?

对于Angular 7,请参阅下面的答案。

我花了很多时间弄清楚怎么做,所以我希望this can help someone

先决条件

我假设您有一个使用 Angular CLI 1.0 或更高版本生成的 Angular 项目(版本 2 或 4)。

并非必须使用 CLI 生成项目才能遵循此步骤,但我给出的与 webpack 文件相关的说明基于 CLI webpack 配置。

步骤

1。提取 webpack 文件

从 Angular CLI v1.0 开始,有“弹出”功能,允许您提取 webpack 配置文件并根据需要对其进行操作。

  • 运行 ng eject 所以 Angular CLI 生成 webpack.config.js 文件。

  • 运行 npm install 这样CLI生成的新依赖就满足了

2。安装 webworker bootstrap 依赖项

运行 npm install --save @angular/platform-webworker @angular/platform-webworker-dynamic

3。 UI 线程中的更改 bootstrap

3.1 app.module.ts

中的变化

在 app.module.ts 文件中将 BrowserModule 替换为 WorkerAppModule。您还需要更新导入语句才能使用 @angular/platform-webworker 库。

//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 src/main.ts

中的变化

将 bootstrap 过程替换为:bootstrapWorkerUI(同时更新导入)。

您需要将 URL 与定义网络工作者的文件一起传递。使用名为 webworker.bundle.js 的文件,别担心,我们很快就会创建这个文件。

//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 创建workerLoader.ts文件

  • 创建一个新文件src/workerLoader.ts.
  • 由于您的 Web Worker 将是包含所有必需内容的单个文件,因此您需要包含 polyfills.ts@angular/core@angular/common 包。在接下来的步骤中,您将更新 Webpack 以便使用结果转译和构建一个包。
  • 导入platformWorkerAppDynamic
  • 使用此 platformWorkerAppDynamic 平台导入 AppModule(从 main.ts 中删除导入)和 bootstrap 它。

// 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。更新 webpack 以构建您的 webworker

webpack 自动生成的配置文件比较长,但你只需要关注以下几点:

  • 为我们的 workerLoader.ts 文件添加一个 webworker 入口点 。如果您查看输出,您会发现它为所有块附加了一个 bundle.js 前缀。这就是为什么在 bootstrap 步骤中我们使用了 webworker.bundle.js

  • 转到HtmlWebpackPlugin并排除webworker入口点,所以生成的Web Worker文件不包含在index.html文件.

  • 转到CommonChunksPlugin,对于inline公共块,显式设置入口块以防止包含webworker .

  • 转到 AotPlugin 并明确设置 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...
};

你准备好了

如果前面的步骤都正确的话,现在你只需要编译代码,试试结果就可以了。

运行 npm start

您的 Angular 应用程序的所有逻辑都应该 运行 放在 WebWorker 中,从而使 UI 更加流畅。

进一步说明

npm start 运行s webpack-dev 服务器,它有一些问题,网络工作者在控制台日志上抛出错误消息。无论如何,网络工作者似乎 运行 没问题。 如果您使用 webpack 命令编译应用程序并从任何像 simplehttpserver 这样的 http 服务器提供它,错误就会消失 ;)

示例代码和演示

您可以从 this repo.

获取完整代码(webpack 配置,app.module.ts,...)

您也可以在这里观看 live demo,了解是否使用 Web Workers 的区别

伙计们好消息,我得到了这个与 Angular 7 一起工作!

要求npm install --save-dev @angular-builders/custom-webpack html-webpack-plugin 如果您只想从下面 copy/past 代码,请确保您的 env 文件中有 production:true

第 1 步:按以下方式编辑您的 angular.json 文件:

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

您只是在编辑 build 部分,因为您实际上并不需要开发服务器中的整个工作人员。

步骤 2:在项目的根目录下创建 webpack.client.config.js 文件。 如果您不使用 SSR,您可以删除 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"
    }
  }
}

步骤 3:编辑您的 AppModule:

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

步骤 4:编辑您的 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);
  });
}

第 5 步:它会编译得很好,但由于在您的应用程序中进行 DOM 操作,您可能会遇到运行时问题。此时,您只需删除任何 DOM 操作并将其替换为其他内容。我仍在努力解决这部分问题,稍后会编辑我的答案以指导有关此问题的方向。

如果您不进行野蛮的 DOM 操作,那么您最好使用免费的主线程,并且使用灯塔审核您的应用程序不应再显示 Minimize main-thread work,因为大多数除了 UI 在第二个线程中加载之外的应用程序。