perf: improve build speed

In practice, both thread-loader and cache (uglify, babel, cache-loader) slow down process up to 10sec with their overhead so disabled by default.
This commit is contained in:
Pooya Parsa 2018-03-23 12:04:55 +04:30
parent ca80e0614d
commit ac79cf3282
7 changed files with 112 additions and 57 deletions

View File

@ -18,6 +18,7 @@ import upath from 'upath'
import { r, wp, wChunk, createRoutes, parallel, relativeTo, waitFor, createSpinner } from '../common/utils' import { r, wp, wChunk, createRoutes, parallel, relativeTo, waitFor, createSpinner } from '../common/utils'
import Options from '../common/options' import Options from '../common/options'
import PerfLoader from './webpack/utils/perf-loader'
import ClientWebpackConfig from './webpack/client' import ClientWebpackConfig from './webpack/client'
import ServerWebpackConfig from './webpack/server' import ServerWebpackConfig from './webpack/server'
@ -39,6 +40,9 @@ export default class Builder {
this.webpackHotMiddleware = null this.webpackHotMiddleware = null
this.filesWatcher = null this.filesWatcher = null
this.customFilesWatcher = null this.customFilesWatcher = null
this.perfLoader = null
// Shared spinner
this.spinner = createSpinner({ minimal: this.options.minimalCLI }) this.spinner = createSpinner({ minimal: this.options.minimalCLI })
this.spinner.enabled = !this.options.test this.spinner.enabled = !this.options.test
@ -419,7 +423,8 @@ export default class Builder {
} }
async webpackBuild() { async webpackBuild() {
debug('Building files...') this.perfLoader = new PerfLoader(this)
const compilersOptions = [] const compilersOptions = []
// Client // Client
@ -449,7 +454,7 @@ export default class Builder {
} }
}) })
// Initialize compilers // Configure compilers
this.compilers = compilersOptions.map(compilersOption => { this.compilers = compilersOptions.map(compilersOption => {
const compiler = webpack(compilersOption) const compiler = webpack(compilersOption)
@ -461,6 +466,13 @@ export default class Builder {
return compiler return compiler
}) })
// Warmup perfLoader before build
if (this.options.build.parallel) {
this.spinner.start('Warming up worker pools')
this.perfLoader.warmup()
this.spinner.succeed('Worker pools ready')
}
// Start Builds // Start Builds
await parallel(this.compilers, compiler => { await parallel(this.compilers, compiler => {
return this.webpackCompile(compiler) return this.webpackCompile(compiler)

View File

@ -99,34 +99,6 @@ export default class WebpackBaseConfig {
} }
} }
perfLoaders(_baseLoaders) {
if (this.options.dev) {
return _baseLoaders
}
const loaders = []
if (this.options.build.cache) {
// https://github.com/webpack-contrib/cache-loader
loaders.push({
loader: 'cache-loader',
options: {
cacheDirectory: path.resolve('node_modules/.cache/cache-loader')
}
})
}
if (this.options.build.parallel) {
// https://github.com/webpack-contrib/thread-loader
loaders.push({
loader: 'thread-loader',
options: {}
})
}
return loaders.concat(_baseLoaders)
}
rules() { rules() {
const styleLoader = new StyleLoader( const styleLoader = new StyleLoader(
this.options, this.options,
@ -134,6 +106,8 @@ export default class WebpackBaseConfig {
{ isServer: this.isServer } { isServer: this.isServer }
) )
const perfLoader = this.builder.perfLoader
return [ return [
{ {
test: /\.vue$/, test: /\.vue$/,
@ -143,56 +117,62 @@ export default class WebpackBaseConfig {
{ {
test: /\.jsx?$/, test: /\.jsx?$/,
exclude: /node_modules/, exclude: /node_modules/,
use: this.perfLoaders({ use: perfLoader.pool('js', {
loader: 'babel-loader', loader: 'babel-loader',
options: this.getBabelOptions() options: this.getBabelOptions()
}) })
}, },
{ {
test: /\.css$/, test: /\.css$/,
use: styleLoader.apply('css') use: perfLoader.pool('css', styleLoader.apply('css'))
}, },
{ {
test: /\.less$/, test: /\.less$/,
use: styleLoader.apply('less', 'less-loader') use: perfLoader.pool('css', styleLoader.apply('less', 'less-loader'))
}, },
{ {
test: /\.sass$/, test: /\.sass$/,
use: styleLoader.apply('sass', { use: perfLoader.pool('css', styleLoader.apply('sass', {
loader: 'sass-loader', loader: 'sass-loader',
options: { indentedSyntax: true } options: { indentedSyntax: true }
}) }))
}, },
{ {
test: /\.scss$/, test: /\.scss$/,
use: styleLoader.apply('scss', 'sass-loader') use: perfLoader.pool('css', styleLoader.apply('scss', 'sass-loader'))
}, },
{ {
test: /\.styl(us)?$/, test: /\.styl(us)?$/,
use: styleLoader.apply('stylus', 'stylus-loader') use: perfLoader.pool('css', styleLoader.apply('stylus', 'stylus-loader'))
}, },
{ {
test: /\.(png|jpe?g|gif|svg)$/, test: /\.(png|jpe?g|gif|svg)$/,
use: perfLoader.pool('assets', {
loader: 'url-loader', loader: 'url-loader',
options: { options: {
limit: 1000, // 1KO limit: 1000, // 1KO
name: 'img/[name].[hash:7].[ext]' name: 'img/[name].[hash:7].[ext]'
} }
})
}, },
{ {
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: perfLoader.pool('assets', {
loader: 'url-loader', loader: 'url-loader',
options: { options: {
limit: 1000, // 1 KO limit: 1000, // 1 KO
name: 'fonts/[name].[hash:7].[ext]' name: 'fonts/[name].[hash:7].[ext]'
} }
})
}, },
{ {
test: /\.(webm|mp4)$/, test: /\.(webm|mp4)$/,
use: perfLoader.pool('assets', {
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: 'videos/[name].[hash:7].[ext]' name: 'videos/[name].[hash:7].[ext]'
} }
})
} }
] ]
} }

View File

@ -112,7 +112,7 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
config.optimization.minimizer = [ config.optimization.minimizer = [
new UglifyJsWebpackPlugin({ new UglifyJsWebpackPlugin({
parallel: true, parallel: true,
cache: true, cache: this.options.build.cache,
sourceMap: false sourceMap: false
}) })
] ]

View File

@ -44,7 +44,8 @@ export default class ProgressPlugin extends webpack.ProgressPlugin {
const progress = Math.floor(percent * 100) const progress = Math.floor(percent * 100)
this.state.progress = progress this.state.progress = progress
this.state.msg = (msg && msg.length) ? msg : 'wait' this.state.msg = (msg && msg.length) ? msg : (progress === 100 ? 'done' : 'still building')
this.state.details = details
// Process all states // Process all states
let inProgress = false let inProgress = false
@ -72,7 +73,9 @@ export default class ProgressPlugin extends webpack.ProgressPlugin {
if (!inProgress) { if (!inProgress) {
logUpdate.clear() logUpdate.clear()
} else { } else {
logUpdate('\n' + lines.join('\n') + '\n') const title = chalk.bgRedBright.black('BUILDING')
logUpdate('\n' + title + '\n\n' + lines.join('\n'))
} }
} }

View File

@ -0,0 +1,60 @@
import path from 'path'
import threadLoader from 'thread-loader'
// https://github.com/webpack-contrib/thread-loader
// https://github.com/webpack-contrib/cache-loader
export default class PerfLoader {
constructor(builder) {
this.builder = builder
this.options = builder.options
this.workerPools = {
js: {
name: 'js',
poolTimeout: this.options.dev ? Infinity : 2000
},
css: {
name: 'css',
poolTimeout: this.options.dev ? Infinity : 2000
}
}
}
warmup() {
if (!this.options.build.parallel) {
return
}
threadLoader.warmup(this.workerPools.js, ['babel-loader', 'babel-preset-env'])
threadLoader.warmup(this.workerPools.css, ['css-loader'])
}
pool(poolName, _loaders) {
const loaders = [].concat(_loaders)
if (this.options.build.parallel) {
const pool = this.workerPools[poolName]
if (pool) {
loaders.unshift({
loader: 'thread-loader',
options: pool
})
}
}
if (this.options.build.cache) {
loaders.unshift({
loader: 'cache-loader',
options: {
cacheDirectory: path.resolve('node_modules/.cache/cache-loader')
}
})
}
return loaders
}
}

View File

@ -37,8 +37,8 @@ export default {
extractCSS: false, extractCSS: false,
cssSourceMap: undefined, cssSourceMap: undefined,
ssr: undefined, ssr: undefined,
parallel: os.cpus().length > 1, parallel: false,
cache: true, cache: false,
publicPath: '/_nuxt/', publicPath: '/_nuxt/',
filenames: { filenames: {
app: '[name].[chunkhash].js', app: '[name].[chunkhash].js',

View File

@ -143,7 +143,7 @@ Options.from = function (_options) {
// babel cacheDirectory // babel cacheDirectory
if (options.build.babel.cacheDirectory === undefined) { if (options.build.babel.cacheDirectory === undefined) {
options.build.babel.cacheDirectory = options.dev options.build.babel.cacheDirectory = options.dev && options.build.cache
} }
// Resource hints // Resource hints