Skip to content

Commit 8dff383

Browse files
committed
feat: auto DLL
1 parent 7605bd6 commit 8dff383

File tree

6 files changed

+166
-91
lines changed

6 files changed

+166
-91
lines changed

packages/@vue/cli-service/lib/config/base.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,6 @@ module.exports = (api, options) => {
102102
child_process: 'empty'
103103
})
104104

105-
const htmlPath = api.resolve('public/index.html')
106-
webpackConfig
107-
.plugin('html')
108-
.use(require('html-webpack-plugin'), [
109-
fs.existsSync(htmlPath) ? { template: htmlPath } : {}
110-
])
111-
112105
// inject preload/prefetch to HTML
113106
const PreloadPlugin = require('../webpack/PreloadPlugin')
114107
webpackConfig
@@ -126,6 +119,13 @@ module.exports = (api, options) => {
126119
include: 'asyncChunks'
127120
}])
128121

122+
const htmlPath = api.resolve('public/index.html')
123+
webpackConfig
124+
.plugin('html')
125+
.use(require('html-webpack-plugin'), [
126+
fs.existsSync(htmlPath) ? { template: htmlPath } : {}
127+
])
128+
129129
webpackConfig
130130
.plugin('define')
131131
.use(require('webpack/lib/DefinePlugin'), [

packages/@vue/cli-service/lib/config/prod.js

+113-81
Original file line numberDiff line numberDiff line change
@@ -42,95 +42,99 @@ module.exports = (api, options) => {
4242
])
4343

4444
// minify JS
45+
const UglifyPlugin = require('uglifyjs-webpack-plugin')
46+
const uglifyPluginOptions = {
47+
uglifyOptions: {
48+
compress: {
49+
// turn off flags with small gains to speed up minification
50+
arrows: false,
51+
collapse_vars: false, // 0.3kb
52+
comparisons: false,
53+
computed_props: false,
54+
hoist_funs: false,
55+
hoist_props: false,
56+
hoist_vars: false,
57+
inline: false,
58+
loops: false,
59+
negate_iife: false,
60+
properties: false,
61+
reduce_funcs: false,
62+
reduce_vars: false,
63+
switches: false,
64+
toplevel: false,
65+
typeofs: false,
66+
67+
// a few flags with noticable gains/speed ratio
68+
// numbers based on out of the box vendor bundle
69+
booleans: true, // 0.7kb
70+
if_return: true, // 0.4kb
71+
sequences: true, // 0.7kb
72+
unused: true, // 2.3kb
73+
74+
// required features to drop conditional branches
75+
conditionals: true,
76+
dead_code: true,
77+
evaluate: true
78+
}
79+
},
80+
sourceMap: options.productionSourceMap,
81+
cache: true,
82+
parallel: true
83+
}
4584
// disable during tests to speed things up
4685
if (!process.env.VUE_CLI_TEST) {
4786
webpackConfig
48-
.plugin('uglifyjs')
49-
.use(require('uglifyjs-webpack-plugin'), [{
50-
uglifyOptions: {
51-
compress: {
52-
// turn off flags with small gains to speed up minification
53-
arrows: false,
54-
collapse_vars: false, // 0.3kb
55-
comparisons: false,
56-
computed_props: false,
57-
hoist_funs: false,
58-
hoist_props: false,
59-
hoist_vars: false,
60-
inline: false,
61-
loops: false,
62-
negate_iife: false,
63-
properties: false,
64-
reduce_funcs: false,
65-
reduce_vars: false,
66-
switches: false,
67-
toplevel: false,
68-
typeofs: false,
69-
70-
// a few flags with noticable gains/speed ratio
71-
// numbers based on out of the box vendor bundle
72-
booleans: true, // 0.7kb
73-
if_return: true, // 0.4kb
74-
sequences: true, // 0.7kb
75-
unused: true, // 2.3kb
76-
77-
// required features to drop conditional branches
78-
conditionals: true,
79-
dead_code: true,
80-
evaluate: true
81-
}
82-
},
83-
sourceMap: options.productionSourceMap,
84-
cache: true,
85-
parallel: true
86-
}])
87+
.plugin('uglify')
88+
.use(UglifyPlugin, [uglifyPluginOptions])
8789
}
8890

89-
// Chunk splits
9091
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin')
9192

92-
// extract vendor libs into its own chunk for better caching, since they
93-
// are more likely to stay the same.
94-
webpackConfig
95-
.plugin('split-vendor')
96-
.use(CommonsChunkPlugin, [{
97-
name: 'vendor',
98-
minChunks (module) {
99-
// any required modules inside node_modules are extracted to vendor
100-
return (
101-
module.resource &&
102-
/\.js$/.test(module.resource) &&
103-
module.resource.indexOf(`node_modules`) > -1
104-
)
105-
}
106-
}])
93+
// Chunk splits
94+
if (!options.dll) {
95+
// extract vendor libs into its own chunk for better caching, since they
96+
// are more likely to stay the same.
97+
webpackConfig
98+
.plugin('split-vendor')
99+
.use(CommonsChunkPlugin, [{
100+
name: 'vendor',
101+
minChunks (module) {
102+
// any required modules inside node_modules are extracted to vendor
103+
return (
104+
module.resource &&
105+
/\.js$/.test(module.resource) &&
106+
module.resource.indexOf(`node_modules`) > -1
107+
)
108+
}
109+
}])
107110

108-
// extract webpack runtime and module manifest to its own file in order to
109-
// prevent vendor hash from being updated whenever app bundle is updated
110-
webpackConfig
111-
.plugin('split-manifest')
112-
.use(CommonsChunkPlugin, [{
113-
name: 'manifest',
114-
minChunks: Infinity
115-
}])
111+
// extract webpack runtime and module manifest to its own file in order to
112+
// prevent vendor hash from being updated whenever app bundle is updated
113+
webpackConfig
114+
.plugin('split-manifest')
115+
.use(CommonsChunkPlugin, [{
116+
name: 'manifest',
117+
minChunks: Infinity
118+
}])
116119

117-
// inline the manifest chunk into HTML
118-
webpackConfig
119-
.plugin('inline-manifest')
120-
.use(require('../webpack/InlineSourcePlugin'), [{
121-
include: /manifest\..*\.js$/
122-
}])
120+
// inline the manifest chunk into HTML
121+
webpackConfig
122+
.plugin('inline-manifest')
123+
.use(require('../webpack/InlineSourcePlugin'), [{
124+
include: /manifest\..*\.js$/
125+
}])
123126

124-
// since manifest is inlined, don't preload it anymore
125-
webpackConfig
126-
.plugin('preload')
127-
.tap(([options]) => {
128-
options.fileBlacklist.push(/manifest\..*\.js$/)
129-
return [options]
130-
})
131-
132-
// This instance extracts shared chunks from code splitted chunks and bundles them
133-
// in a separate chunk, similar to the vendor chunk
127+
// since manifest is inlined, don't preload it anymore
128+
webpackConfig
129+
.plugin('preload')
130+
.tap(([options]) => {
131+
options.fileBlacklist.push(/manifest\..*\.js$/)
132+
return [options]
133+
})
134+
}
135+
136+
// This CommonsChunkPlugin instance extracts shared chunks from async
137+
// chunks and bundles them in a separate chunk, similar to the vendor chunk
134138
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
135139
webpackConfig
136140
.plugin('split-vendor-async')
@@ -141,6 +145,36 @@ module.exports = (api, options) => {
141145
minChunks: 3
142146
}])
143147

