Heim > Web-Frontend > js-Tutorial > Hauptteil

Was soll ich tun, wenn der Angular13+-Entwicklungsmodus zu langsam ist? Ursachen und Lösungen

青灯夜游
Freigeben: 2023-01-13 20:23:49
nach vorne
2506 Leute haben es durchsucht

Was soll ich tun, wenn der Angular13+-Entwicklungsmodus zu langsam ist? Im folgenden Artikel erfahren Sie, warum der Entwicklungsmodus von Angular 13+ zu langsam ist und wie Sie die Build-Leistung optimieren können.

Was soll ich tun, wenn der Angular13+-Entwicklungsmodus zu langsam ist? Ursachen und Lösungen

1 Gründe und Lösungen für den langsamen Entwicklungsmodus von Angular 13+

Nachdem kürzlich ein Angular-Projekt mit Hochfrequenziteration für sieben Jahre auf Angular 13 aktualisiert wurde, war sein Entwicklungsmodus langsam aufzubauen und beschäftigt hohe Ressourcen, die Entwicklungserfahrung ist ziemlich dürftig. Wenn ich einen Build auf einem Macbook Air starte, das ich nur gelegentlich für Besprechungen verwende (und das in letzter Zeit zu meinem primären Produktivitätstool während der Arbeit von zu Hause aus geworden ist), surren die Lüfter und die CPU ist am Limit Nach Abschluss des Builds dauert ein Hot-Update mehr als eine Minute. [Verwandte Tutorial-Empfehlung: „Angular-Tutorial"]Macbook air(近期居家办公期间转换为了主要生产力工具) 中启动构建时,它的风扇会呼呼作响,CPU 负荷被打满,而在构建完成后,热更新一次的时间在一分钟以上。【相关教程推荐:《angular教程》】

在经过各种原因分析与排查后,最终在 angular.json 的 schema(./node_modules/@angular/cli/lib/config/schema.json) 中发现了问题,再结合 Angular 12 release 文档定位到了具体原因: Angular 12 一个主要的改动是将 aotbuildOptimizeroptimization 等参数由默认值 false 改为了 true

A number of browser and server builder options have had their default values changed. The aim of these changes is to reduce the configuration complexity and support the new "production builds by default" initiative.

可以看到 Angular 12 后的默认生产模式,对于跨版本升级来说是比较坑爹的。我们可以从这个提交中了解变动细节:656f8d7

1.1 解决 Angular 12+ 开发模式慢的问题

解决办法则是在 development 配置中禁用生产模式相关的配置项。示例:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "projects": {
    "front": {
      "architect": {
        "build": {
          "configurations": {
            "development": {
              "tsConfig": "./tsconfig.dev.json",
              "aot": false,
              "buildOptimizer": false,
              "optimization": false,
              "extractLicenses": false,
              "sourceMap": true,
              "vendorChunk": true,
              "namedChunks": true
            }
          }
        },
    }
  },
  "defaultProject": "front"
}
Nach dem Login kopieren

需注意 aot 开启与关闭时,在构建结果表现上可能会有一些差异,需视具体问题而分析。

1.2 问题:开启 aotpug 编译报错

该项目中使用 pug 开发 html 内容。关闭 aot 时构建正常,开启后则会报错。

根据报错内容及位置进行 debugger 调试,可以看到其编译结果为一个 esModule 的对象。这是由于使用了 raw-loader,其编译结果默认为 esModule 模式,禁用 esModule 配置项即可。示例(自定义 webpack 配置可参考下文的 dll 配置相关示例):

{
  test: /\.pug$/,
  use: [
    {
      loader: 'raw-loader',
      options: {
        esModule: false,
      },
    },
    {
      loader: 'pug-html-loader',
      options: {
        doctype: 'html',
      },
    },
  ],
},
Nach dem Login kopieren

2 进一步优化:Angular 自定义 webpack 配置 dll 支持

该项目项目构建上有自定义 webpack 配置的需求,使用了 @angular-builders/custom-webpack 库实现,但是没有配置 dll。

Angular 提供了 vendorChunk 参数,开启它会提取在 package.json 中的依赖等公共资源至独立 chunk 中,其可以很好的解决热更新 bundles 过大导致热更新太慢等的问题,但仍然存在较高的内存占用,而且实际的对比测试中,在存在 webpack5 缓存的情况下,其相比 dll 模式的构建编译速度以及热更新速度都稍微慢一些。故对于开发机器性能一般的情况下,给开发模式配置 dll 是会带来一定的收益的。

2.1 Angular 支持自定义 webpack 配置

首先需要配置自定义 webpack 配置的构建支持。执行如下命令添加依赖:

npm i -D @angular-builders/custom-webpack
Nach dem Login kopieren

修改 angluar.json 配置。内容格式参考:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "cli": {
    "analytics": false,
    "cache": {
      "path": "node_modules/.cache/ng"
    }
  },
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "front": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {
        "@schematics/angular:component": {
          "style": "less"
        }
      },
      "architect": {
        "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {
            "customWebpackConfig": {
              "path": "./webpack.config.js"
            },
            "indexTransform": "scripts/index-html-transform.js",
            "outputHashing": "media",
            "deleteOutputPath": true,
            "watch": true,
            "sourceMap": false,
            "outputPath": "dist/dev",
            "index": "src/index.html",
            "main": "src/app-main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "./tsconfig.app.json",
            "baseHref": "./",
            "assets": [
              "src/assets/",
              {
                "glob": "**/*",
                "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
                "output": "/assets/"
              }
            ],
            "styles": [
              "node_modules/angular-tree-component/dist/angular-tree-component.css",
              "src/css/index.less"
            ],
            "scripts": []
          },
          "configurations": {
            "development": {
              "tsConfig": "./tsconfig.dev.json",
              "buildOptimizer": false,
              "optimization": false,
              "aot": false,
              "extractLicenses": false,
              "sourceMap": true,
              "vendorChunk": true,
              "namedChunks": true,
              "scripts": [
                {
                  "inject": true,
                  "input": "./dist/dll/dll.js",
                  "bundleName": "dll_library"
                }
              ]
            },
            "production": {
              "outputPath": "dist/prod",
              "baseHref": "./",
              "watch": false,
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": {
                "scripts": true,
                "styles": {
                  "minify": true,
                  "inlineCritical": false
                },
                "fonts": true
              },
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": false,
              "vendorChunk": false,
              "buildOptimizer": true
            }
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-builders/custom-webpack:dev-server",
          "options": {
            "browserTarget": "front:build",
            "liveReload": false,
            "open": false,
            "host": "0.0.0.0",
            "port": 3002,
            "servePath": "/",
            "publicHost": "localhost.gf.com.cn",
            "proxyConfig": "config/ngcli-proxy-config.js",
            "disableHostCheck": true
          },
          "configurations": {
            "production": {
              "browserTarget": "front:build:production"
            },
            "development": {
              "browserTarget": "front:build:development"
            }
          },
          "defaultConfiguration": "development"
        },
        "test": {
          "builder": "@angular-builders/custom-webpack:karma",
          "options": {
            "customWebpackConfig": {
              "path": "./webpack.test.config.js"
            },
            "indexTransform": "scripts/index-html-transform.js",
            "main": "src/ngtest.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "./tsconfig.spec.json",
            "karmaConfig": "./karma.conf.js",
            "assets": [
              "src/assets/",
              {
                "glob": "**/*",
                "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
                "output": "/assets/"
              }
            ],
            "styles": [
              "node_modules/angular-tree-component/dist/angular-tree-component.css",
              "src/css/index.less"
            ],
            "scripts": []
          }
        }
      }
    }
  },
  "defaultProject": "front",
  "schematics": {
    "@schematics/angular:module": {
      "routing": true,
      "spec": false
    },
    "@schematics/angular:component": {
      "flat": false,
      "inlineStyle": true,
      "inlineTemplate": false
    }
  }
}
Nach dem Login kopieren

