Skip to content

Commit 31e538a

Browse files
committed
feat(build): auto generate vendor chunk
Add `--vendor-chunk` option to `ng build/serve`. Enabling vendor chunk can help with rebuild speeds. My tests on a medium project show a 34% improvement on rebuild. This option is enabled by default on `ng serve` and `ng build`. To disable it, use `--no-vendor-chunk` flag. Partially address #1980 BREAKING CHANGE: `ng build/serve` now generated `vendor.bundle.js` by default.
1 parent 6f9d2c1 commit 31e538a

File tree

10 files changed

+58
-13
lines changed

10 files changed

+58
-13
lines changed

packages/angular-cli/commands/build.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface BuildOptions {
1212
baseHref?: string;
1313
aot?: boolean;
1414
sourcemap?: boolean;
15+
vendorChunk?: boolean;
1516
}
1617

1718
const BuildCommand = Command.extend({
@@ -33,7 +34,8 @@ const BuildCommand = Command.extend({
3334
{ name: 'suppress-sizes', type: Boolean, default: false },
3435
{ name: 'base-href', type: String, default: null, aliases: ['bh'] },
3536
{ name: 'aot', type: Boolean, default: false },
36-
{ name: 'sourcemap', type: Boolean, default: true, aliases: ['sm'] }
37+
{ name: 'sourcemap', type: Boolean, default: true, aliases: ['sm'] },
38+
{ name: 'vendor-chunk', type: Boolean, default: true }
3739
],
3840

3941
run: function (commandOptions: BuildOptions) {

packages/angular-cli/commands/serve.ts

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface ServeTaskOptions {
2828
aot?: boolean;
2929
sourcemap?: boolean;
3030
open?: boolean;
31+
vendorChunk?: boolean;
3132
}
3233

3334
const ServeCommand = Command.extend({
@@ -83,6 +84,7 @@ const ServeCommand = Command.extend({
8384
{ name: 'ssl-cert', type: String, default: 'ssl/server.crt' },
8485
{ name: 'aot', type: Boolean, default: false },
8586
{ name: 'sourcemap', type: Boolean, default: true, aliases: ['sm'] },
87+
{ name: 'vendor-chunk', type: Boolean, default: true },
8688
{
8789
name: 'open',
8890
type: Boolean,

packages/angular-cli/models/webpack-build-common.ts

+15-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as webpack from 'webpack';
22
import * as path from 'path';
33
import {GlobCopyWebpackPlugin} from '../plugins/glob-copy-webpack-plugin';
4+
import {packageChunkSort} from '../utilities/package-chunk-sort';
45
import {BaseHrefWebpackPlugin} from '@angular-cli/base-href-webpack';
56

67
const HtmlWebpackPlugin = require('html-webpack-plugin');
@@ -12,7 +13,8 @@ export function getWebpackCommonConfig(
1213
environment: string,
1314
appConfig: any,
1415
baseHref: string,
15-
sourcemap: boolean
16+
sourcemap: boolean,
17+
vendorChunk: boolean
1618
) {
1719

1820
const appRoot = path.resolve(projectRoot, appConfig.root);
@@ -23,6 +25,7 @@ export function getWebpackCommonConfig(
2325
const scripts = appConfig.scripts
2426
? appConfig.scripts.map((script: string) => path.resolve(appRoot, script))
2527
: [];
28+
const extraPlugins: any[] = [];
2629

2730
let entry: { [key: string]: string[] } = {
2831
main: [appMain]
@@ -32,6 +35,14 @@ export function getWebpackCommonConfig(
3235
if (appConfig.styles.length > 0) { entry['styles'] = styles; }
3336
if (appConfig.scripts.length > 0) { entry['scripts'] = scripts; }
3437

38+
if (vendorChunk) {
39+
extraPlugins.push(new webpack.optimize.CommonsChunkPlugin({
40+
name: 'vendor',
41+
chunks: ['main'],
42+
minChunks: (module: any) => module.userRequest && !module.userRequest.includes(appRoot)
43+
}));
44+
}
45+
3546
return {
3647
devtool: sourcemap ? 'source-map' : 'eval',
3748
resolve: {
@@ -91,7 +102,7 @@ export function getWebpackCommonConfig(
91102
new HtmlWebpackPlugin({
92103
template: path.resolve(appRoot, appConfig.index),
93104
filename: path.resolve(appConfig.outDir, appConfig.index),
94-
chunksSortMode: 'dependency'
105+
chunksSortMode: packageChunkSort(['inline', 'styles', 'scripts', 'vendor', 'main'])
95106
}),
96107
new BaseHrefWebpackPlugin({
97108
baseHref: baseHref
@@ -104,10 +115,6 @@ export function getWebpackCommonConfig(
104115
.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')),
105116
path.resolve(appRoot, appConfig.environments[environment])
106117
),
107-
new webpack.optimize.CommonsChunkPlugin({
108-
// Optimizing ensures loading order in index.html
109-
name: ['styles', 'scripts', 'main'].reverse()
110-
}),
111118
new webpack.optimize.CommonsChunkPlugin({
112119
minChunks: Infinity,
113120
name: 'inline'
@@ -121,8 +128,8 @@ export function getWebpackCommonConfig(
121128
options: {
122129
postcss: [ autoprefixer() ]
123130
},
124-
}),
125-
],
131+
})
132+
].concat(extraPlugins),
126133
node: {
127134
fs: 'empty',
128135
global: true,

packages/angular-cli/models/webpack-config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export class NgCliWebpackConfig {
2525
baseHref?: string,
2626
isAoT = false,
2727
sourcemap = true,
28+
vendorChunk = false,
2829
) {
2930
const config: CliConfig = CliConfig.fromProject();
3031
const appConfig = config.config.apps[0];
@@ -36,7 +37,8 @@ export class NgCliWebpackConfig {
3637
environment,
3738
appConfig,
3839
baseHref,
39-
sourcemap
40+
sourcemap,
41+
vendorChunk
4042
);
4143
let targetConfigPartial = this.getTargetConfig(this.ngCliProject.root, appConfig);
4244
const typescriptConfigPartial = isAoT

packages/angular-cli/tasks/build-webpack-watch.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ export default Task.extend({
2525
outputDir,
2626
runTaskOptions.baseHref,
2727
runTaskOptions.aot,
28-
runTaskOptions.sourcemap
28+
runTaskOptions.sourcemap,
29+
runTaskOptions.vendorChunk
2930
).config;
3031
const webpackCompiler: any = webpack(config);
3132

packages/angular-cli/tasks/build-webpack.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ export default <any>Task.extend({
2424
outputDir,
2525
runTaskOptions.baseHref,
2626
runTaskOptions.aot,
27-
runTaskOptions.sourcemap
27+
runTaskOptions.sourcemap,
28+
runTaskOptions.vendorChunk
2829
).config;
2930

3031
const webpackCompiler: any = webpack(config);

packages/angular-cli/tasks/serve-webpack.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export default Task.extend({
2727
undefined,
2828
undefined,
2929
commandOptions.aot,
30-
commandOptions.sourcemap
30+
commandOptions.sourcemap,
31+
commandOptions.vendorChunk
3132
).config;
3233

3334
// This allows for live reload of page when changes are made to repo.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export function packageChunkSort(packages: string[]) {
2+
return function sort(left: any, right: any) {
3+
let leftIndex = packages.indexOf(left.names[0]);
4+
let rightindex = packages.indexOf(right.names[0]);
5+
6+
if ( leftIndex < 0 || rightindex < 0) {
7+
// Unknown packages are loaded last
8+
return 1;
9+
}
10+
11+
if (leftIndex > rightindex) {
12+
return 1;
13+
}
14+
15+
return -1;
16+
};
17+
}

tests/e2e/tests/build/vendor-chunk.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {ng} from '../../utils/process';
2+
import {expectFileToExist} from '../../utils/fs';
3+
import {expectToFail} from '../../utils/utils';
4+
5+
6+
export default function() {
7+
return ng('build')
8+
.then(() => expectFileToExist('dist/vendor.bundle.js'))
9+
.then(() => ng('build', '--no-vendor-chunk'))
10+
.then(() => expectToFail(() => expectFileToExist('dist/vendor.bundle.js')));
11+
}

tests/e2e/tests/third-party/bootstrap.ts

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default function() {
2525
<script type="text/javascript" src="inline.bundle.js"></script>
2626
<script type="text/javascript" src="styles.bundle.js"></script>
2727
<script type="text/javascript" src="scripts.bundle.js"></script>
28+
<script type="text/javascript" src="vendor.bundle.js"></script>
2829
<script type="text/javascript" src="main.bundle.js"></script>
2930
`));
3031
}

0 commit comments

Comments
 (0)