148+
// DLL
149+
if (options.dll) {
150+
const webpack = require('webpack')
151+
const resolveClientEnv = require('../util/resolveClientEnv')
152+
const dllEntries = Array.isArray(options.dll)
153+
? options.dll
154+
: Object.keys(api.service.pkg.dependencies)
155+
156+
webpackConfig
157+
.plugin('dll')
158+
.use(require('autodll-webpack-plugin'), [{
159+
inject: true,
160+
inherit: true,
161+
path: 'js/',
162+
context: api.resolve('.'),
163+
filename: '[name].[hash:8].js',
164+
entry: {
165+
'vendor': [
166+
...dllEntries,
167+
'vue-loader/lib/component-normalizer'
168+
]
169+
},
170+
plugins: [
171+
new webpack.DefinePlugin(resolveClientEnv(options.baseUrl)),
172+
new UglifyPlugin(uglifyPluginOptions)
173+
]
174+
}])
175+
.after('preload')
176+
}
177+
144178
// copy static assets in public/
145179
webpackConfig
146180
.plugin('copy')
@@ -162,8 +196,6 @@ module.exports = (api, options) => {
162196
// .before('vue-loader')
163197
// .loader('thread-loader')
164198
// .options({ name: 'vue' })
165-
166-
// TODO DLL
167199
}
168200
})
169201
}

