Angular5带进度追踪的Http请求引发路由异常问题
看起来你的路由异常问题大概率是手动调用detectChanges()干扰了Angular的变更检测周期,再加上上传代码里的异步逻辑瑕疵共同导致的,我来一步步帮你解决:
核心问题:手动变更检测打破了Angular的默认机制
Angular的HTTP请求(包括进度事件)本身是运行在NgZone中的,默认会自动触发变更检测,你不需要手动调用this.ref.detectChanges()。频繁或不恰当的手动检测会导致组件的变更检测状态紊乱,比如让组件脱离了Angular的自动检测周期——这就解释了为什么路由跳转后视图不更新、两个组件同时显示,而点击外部的搜索栏(触发了一次全局变更检测)后又恢复正常。
具体修复步骤
1. 移除所有手动detectChanges()调用
把上传函数里的两处this.ref.detectChanges()全部删掉,Angular会自动处理HTTP事件带来的视图更新。如果确实遇到某些特殊场景需要手动触发(比如非NgZone内的异步操作),也应该用this.ref.markForCheck()替代,它只会标记组件需要在下一个检测周期被检查,不会直接触发检测,更安全。
2. 管理HTTP订阅,避免内存泄漏与组件残留
你的上传请求订阅如果没有在组件销毁时取消,会导致组件实例无法被正确回收,这也是路由跳转后旧组件依然显示的可能原因之一:
- 在组件类中定义一个订阅对象:
private uploadSubscription?: Subscription;
- 在订阅HTTP请求时赋值:
this.uploadSubscription = this.http.request(req).subscribe(...);
- 添加组件销毁钩子,取消订阅:
ngOnDestroy() { this.uploadSubscription?.unsubscribe(); }
3. 修正上传代码的异步逻辑瑕疵
你当前的代码在循环里创建HttpRequest,然后在fileEntry.file()的异步回调里添加文件到FormData——这里存在逻辑问题:循环会先执行完,而回调是异步的,导致最后发送请求时可能前面的文件还没被添加到FormData里(虽然你说上传功能正常,但这是浏览器FormData的引用特性带来的巧合,逻辑上不严谨)。
改用Promise.all等待所有文件都添加完成后再发送请求:
upload(){ this.showFilesToUpload = false; this.showProgressBar = true; const headers = new HttpHeaders().set('x-access-token', localStorage.getItem('x-access-token')); const formData = new FormData(); // 收集所有文件读取的Promise const filePromises = this.files.map(fileItem => { return new Promise<void>((resolve) => { fileItem.fileEntry.file(file => { formData.append("fileupload", file, fileItem.relativePath); resolve(); }); }); }); // 等待所有文件添加完毕后再发送请求 Promise.all(filePromises).then(() => { const req = new HttpRequest('POST', this.domain + 'api/uploadpdf', formData, { reportProgress: true, headers: headers }); this.uploadSubscription = this.http.request(req).subscribe(event => { if (event.type === HttpEventType.UploadProgress) { this.setProzentUploaded(Math.round(100 * event.loaded / event.total)); // 这里不再需要detectChanges() } else if (event instanceof HttpResponse) { console.log(event); swal({ title: 'All Documents Uploaded!', type: 'success' }).then((isConfirm) => { if (isConfirm) { this.showProgressBar = false; this.showFilesToUpload = true; this.files = []; this.progressBarValue = 0; // 这里也不需要detectChanges() } }); } }, error => { // 别忘了处理上传错误,避免组件一直停留在加载状态 console.error('Upload failed:', error); this.showProgressBar = false; this.showFilesToUpload = true; }); }); }
验证与额外建议
做完以上修改后,重新测试上传后的路由跳转:
- 路由应该能正常切换,视图只会显示目标组件
- 路由按钮的功能也会恢复正常
如果问题依然存在,可以检查:
- 组件是否有手动调用
this.ref.detach()的代码,如果有,需要在合适的时机调用this.ref.reattach() - 确保你的路由配置没有错误,比如没有重复的路由路径
内容的提问来源于stack exchange,提问作者Daniel Klotzsche