该示例中涉及多处自定义配置内容,主要需注意 webpack 相关的部分, 其他内容可视自身项目具体情况对比参考。一些细节也可参考以前的这篇文章中的实践介绍:lzw.me/a/update-to…

2.2 为 Angular 配置 webpack dll 支持

新建 webpack.config.js 文件。内容参考:

const { existsSync } = require('node:fs');
const { resolve } = require('node:path');
const webpack = require('webpack');

// require('events').EventEmitter.defaultMaxListeners = 0;

/**
 * @param {import('webpack').Configuration} config
 * @param {import('@angular-builders/custom-webpack').CustomWebpackBrowserSchema} options
 * @param {import('@angular-builders/custom-webpack').TargetOptions} targetOptions
 */
module.exports = (config, options, targetOptions) => {
  if (!config.devServer) config.devServer = {};

  config.plugins.push(
    new webpack.DefinePlugin({ LZWME_DEV: config.mode === 'development' }),
  );

  const dllDir = resolve(__dirname, './dist/dll');
  if (
    existsSync(dllDir) &&
    config.mode === 'development' &&
    options.scripts?.some((d) => d.bundleName === 'dll_library')
  ) {
    console.log('use dll:', dllDir);
    config.plugins.unshift(
      new webpack.DllReferencePlugin({
        manifest: require(resolve(dllDir, 'dll-manifest.json')),
        context: __dirname,
      })
    );
  }

  config.module.rules = config.module.rules.filter((d) => {
    if (d.test instanceof RegExp) {
      // 使用 less,移除 sass/stylus loader
      return !(d.test.test('x.sass') || d.test.test('x.scss') || d.test.test('x.styl'));
    }
    return true;
  });

  config.module.rules.unshift(
    {
      test: /\.pug$/,
      use: [
        {
          loader: 'raw-loader',
          options: {
            esModule: false,
          },
        },
        {
          loader: 'pug-html-loader',
          options: {
            doctype: 'html',
          },
        },
      ],
    },
    {
      test: /\.html$/,
      loader: 'raw-loader',
      exclude: [helpers.root('src/index.html')],
    },
    {
      test: /\.svg$/,
      loader: 'raw-loader',
    },
    {
      test: /\.(t|les)s/,
      loader: require.resolve('@lzwme/strip-loader'),
      exclude: /node_modules/,
      options: {
        disabled: config.mode !== 'production',
      },
    }
  );

  // AngularWebpackPlugin,用于自定义 index.html 处理插件
  const awPlugin = config.plugins.find((p) => p.options?.hasOwnProperty('directTemplateLoading'));
  if (awPlugin) awPlugin.pluginOptions.directTemplateLoading = false;

  // 兼容上古遗传逻辑,禁用部分插件
  config.plugins = config.plugins.filter((plugin) => {
    const pluginName = plugin.constructor.name;
    if (/CircularDependency|CommonJsUsageWarnPlugin/.test(pluginName)) {
      console.log('[webpack][plugin] disabled: ', pluginName);
      return false;
    }

    return true;
  });
  // console.log('[webpack][config]', config.mode, config, options, targetOptions);
  return config;
};
Nach dem Login kopieren

