可以看到 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"
}
需注意 aot
开启与关闭时,在构建结果表现上可能会有一些差异,需视具体问题而分析。
1.2 问题:开启 aot
后 pug
编译报错
该项目中使用 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',
},
},
],
},
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
修改 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": []
}
}
}
}
},
"defaultProject": "front",
"schematics": {
"@schematics/angular:module": {
"routing": true,
"spec": false
},
"@schematics/angular:component": {
"flat": false,
"inlineStyle": true,
"inlineTemplate": false
}
}
}
该示例中涉及多处自定义配置内容,主要需注意 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;
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;
};
新建 webpack.dll.mjs
文件,用于 dll 构建。内容示例:
import { join } from 'node:path';
import webpack from 'webpack';
const rootDir = process.cwd();
const isDev = process.argv.slice(2).includes('--dev') || process.env.NODE_ENV === 'development';
const config = {
context: rootDir,
mode: isDev ? 'development' : 'production',
entry: {
dll: [
'@angular/common',
'@angular/core',
'@angular/forms',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/router',
'@lzwme/asmd-calc',
// more...
],
},
output: {
path: join(rootDir, 'dist/dll'),
filename: 'dll.js',
library: '[name]_library',
},
plugins: [
new webpack.DllPlugin({
path: join(rootDir, 'dist/dll/[name]-manifest.json'),
name: '[name]_library',
}),
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
}),
],
cache: { type: 'filesystem' },
};
webpack(config).run((err, result) => {
console.log(err ? `Failed!` : `Success!`, err || `${result.endTime - result.startTime}ms`);
});
在 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",
}
}
最后,可执行 npm run dev
测试效果是否符合预期。
3 小结
angular-cli
在升级至 webpack 5 以后,基于 webpack 5 的缓存能力做了许多编译优化,一般情况下开发模式二次构建速度相比之前会有大幅的提升。但是相比 snowpack
和 vite
一类的 esm no bundles 方案仍有较大的差距。其从 Angular 13
开始已经在尝试引入 esbuild
,但由于其高度定制化的构建逻辑适配等问题,对一些配置参数的兼容支持相对较为复杂。在 Angular 15
中已经可以进行生产级配置尝试了,有兴趣也可作升级配置与尝试。
以上就是Angular13+ 开发模式太慢怎么办?原因与解决方法介绍的详细内容,更多请关注编程网其它相关文章!