我越来越慢pagespeed排名低于20的一些网页,因此我lazyloaded模块,在pagespeed测试几乎没有区别,我决定lazyload所有的模块。因此,我大大减少了app.module.ts的导入和组件。顺便说一句,一个简单的页面,只包含文本不得分超过50。
但是当我为服务器端渲染构建应用程序时,main.js文件仍然很大。现在是9mb
。延迟加载似乎并没有减少这一点。在我的方法中是否有一些问题,为什么页面速度性能没有得到改善!此外,我运行sourcemap组合大小显示3mb
。
SSR构建命令
"build:ssr": "ng build --configuration=production --output-hashing=all && ng run myApp:server",
tsv.json文件
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"module": "esnext",
"moduleResolution": "node",
"noUnusedLocals": true,
"experimentalDecorators": true,
"target": "ES2022",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
],
"useDefineForClassFields": false
},
}
`
Angular.json
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"myApp": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/myApp/browser",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/ads.txt",
"src/favicon.ico",
"src/assets",
"src/web.config",
"src/manifest.json",
"src/google43d412fda361808c.html",
"src/sitemap.xml"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/purple-green.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/angular-calendar/css/angular-calendar.css",
"node_modules/cropperjs/dist/cropper.min.css",
"node_modules/quill/dist/quill.core.css",
"node_modules/quill/dist/quill.bubble.css",
"node_modules/quill/dist/quill.snow.css",
"node_modules/@ctrl/ngx-emoji-mart/picker.css",
"src/styles/styles.css",
"src/styles/main.scss"
],
"scripts": [
"node_modules/jquery/dist/jquery.js",
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
"node_modules/quill/dist/quill.min.js"
],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": false,
"optimization": false,
"namedChunks": true
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "10kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"serviceWorker": false,
"i18nMissingTranslation": "error"
},
"development": {}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
},
"configurations": {
"production": {
"browserTarget": "myApp:build:production"
},
"development": {
"browserTarget": "myApp:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "myApp:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"./node_modules/@angular/material/prebuilt-themes/purple-green.css",
"src/styles.css"
],
"scripts": [],
"assets": [
"src/favicon.ico",
"src/assets",
"src/web.config",
"src/manifest.json",
"src/sitemap.xml"
]
}
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/myApp/server",
"main": "server.ts",
"tsConfig": "src/tsconfig.server.json",
"optimization": false,
"sourceMap": true,
"extractLicenses": false,
"vendorChunk": true
},
"configurations": {
"production": {
"outputHashing": "media",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"sourceMap": false,
"extractLicenses": true,
"vendorChunk": false
},
"development": {}
},
"defaultConfiguration": "production"
},
"serve-ssr": {
"builder": "@nguniversal/builders:ssr-dev-server",
"configurations": {
"development": {
"browserTarget": "myApp:build:development",
"serverTarget": "myApp:server:development"
},
"production": {
"browserTarget": "myApp:build:production",
"serverTarget": "myApp:server:production"
}
},
"defaultConfiguration": "development"
},
"prerender": {
"builder": "@nguniversal/builders:prerender",
"options": {
"routes": [
"/"
]
},
"configurations": {
"production": {
"browserTarget": "myApp:build:production",
"serverTarget": "myApp:server:production"
},
"development": {
"browserTarget": "myApp:build:development",
"serverTarget": "myApp:server:development"
}
},
"defaultConfiguration": "production"
}
}
},
"myApp-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js"
},
"configurations": {
"production": {
"devServerTarget": "myApp:serve:production"
},
"development": {
"devServerTarget": "myApp:serve:development"
}
},
"defaultConfiguration": "development"
}
}
}
},
"cli": {
"analytics": false
}
}
服务器.ts文件
import 'zone.js/node';
import { APP_BASE_HREF } from '@angular/common';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { existsSync } from 'fs';
import * as fs from 'fs';
import * as bodyParser from 'body-parser';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { parseString } from 'xml2js';
import 'localstorage-polyfill';
import * as path from 'path';
global['localStorage'] = localStorage;
const compression = require('compression');
const xmlFilePath = 'sitemap.xml';
let xmlData = null;
// let sitemapData = null;
// The Express app is exported so that it can be used by serverless Functions.
export function app(data): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/my-app/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
let sitemapData = data;
// compress dist folder
server.use(express.static(path.join(__dirname, 'dist')));
server.use(compression()) //compressing dist folder
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.use(bodyParser.urlencoded({ extended: true })); // Handle URL-encoded data
server.use(bodyParser.json());
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// Function to make a GET request
server.get('/api/appleauth', (req, res) => {
// Handle the GET request here
console.log("get request done");
res.send('GET request received');
});
// POST request handler
server.post('/api/appleauth', (req, res) => {
// Handle the incoming POST request
console.log("start post");
res.redirect('/login?applecode=' + req.body.id_token);
});
server.post('/api/appleauth/connect', (req, res) => {
// Handle the incoming POST request
res.redirect('/einstellungen?appleconnectcode=' + req.body.id_token);
});
// All regular routes use the Universal engine
server.get('*', (req, res) => {
if (req.originalUrl.includes("/pflanze")) {
var urlSplit = req.originalUrl.split("/");
var splitCount = urlSplit.length;
if (splitCount > 3) {
var plantId = urlSplit[splitCount - 2];
var plantName = urlSplit[splitCount - 1];
var currentUrlId = "/pflanze/" + plantId;
if (sitemapData != null) {
const urls = sitemapData?.urlset?.url || [];
console.error('Error parsing XML:', urls);
const searchString = currentUrlId;
const foundItem = urls.find(item => item.loc[0].includes(searchString));
if (foundItem != null) {
var correctUrl = foundItem.loc[0];
var correctSplit = correctUrl.split("/");
var correctCount = correctSplit.length;
var finalName = correctSplit[correctCount - 1];
if (finalName.toLowerCase() == plantName.toLowerCase()) {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
}
else {
res.redirect(301, correctUrl);
}
}
else {
// res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
res.redirect(301, 'https://my-app.de/video4/' + searchString);
}
}
else {
res.redirect(301, 'https://my-app.de/video2');
}
}
} else {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
}
});
return server;
}
function run(data): void {
const port = process.env['PORT'] || 4200;
// Start up the Node server
const server = app(data);
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
fs.readFile(xmlFilePath, 'utf8', (err, data) => {
if (err) {
console.error('Error reading XML file:', err);
} else {
xmlData = data;
parseString(xmlData, (err, result) => {
if (err) {
// res.redirect(301, 'https://my-app.de/video3');
console.error('Error parsing XML:', err);
} else {
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run(result);
}
}
});
// }
}
});
export * from './src/main.server';
2条答案
按热度按时间j2qf4p5b1#
在Angular应用程序中减小main.js文件的大小,特别是对于服务器端渲染(SSR),涉及到几种优化包大小和提高性能的策略。以下是一些解决大型main.js文件大小的潜在解决方案:
1.**Tree Shaking:**确保在生产构建中启用Tree Shaking,以消除未使用的代码并减少捆绑包大小。
1.**代码拆分:**继续利用延迟加载和代码拆分按需加载模块,减少初始加载时间,提高性能。
1.**捆绑包分析:**利用Webpack Bundle Analyzer等工具分析捆绑包,并确定影响其大小的因素。这可以帮助确定优化区域。
1.**Angular AOT编译:**在构建过程中使用Angular Ahead-of-Time(AOT)编译,以减少bundle大小并提高运行时性能。
1.**优化图像:**压缩和优化应用程序中的图像以减小其大小,这可能会对整体捆绑大小产生重大影响。
1.**缩小和美化代码:**在构建过程中启用代码缩小和模糊处理,以减小JavaScript文件的大小。
1.**优化第三方库:**确保优化第三方库和依赖项,并仅包含必要的组件,以最大限度地减少其对捆绑包大小的影响。
1.**Angular构建配置:**检查您的Angular构建配置,并确保设置了适当的选项,如
buildOptimizer
,sourceMap
和extractLicenses
,以实现所需的优化。1.**捆绑供应商文件:**配置您的构建以将供应商文件与应用程序代码分开捆绑。这可以帮助保持main.js文件较小。
1.**CDN集成:**利用内容分发网络(CDN)提供脚本和样式等静态资产,减轻服务器负载,提高页面加载速度。
1.**服务器端渲染(SSR)优化:**研究特定于SSR的优化和最佳实践,以确保高效的服务器端渲染,而不会过度增加捆绑包大小。
1.**文件缓存:**实施适当的缓存策略,确保用户在发生更改时只下载必要的文件,减少冗余下载。
1.**分析和重构代码:**分析和分析您的应用程序代码,以确定重构和优化的区域,确保您的代码尽可能高效。
通过实现这些策略的组合并分析bundle的大小和结构,您应该能够显着减少main.js文件的大小并提高Angular应用程序的性能。
gcuhipw92#
有几个因素可能会导致main.js文件很大,PageSpeed分数很低:
**第三方库:**jQuery、Bootstrap和Quill等库添加到JavaScript负载中。考虑只在绝对必要的页面上使用它们。
**Angular.json脚本:**Angular.json文件中有几个样式和脚本。确保没有加载不必要的样式或不使用的脚本。
**源Map:**在tslog.json和angular.json中,启用了源Map。这些对于调试来说很好,但对于生产来说就不好了。禁用这些将减少您的捆绑包大小。
**优化:**在你的angular.json中,你在某些地方将optimization设置为false。Angular的构建优化器删除了不必要的代码和注解,这将减少bundle的大小。
**SSR和延迟加载:**延迟加载一般不会影响您的SSR main.js文件;它只影响客户端导航。但是,请确保您的服务器端渲染的路线是真正优化的。
**代码拆分:**考虑手动代码拆分策略,例如为不同的功能或路由创建单独的块。
**资产压缩:**使用Gzip或Brotli等服务器级压缩库。
**Analyze Bundle:**使用webpack-bundle-analyzer等工具查看JavaScript bundle中占用空间的内容。
**性能预算:**您已经在angular.json中设置了预算,但您可能需要降低这些数字以实施更好的实践。
**Web最佳实践:**使用图像和文本压缩,异步加载脚本,并利用浏览器缓存来提高PageSpeed。
**Server.ts文件:**您似乎有很多逻辑,可能会减慢初始服务器响应时间。尝试简化或优化它。
最后,使用Angular内置的ng build --prod --stats-json调试应用程序,然后将生成的stats.json加载到bundle分析器中可能会很有用。
请记住,实现高性能分数是一个持续的过程,涉及许多优化层。