iis 非常慢的页面速度排名- Angular 15服务器端渲染

ltqd579y  于 2023-10-19  发布在  Angular
关注(0)|答案(2)|浏览(116)

我越来越慢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';
j2qf4p5b

j2qf4p5b1#

在Angular应用程序中减小main.js文件的大小,特别是对于服务器端渲染(SSR),涉及到几种优化包大小和提高性能的策略。以下是一些解决大型main.js文件大小的潜在解决方案:
1.**Tree Shaking:**确保在生产构建中启用Tree Shaking,以消除未使用的代码并减少捆绑包大小。

{
  "optimization": {
    "removeAvailableModules": true,
    "usedExports": true,
    "moduleIds": "named"
  }
}

1.**代码拆分:**继续利用延迟加载和代码拆分按需加载模块,减少初始加载时间,提高性能。
1.**捆绑包分析:**利用Webpack Bundle Analyzer等工具分析捆绑包,并确定影响其大小的因素。这可以帮助确定优化区域。
1.**Angular AOT编译:**在构建过程中使用Angular Ahead-of-Time(AOT)编译,以减少bundle大小并提高运行时性能。
1.**优化图像:**压缩和优化应用程序中的图像以减小其大小,这可能会对整体捆绑大小产生重大影响。
1.**缩小和美化代码:**在构建过程中启用代码缩小和模糊处理,以减小JavaScript文件的大小。
1.**优化第三方库:**确保优化第三方库和依赖项,并仅包含必要的组件,以最大限度地减少其对捆绑包大小的影响。
1.**Angular构建配置:**检查您的Angular构建配置,并确保设置了适当的选项,如buildOptimizersourceMapextractLicenses,以实现所需的优化。
1.**捆绑供应商文件:**配置您的构建以将供应商文件与应用程序代码分开捆绑。这可以帮助保持main.js文件较小。
1.**CDN集成:**利用内容分发网络(CDN)提供脚本和样式等静态资产,减轻服务器负载,提高页面加载速度。
1.**服务器端渲染(SSR)优化:**研究特定于SSR的优化和最佳实践,以确保高效的服务器端渲染,而不会过度增加捆绑包大小。
1.**文件缓存:**实施适当的缓存策略,确保用户在发生更改时只下载必要的文件,减少冗余下载。
1.**分析和重构代码:**分析和分析您的应用程序代码,以确定重构和优化的区域,确保您的代码尽可能高效。
通过实现这些策略的组合并分析bundle的大小和结构,您应该能够显着减少main.js文件的大小并提高Angular应用程序的性能。

gcuhipw9

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分析器中可能会很有用。
请记住,实现高性能分数是一个持续的过程,涉及许多优化层。

相关问题