Angular 15 SSR页面性能过低求助:懒加载无效,main.js过大
问题诊断与优化方案
一、核心问题定位
1. 懒加载未生效的核心原因
- 路由配置错误:若仍在
AppModule中直接导入非核心模块/组件,或路由未使用loadChildren/loadComponent语法,懒加载完全不生效 - 预加载策略冲突:如果设置了
PreloadAllModules,所有懒加载模块会被提前加载,导致main.js体积无缩减 - 依赖污染:核心模块(如
AppModule)间接导入了懒加载模块的组件/服务,打包时被强制合并进main.js - Tree Shaking失效:编译配置未开启相关优化,冗余代码未被剔除
2. Pagespeed评分低的关键诱因
- 首屏JS体积过大(9mb的
main.js):浏览器解析执行时间过长,严重阻塞渲染 - SSR渲染冗余:服务器端存在不必要的同步逻辑、未过滤的非首屏数据请求,导致LCP(最大内容绘制)延迟
- 资源未压缩:构建产物未启用gzip/brotli压缩,传输体积远超预期
- 生产模式优化未全开:未启用AOT编译、代码混淆压缩等核心优化
二、分步优化方案
1. 修复懒加载,缩减main.js体积
- 规范路由配置:所有非核心路由必须使用懒加载语法:
// app-routing.module.ts const routes: Routes = [ { path: '', component: HomeComponent }, // 首页可保留,其余页面按需懒加载 { path: 'about', loadComponent: () => import('./about/about.component').then(m => m.AboutComponent) }, { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule) } ]; - 调整预加载策略:若不需要提前加载,将预加载策略改为
NoPreloading:RouterModule.forRoot(routes, { preloadingStrategy: NoPreloading }) - 分析依赖冗余:生成打包统计文件并分析冗余代码:
重点排查第三方库(如UI组件库、图表库)是否被完整打包进ng build --configuration production --stats-json npx webpack-bundle-analyzer dist/[你的项目名]/stats.jsonmain.js,改用按需导入(例如Angular Material仅导入所需模块,而非全量MatModule) - 开启Tree Shaking:确保
tsconfig.json中compilerOptions.module设为ESNext,angular.json中build.optimization设为true
2. Pagespeed专项优化
- 启用资源压缩:在服务器端(Nginx/Apache)配置gzip或brotli压缩,针对JS、CSS、HTML文件开启压缩,可减少约70%传输体积
- 优化SSR渲染逻辑:
- 清理
server.ts中的冗余API请求与同步阻塞逻辑,仅加载首屏必要数据 - 避免在服务器端执行浏览器专属API(如
window/document),用PLATFORM_ID区分环境:import { PLATFORM_ID, Inject } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; constructor(@Inject(PLATFORM_ID) private platformId: Object) { if (isPlatformBrowser(this.platformId)) { // 仅在浏览器端执行的逻辑 } }
- 清理
- 精简首屏内容:移除首屏非必要的第三方脚本(统计、广告等),改用异步/延迟加载;优化DOM结构,减少嵌套层级与冗余元素
- 强制执行生产构建:确保构建命令使用生产配置,自动开启AOT编译、代码混淆压缩:
ng build --configuration production && ng run [你的项目名]:server:production
三、配置文件检查要点
1. tsconfig.json
确保核心编译配置正确:
{ "compilerOptions": { "module": "ESNext", "target": "ES2020", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true }, "angularCompilerOptions": { "strictInjectionParameters": true, "strictInputAccessModifiers": true, "strictTemplates": true, "enableAot": true } }
2. angular.json
验证生产构建优化项:
{ "projects": { "[你的项目名]": { "architect": { "build": { "configurations": { "production": { "optimization": true, "outputHashing": "all", "sourceMap": false, "namedChunks": false, "vendorChunk": false, "buildOptimizer": true, "budgets": [ { "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" } ] } } }, "server": { "configurations": { "production": { "outputHashing": "media", "sourceMap": false, "optimization": true } } } } } } }
3. server.ts
确保无冗余导入与逻辑:
import 'zone.js/node'; import { ngExpressEngine } from '@nguniversal/express-engine'; import * as express from 'express'; import { join } from 'path'; import { AppServerModule } from './src/main.server'; import { APP_BASE_HREF } from '@angular/common'; import { existsSync } from 'fs'; const app = express(); const distFolder = join(process.cwd(), 'dist/[你的项目名]/browser'); const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index'; app.engine('html', ngExpressEngine({ bootstrap: AppServerModule, })); app.set('view engine', 'html'); app.set('views', distFolder); app.get('*.*', express.static(distFolder, { maxAge: '1y' })); app.get('*', (req, res) => { res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] }); }); const port = process.env['PORT'] || 4000; app.listen(port, () => { console.log(`Node Express server listening on http://localhost:${port}`); });
内容的提问来源于stack exchange,提问作者Amir Dora.
相关产品推荐
相关产品推荐

