Angular V8构建Web Worker时找不到核心Angular对象
解决Angular 8中Web Worker发起HTTP请求的编译错误
首先,咱们得先搞清楚你遇到的问题根源:Web Worker运行在独立的线程中,没有浏览器DOM环境,而且Angular的服务依赖Angular的注入系统,你直接在Worker里导入Angular核心模块、传递服务实例的做法,本身就不符合Worker的运行规则。下面是一步步的解决方案:
核心问题拆解
你看到的编译错误(找不到Element/Node、找不到.ngfactory.ts文件),本质是:
- Angular的
@angular/core和@angular/common/http模块依赖DOM类型定义,但Web Worker环境不包含这些DOM对象,编译时自然报错。 - 你试图把Angular服务实例传递给Worker,但服务是Angular注入器创建的,包含内部依赖和状态,序列化后会丢失所有功能,根本无法在Worker中正常调用。
解决方案步骤
1. 重构Web Worker:移除Angular依赖,改用原生HTTP请求
Worker里不能用Angular的HttpClient,咱们换成原生fetch或者RxJS的fromFetch来发起请求,完全脱离Angular模块的依赖。
修改你的Worker文件如下(保留核心逻辑,去掉Angular服务导入):
/// <reference lib="webworker" /> import { fromFetch } from 'rxjs/fetch'; import { switchMap, catchError, repeatWhen, delay, tap } from 'rxjs/operators'; import { of } from 'rxjs'; import { SpringBatchJobExecutionStatus } from '../../../../shared/models/SpringBatchJobExecutionStatus'; function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } function postMessageWithLog(message: SpringBatchJobExecutionStatus) { console.log('Sending message back: ' + JSON.stringify(message)); postMessage(message); } self.addEventListener('message', async ({ data }) => { // 从主线程接收必要参数:文件、API地址、请求头 const { file, apiUrls, requestHeaders } = data; console.log('@@@ Begin addEventListener'); // 1. 上传文件的逻辑 const formData = new FormData(); formData.append('file', file); fromFetch(apiUrls.postAsyncFiles, { method: 'POST', body: formData, headers: requestHeaders }).pipe( // 处理上传响应 switchMap(response => { if (!response.ok) throw new Error('Upload failed'); return response.json() as Promise<SpringBatchJobExecutionStatus>; }), tap(postAsyncFilesData => { postAsyncFilesData.httpRequestSuccessful = true; postMessageWithLog(postAsyncFilesData); }), // 2. 轮询任务状态的逻辑 switchMap(postAsyncFilesData => { if (postAsyncFilesData.exitStatus === 'RUNNING' || postAsyncFilesData.batchStatus === 'STARTING') { return fromFetch( `${apiUrls.getAsyncFiles}?jobInstanceId=${postAsyncFilesData.jobInstanceId}&jobExecutionId=${postAsyncFilesData.jobExecutionId}`, { headers: requestHeaders } ).pipe( switchMap(res => res.json() as Promise<SpringBatchJobExecutionStatus>), tap(jobExecutionResult => postMessageWithLog(jobExecutionResult)), repeatWhen(obs => obs.pipe(delay(10000))), // 每10秒轮询一次 catchError(error => { console.log('Error getting job execution status ' + error); const errorBlock = new SpringBatchJobExecutionStatus(); errorBlock.httpRequestSuccessful = false; postMessageWithLog(errorBlock); return of(errorBlock); // 终止轮询 }) ); } else { const errorBlock = { ...postAsyncFilesData, httpRequestSuccessful: false }; console.log(`Not entering check loop: batchStatus=${errorBlock.batchStatus}, exitStatus=${errorBlock.exitStatus}`); postMessageWithLog(errorBlock); return of(errorBlock); } }), // 处理上传失败的情况 catchError(error => { const errorBlock = new SpringBatchJobExecutionStatus(); errorBlock.httpRequestSuccessful = false; postMessageWithLog(errorBlock); return of(errorBlock); }) ).subscribe(); console.log('@@@ addEventListener(...): worker ended'); });
2. 主线程传递参数(而非服务实例)
在你的Angular组件中,创建Worker时传递Worker需要的所有数据(文件、API地址、请求头等),不要传递服务实例:
import { Component } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; @Component({ selector: 'app-upload', templateUrl: './upload.component.html' }) export class UploadComponent { constructor(private http: HttpClient) {} onFileSelected(files: FileList) { if (!files.length) return; // 构造请求头(比如添加认证token等) const headers = new HttpHeaders(); // 示例:headers.set('Authorization', `Bearer ${yourAuthToken}`); // 创建Worker const worker = new Worker('./softcopy-report-file-uploader.worker', { type: 'module' }); // 传递参数给Worker worker.postMessage({ file: files.item(0), apiUrls: { postAsyncFiles: '/api/softlayer/postAsyncFiles', // 替换为你的实际API地址 getAsyncFiles: '/api/softlayer/getAsyncFiles' }, requestHeaders: headers.keys().reduce((acc, key) => { acc[key] = headers.get(key); return acc; }, {} as Record<string, string>) }); // 监听Worker返回的消息 worker.onmessage = ({ data }) => { console.log('Received from worker:', data); // 在这里更新UI状态,比如进度、结果提示等 }; // 监听Worker错误 worker.onerror = (error) => { console.error('Worker error:', error); worker.terminate(); }; } }
3. 配置Worker的TypeScript编译规则
创建/修改tsconfig.worker.json文件,确保Worker编译时只使用Web Worker所需的类型库,排除DOM相关依赖:
{ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/worker", "lib": ["es2018", "webworker"], "types": [] }, "include": [ "src/**/*.worker.ts" ] }
然后修改angular.json的构建配置,指定Worker使用这个tsconfig:
"architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { // 其他配置... "workerTsConfig": "tsconfig.worker.json" } } }
为什么原来的方法行不通?
- Web Worker无DOM环境:Angular核心模块依赖DOM类型(比如
Element、Node),但Worker线程没有这些对象,编译时必然报错找不到类型定义。 - Angular服务无法跨线程传递:Angular服务是通过注入器创建的,包含内部依赖和运行时状态,序列化后传递给Worker会变成普通对象,所有方法都会失效。
内容的提问来源于stack exchange,提问作者Woodsman
相关产品推荐
相关产品推荐

