Angular 6应用启动加载进度展示方案咨询
解决Angular应用初始加载进度提示的方案
嘿,针对你遇到的慢网下vendor.js加载耗时过长的问题,我整理了几个实用的加载进度实现方案,结合你已经启用的PWA/Service Worker,能很好地让用户感知应用加载状态:
一、静态初始进度条(Angular启动前显示)
因为vendor.js加载完成前Angular还未初始化,所以先在index.html里添加一个纯HTML/CSS的进度条,再配合简单JS监听加载状态:
<!-- index.html 头部添加样式与进度条容器 --> <style> .loader-container { position: fixed; top: 0; left: 0; width: 100%; height: 4px; background-color: #f0f0f0; z-index: 9999; } .loader-bar { height: 100%; width: 0%; background-color: #1976d2; /* 匹配Material主题色 */ transition: width 0.3s ease; } </style> <div class="loader-container"> <div class="loader-bar" id="loaderBar"></div> </div> <!-- 页面底部、vendor.js引入前添加监听脚本 --> <script> let loadedBytes = 0; let totalBytes = 0; // 预统计所有初始资源总大小 document.querySelectorAll('link[rel="stylesheet"], script').forEach(el => { const resourceUrl = el.href || el.src; if (resourceUrl) { const xhr = new XMLHttpRequest(); xhr.open('HEAD', resourceUrl, true); xhr.onload = () => { totalBytes += parseInt(xhr.getResponseHeader('Content-Length')) || 0; }; xhr.send(); } }); // 监听资源加载完成事件,更新进度 document.addEventListener('load', (e) => { if (['LINK', 'SCRIPT'].includes(e.target.tagName)) { const resourceSize = parseInt(e.target.getAttribute('data-size')) || 0; loadedBytes += resourceSize; const progress = Math.min(Math.round((loadedBytes / totalBytes) * 100), 100); document.getElementById('loaderBar').style.width = `${progress}%`; // 加载完成后延迟隐藏进度条 if (progress >= 100) { setTimeout(() => { document.querySelector('.loader-container').style.display = 'none'; }, 500); } } }, true); </script>
二、Angular启动后的进度跟踪(结合APP_INITIALIZER)
当Angular开始初始化后,可以利用APP_INITIALIZER跟踪初始化任务(比如Firebase初始化、配置加载),并在组件中实时显示进度:
- 在
app.module.ts定义初始化工厂:
import { APP_INITIALIZER, NgModule } from '@angular/core'; import { ProgressService } from './progress.service'; export function appInitializerFactory(progressService: ProgressService) { return () => new Promise<void>((resolve) => { // 模拟实际初始化任务,比如Firebase初始化 progressService.updateProgress(20); setTimeout(() => { progressService.updateProgress(60); setTimeout(() => { progressService.updateProgress(100); resolve(); }, 800); }, 1200); }); } @NgModule({ providers: [ { provide: APP_INITIALIZER, useFactory: appInitializerFactory, deps: [ProgressService], multi: true } ] }) export class AppModule {}
- 创建进度状态服务
progress.service.ts:
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class ProgressService { private progressSubject = new BehaviorSubject<number>(0); progress$ = this.progressSubject.asObservable(); updateProgress(value: number) { this.progressSubject.next(Math.min(value, 100)); } }
- 在
app.component.ts中订阅并显示进度:
import { Component, OnInit } from '@angular/core'; import { ProgressService } from './progress.service'; @Component({ selector: 'app-root', template: ` <div *ngIf="progress < 100" class="app-loader"> <div class="loader-bar" [style.width.%]="progress"></div> <p>应用加载中... {{ progress }}%</p> </div> <router-outlet *ngIf="progress >= 100"></router-outlet> `, styles: [` .app-loader { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: white; display: flex; flex-direction: column; align-items: center; justify-content: center; } .loader-bar { width: 50%; height: 4px; background: #1976d2; margin-bottom: 1rem; border-radius: 2px; } `] }) export class AppComponent implements OnInit { progress = 0; constructor(private progressService: ProgressService) {} ngOnInit() { this.progressService.progress$.subscribe(value => { this.progress = value; }); } }
三、结合Service Worker的缓存/加载进度
因为你已经启用了Service Worker,可以监听SW的资源缓存事件,把进度同步给前端UI:
在自定义Service Worker文件(或修改ngsw-worker.js)中添加进度监听:
// Service Worker 片段 self.addEventListener('install', (event) => { const cacheName = 'my-app-cache-v1'; const resourcesToCache = [ '/vendor.js', '/main.js', // 其他需要缓存的资源 ]; event.waitUntil( caches.open(cacheName) .then((cache) => { const totalResources = resourcesToCache.length; let loadedCount = 0; return Promise.all(resourcesToCache.map(resource => { return cache.add(resource) .then(() => { loadedCount++; // 向所有打开的页面发送进度消息 self.clients.matchAll().then(clients => { clients.forEach(client => { client.postMessage({ type: 'CACHE_PROGRESS', progress: Math.round((loadedCount / totalResources) * 100) }); }); }); }); })); }) ); });
然后在Angular应用中监听SW消息:
// app.component.ts 中添加监听逻辑 import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-root', template: ` <div *ngIf="cacheProgress < 100" class="cache-loader"> 缓存更新中... {{ cacheProgress }}% </div> <!-- 原有模板内容 --> `, styles: [` .cache-loader { position: fixed; bottom: 1rem; right: 1rem; padding: 0.5rem 1rem; background: rgba(0,0,0,0.7); color: white; border-radius: 4px; } `] }) export class AppComponent implements OnInit { cacheProgress = 0; ngOnInit() { if ('serviceWorker' in navigator) { navigator.serviceWorker.addEventListener('message', (event) => { if (event.data.type === 'CACHE_PROGRESS') { this.cacheProgress = event.data.progress; } }); } } }
额外优化建议:减小vendor.js体积
虽然进度提示能提升用户体验,但从根源上缩小文件大小能更有效解决慢网问题:
- 启用代码分割:用
loadChildren懒加载模块,将Material、Firebase等依赖拆分为按需加载的chunk - 清理未使用的Material组件:仅导入项目实际用到的Material模块,避免全量导入
- 精简Firebase依赖:只导入需要的子模块(比如
@angular/fire/auth而非整个@angular/fire)
内容的提问来源于stack exchange,提问作者Eng1234
相关产品推荐
相关产品推荐