packages/@vue/cli-service/lib/options.js

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ const schema = createSchema(joi => joi.object({
66
compiler: joi.boolean(),
77
productionSourceMap: joi.boolean(),
88
vueLoader: joi.object(),
9+
dll: joi.alternatives().try(
10+
joi.boolean(),
11+
joi.array().items(joi.string())
12+
),
913
css: joi.object({
1014
modules: joi.boolean(),
1115
extract: joi.boolean(),

packages/@vue/cli-service/lib/webpack/PreloadPlugin.js

+25-2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ module.exports = class PreloadPlugin {
5757
apply (compiler) {
5858
const options = this.options
5959
compiler.plugin('compilation', compilation => {
60+
// Auto DLL plugin injects assets by mutating html plugin data, so the only
61+
// way to get a hold of those is by saving the pre-mutated assets and
62+
// comparing them later.
63+
let originalAssets
64+
compilation.plugin('html-webpack-plugin-before-html-generation', (htmlPluginData, cb) => {
65+
originalAssets = [
66+
...htmlPluginData.assets.js,
67+
...htmlPluginData.assets.css
68+
]
69+
cb(null, htmlPluginData)
70+
})
71+
6072
compilation.plugin('html-webpack-plugin-before-html-processing', (htmlPluginData, cb) => {
6173
let filesToInclude = ''
6274
let extractedChunks = []
@@ -95,11 +107,22 @@ module.exports = class PreloadPlugin {
95107

96108
const publicPath = compilation.outputOptions.publicPath || ''
97109

98-
// Only handle the chunk import by the htmlWebpackPlugin
110+
// Only handle the chunk imported by the htmlWebpackPlugin
99111
extractedChunks = extractedChunks.filter(chunk => doesChunkBelongToHTML(
100112
chunk, getValues(htmlPluginData.assets.chunks), {}))
101113

102-
flatten(extractedChunks.map(chunk => chunk.files)).filter(entry => {
114+
let files = flatten(extractedChunks.map(chunk => chunk.files))
115+
116+
// if handling initial or all chunks, also include assets injected by
117+
// Auto DLL plugin.
118+
if (options.include === 'initial' || options.include === 'all') {
119+
files = [...htmlPluginData.assets.js, ...htmlPluginData.assets.css]
120+
.filter(file => !originalAssets.includes(file))
121+
.map(file => file.replace(publicPath, ''))
122+
.concat(files)
123+
}
124+
125+
Array.from(new Set(files)).filter(entry => {
103126
return this.options.fileBlacklist.every(regex => regex.test(entry) === false)
104127
}).forEach(entry => {
105128
entry = `${publicPath}${entry}`

packages/@vue/cli-service/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@vue/cli-overlay": "^3.0.0-alpha.1",
2525
"@vue/cli-shared-utils": "^3.0.0-alpha.1",
2626
"address": "^1.0.3",
27+
"autodll-webpack-plugin": "^0.3.8",
2728
"autoprefixer": "^7.2.5",
2829
"case-sensitive-paths-webpack-plugin": "^2.1.1",
2930
"chalk": "^2.3.0",

yarn.lock

+16-1
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,21 @@ atob@^2.0.0:
10031003
version "2.0.3"
10041004
resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d"
10051005

1006+
autodll-webpack-plugin@^0.3.8:
1007+
version "0.3.8"
1008+
resolved "https://registry.yarnpkg.com/autodll-webpack-plugin/-/autodll-webpack-plugin-0.3.8.tgz#d087dda8ca3f9e3231a15df3d42d1efde73c5777"
1009+
dependencies:
1010+
bluebird "^3.5.0"
1011+
del "^3.0.0"
1012+
find-cache-dir "^1.0.0"
1013+
lodash "^4.17.4"
1014+
make-dir "^1.0.0"
1015+
memory-fs "^0.4.1"
1016+
mkdirp "^0.5.1"
1017+
read-pkg "^2.0.0"
1018+
webpack-merge "^4.1.0"
1019+
webpack-sources "^1.0.1"
1020+
10061021
autoprefixer@^6.3.1:
10071022
version "6.7.7"
10081023
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
@@ -9796,7 +9811,7 @@ webpack-dev-server@^2.11.0:
97969811
webpack-dev-middleware "1.12.2"
97979812
yargs "6.6.0"
97989813

9799-
webpack-merge@^4.1.1:
9814+
webpack-merge@^4.1.0, webpack-merge@^4.1.1:
98009815
version "4.1.1"
98019816
resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.1.tgz#f1197a0a973e69c6fbeeb6d658219aa8c0c13555"
98029817
dependencies:

0 commit comments

Comments
 (0)