新建 webpack.dll.mjs

Nach verschiedenen Gründen Nach der Analyse und Fehlerbehebung wurde das Problem schließlich im Schema (./node_modules/@angular/cli/lib/config/schema.json) von angular.json gefunden, und dann Kombiniert mit Angular 12-Releasedokument findet die spezifischen Gründe: Eine wesentliche Änderung in Angular 12 besteht darin, aot und zu ersetzen buildOptimizer< /code>, <code>optimization und andere Parameter wurden vom Standardwert false in true geändert. 🎜
🎜Bei einer Reihe von Browser- und Server-Builder-Optionen wurden die Standardwerte geändert. Ziel dieser Änderungen ist es, die Konfigurationskomplexität zu reduzieren und die neue Initiative „Produktions-Builds standardmäßig“ zu unterstützen.🎜
🎜Ja, da der Standardproduktionsmodus nach Angular 12 für versionübergreifende Upgrades ziemlich schwierig ist. Die Änderungsdetails können wir diesem Commit entnehmen: 656f8d7🎜

🎜1.1 Lösen Sie das Problem des langsamen Angular 12+-Entwicklungsmodus 🎜

🎜Die Lösung besteht darin, produktionsmodusbezogene Konfigurationselemente in der Entwicklung-Konfiguration zu deaktivieren. Beispiel: 🎜
import { join } from &#39;node:path&#39;;
import webpack from &#39;webpack&#39;;

const rootDir = process.cwd();
const isDev = process.argv.slice(2).includes(&#39;--dev&#39;) || process.env.NODE_ENV === &#39;development&#39;;

/** @type {import(&#39;webpack&#39;).Configuration} */
const config = {
  context: rootDir,
  mode: isDev ? &#39;development&#39; : &#39;production&#39;,
  entry: {
    dll: [
      &#39;@angular/common&#39;,
      &#39;@angular/core&#39;,
      &#39;@angular/forms&#39;,
      &#39;@angular/platform-browser&#39;,
      &#39;@angular/platform-browser-dynamic&#39;,
      &#39;@angular/router&#39;,
      &#39;@lzwme/asmd-calc&#39;,
      // more...
    ],
  },
  output: {
    path: join(rootDir, &#39;dist/dll&#39;),
    filename: &#39;dll.js&#39;,
    library: &#39;[name]_library&#39;,
  },
  plugins: [
    new webpack.DllPlugin({
      path: join(rootDir, &#39;dist/dll/[name]-manifest.json&#39;),
      name: &#39;[name]_library&#39;,
    }),
    new webpack.IgnorePlugin({
      resourceRegExp: /^\.\/locale$/,
      contextRegExp: /moment$/,
    }),
  ],
  cache: { type: &#39;filesystem&#39; },
};

webpack(config).run((err, result) => {
  console.log(err ? `Failed!` : `Success!`, err || `${result.endTime - result.startTime}ms`);
});
Nach dem Login kopieren
Nach dem Login kopieren
🎜 Bitte beachten Sie, dass es beim Ein- und Ausschalten von aot einige Unterschiede in der Leistung der Build-Ergebnisse geben kann, die basierend auf dem spezifischen Problem analysiert werden müssen. 🎜

🎜1.2 Problem: Nach dem Einschalten von aot kompiliert pug und meldet einen Fehler🎜

🎜 wird in diesem Projekt verwendet pug entwickelt HTML-Inhalte. Der Build ist normal, wenn aot ausgeschaltet ist, nach dem Einschalten wird jedoch ein Fehler gemeldet. 🎜🎜 Führen Sie das Debugger-Debugging basierend auf dem Fehlerinhalt und -ort durch. Sie können sehen, dass das Kompilierungsergebnis ein esModule-Objekt ist. Dies liegt an der Verwendung von raw-loader, dessen Kompilierungsergebnis standardmäßig auf den esModule-Modus eingestellt ist. Deaktivieren Sie einfach das Konfigurationselement esModule. Beispiel (benutzerdefinierte Webpack-Konfiguration kann sich auf die folgenden Beispiele für die DLL-Konfiguration beziehen): 🎜
{
    "scripts": {
        "ng:serve": "node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng serve",
        "dll": "node config/webpack.dll.mjs",
        "dev": "npm run dll -- --dev && npm run ng:serve -- -c development",
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

🎜2 Weitere Optimierung: Angular-Unterstützung für benutzerdefinierte Webpack-Konfigurations-DLLs 🎜🎜🎜Dieses Projekt basiert auf There Es ist erforderlich, die webpack-Konfiguration anzupassen, die mithilfe der @angular-builders/custom-webpack-Bibliothek implementiert wird, aber es ist keine DLL konfiguriert. 🎜🎜Angular stellt den Parameter vendorChunk bereit. Wenn Sie ihn aktivieren, werden öffentliche Ressourcen wie Abhängigkeiten in package.json in unabhängige Blöcke extrahiert, die es ermöglichen ist eine gute Lösung für das Problem, dass Hot-Update-Bundles zu groß sind, was dazu führt, dass Hot-Updates zu langsam sind usw., aber es gibt immer noch eine hohe Speichernutzung, und im tatsächlichen Vergleichstest ist dies bei Vorhandensein von Webpack5-Cache der Fall ist besser als der DLL-Modus zum Erstellen und Kompilieren. Die Geschwindigkeit sowie die Hot-Update-Geschwindigkeit sind etwas langsamer. Wenn die Leistung der Entwicklungsmaschine also durchschnittlich ist, bringt die Konfiguration der DLL im Entwicklungsmodus bestimmte Vorteile. 🎜

🎜2.1 Angular unterstützt die benutzerdefinierte Webpack-Konfiguration🎜

🎜Zuerst müssen Sie die Build-Unterstützung für die benutzerdefinierte Webpack-Konfiguration konfigurieren. Führen Sie den folgenden Befehl aus, um Abhängigkeiten hinzuzufügen: 🎜rrreee🎜Ändern Sie die angluar.json-Konfiguration. Referenz zum Inhaltsformat: 🎜rrree🎜Dieses Beispiel umfasst viele benutzerdefinierte Konfigurationsinhalte. Sie sollten hauptsächlich auf die Webpack-bezogenen Teile achten. Andere Inhalte können basierend auf der spezifischen Situation Ihres eigenen Projekts verglichen und referenziert werden. Weitere Informationen finden Sie in der praktischen Einführung in diesem vorherigen Artikel: lzw.me/a/update-to…🎜

🎜2.2 Konfigurieren Sie die Webpack-DLL-Unterstützung für Angular🎜

🎜Erstellen Sie eine neue webpack.config.js-Datei. Inhaltsreferenz: 🎜rrreee🎜Erstellen Sie eine neue webpack.dll.mjs-Datei für die DLL-Erstellung. Inhaltsbeispiel: 🎜
import { join } from &#39;node:path&#39;;
import webpack from &#39;webpack&#39;;

const rootDir = process.cwd();
const isDev = process.argv.slice(2).includes(&#39;--dev&#39;) || process.env.NODE_ENV === &#39;development&#39;;

/** @type {import(&#39;webpack&#39;).Configuration} */
const config = {
  context: rootDir,
  mode: isDev ? &#39;development&#39; : &#39;production&#39;,
  entry: {
    dll: [
      &#39;@angular/common&#39;,
      &#39;@angular/core&#39;,
      &#39;@angular/forms&#39;,
      &#39;@angular/platform-browser&#39;,
      &#39;@angular/platform-browser-dynamic&#39;,
      &#39;@angular/router&#39;,
      &#39;@lzwme/asmd-calc&#39;,
      // more...
    ],
  },
  output: {
    path: join(rootDir, &#39;dist/dll&#39;),
    filename: &#39;dll.js&#39;,
    library: &#39;[name]_library&#39;,
  },
  plugins: [
    new webpack.DllPlugin({
      path: join(rootDir, &#39;dist/dll/[name]-manifest.json&#39;),
      name: &#39;[name]_library&#39;,
    }),
    new webpack.IgnorePlugin({
      resourceRegExp: /^\.\/locale$/,
      contextRegExp: /moment$/,
    }),
  ],
  cache: { type: &#39;filesystem&#39; },
};

webpack(config).run((err, result) => {
  console.log(err ? `Failed!` : `Success!`, err || `${result.endTime - result.startTime}ms`);
});
Nach dem Login kopieren
Nach dem Login kopieren

angular.json 中添加 dll.js 文件的注入配置,可参考前文示例中 development.scripts 中的配置内容格式。

package.json 中增加启动脚本配置。示例:

{
    "scripts": {
        "ng:serve": "node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng serve",
        "dll": "node config/webpack.dll.mjs",
        "dev": "npm run dll -- --dev && npm run ng:serve -- -c development",
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

最后,可执行 npm run dev 测试效果是否符合预期。

3 小结

angular-cli 在升级至 webpack 5 以后,基于 webpack 5 的缓存能力做了许多编译优化,一般情况下开发模式二次构建速度相比之前会有大幅的提升。但是相比 snowpackvite 一类的 esm no bundles 方案仍有较大的差距。其从 Angular 13 开始已经在尝试引入 esbuild,但由于其高度定制化的构建逻辑适配等问题,对一些配置参数的兼容支持相对较为复杂。在 Angular 15 中已经可以进行生产级配置尝试了,有兴趣也可作升级配置与尝试。

更多编程相关知识,请访问:编程教学!!

Das obige ist der detaillierte Inhalt vonWas soll ich tun, wenn der Angular13+-Entwicklungsmodus zu langsam ist? Ursachen und Lösungen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:juejin.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!