webpack4.0
Pan 2018-08-14 18:11:47 +08:00
parent f46e99300b
commit 4cdc865b50
6 changed files with 107 additions and 75 deletions

View File

@ -19,13 +19,15 @@ rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
webpack(webpackConfig, (err, stats) => { webpack(webpackConfig, (err, stats) => {
spinner.stop() spinner.stop()
if (err) throw err if (err) throw err
process.stdout.write(stats.toString({ process.stdout.write(
colors: true, stats.toString({
modules: false, colors: true,
children: false, modules: false,
chunks: false, children: false,
chunkModules: false chunks: false,
}) + '\n\n') chunkModules: false
}) + '\n\n'
)
if (stats.hasErrors()) { if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n')) console.log(chalk.red(' Build failed with errors.\n'))
@ -33,9 +35,11 @@ rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
} }
console.log(chalk.cyan(' Build complete.\n')) console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow( console.log(
' Tip: built files are meant to be served over an HTTP server.\n' + chalk.yellow(
' Opening index.html over file:// won\'t work.\n' ' Tip: built files are meant to be served over an HTTP server.\n' +
)) " Opening index.html over file:// won't work.\n"
)
)
}) })
}) })

View File

@ -4,8 +4,11 @@ const semver = require('semver')
const packageConfig = require('../package.json') const packageConfig = require('../package.json')
const shell = require('shelljs') const shell = require('shelljs')
function exec (cmd) { function exec(cmd) {
return require('child_process').execSync(cmd).toString().trim() return require('child_process')
.execSync(cmd)
.toString()
.trim()
} }
const versionRequirements = [ const versionRequirements = [
@ -24,23 +27,30 @@ if (shell.which('npm')) {
}) })
} }
module.exports = function () { module.exports = function() {
const warnings = [] const warnings = []
for (let i = 0; i < versionRequirements.length; i++) { for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i] const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' + warnings.push(
chalk.red(mod.currentVersion) + ' should be ' + mod.name +
chalk.green(mod.versionRequirement) ': ' +
chalk.red(mod.currentVersion) +
' should be ' +
chalk.green(mod.versionRequirement)
) )
} }
} }
if (warnings.length) { if (warnings.length) {
console.log('') console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:')) console.log(
chalk.yellow(
'To use this template, you must update following to modules:'
)
)
console.log() console.log()
for (let i = 0; i < warnings.length; i++) { for (let i = 0; i < warnings.length; i++) {

View File

@ -4,15 +4,16 @@ const config = require('../config')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const packageConfig = require('../package.json') const packageConfig = require('../package.json')
exports.assetsPath = function (_path) { exports.assetsPath = function(_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production' ? const assetsSubDirectory =
config.build.assetsSubDirectory : process.env.NODE_ENV === 'production'
config.dev.assetsSubDirectory ? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path) return path.posix.join(assetsSubDirectory, _path)
} }
exports.cssLoaders = function (options) { exports.cssLoaders = function(options) {
options = options || {} options = options || {}
const cssLoader = { const cssLoader = {
@ -73,7 +74,7 @@ exports.cssLoaders = function (options) {
} }
// Generate loaders for standalone style files (outside of .vue) // Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) { exports.styleLoaders = function(options) {
const output = [] const output = []
const loaders = exports.cssLoaders(options) const loaders = exports.cssLoaders(options)

View File

@ -5,7 +5,7 @@ const config = require('../config')
const { VueLoaderPlugin } = require('vue-loader') const { VueLoaderPlugin } = require('vue-loader')
const vueLoaderConfig = require('./vue-loader.conf') const vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) { function resolve(dir) {
return path.join(__dirname, '..', dir) return path.join(__dirname, '..', dir)
} }
@ -28,9 +28,10 @@ module.exports = {
output: { output: {
path: config.build.assetsRoot, path: config.build.assetsRoot,
filename: '[name].js', filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production' publicPath:
? config.build.assetsPublicPath process.env.NODE_ENV === 'production'
: config.dev.assetsPublicPath ? config.build.assetsPublicPath
: config.dev.assetsPublicPath
}, },
resolve: { resolve: {
extensions: ['.js', '.vue', '.json'], extensions: ['.js', '.vue', '.json'],
@ -49,7 +50,11 @@ module.exports = {
{ {
test: /\.js$/, test: /\.js$/,
loader: 'babel-loader', loader: 'babel-loader',
include: [resolve('src'), resolve('test') ,resolve('node_modules/webpack-dev-server/client')] include: [
resolve('src'),
resolve('test'),
resolve('node_modules/webpack-dev-server/client')
]
}, },
{ {
test: /\.svg$/, test: /\.svg$/,
@ -86,9 +91,7 @@ module.exports = {
} }
] ]
}, },
plugins: [ plugins: [new VueLoaderPlugin()],
new VueLoaderPlugin(),
],
node: { node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue // prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native). // source contains it (although only uses it if it's native).

View File

@ -9,7 +9,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder') const portfinder = require('portfinder')
function resolve (dir) { function resolve(dir) {
return path.join(__dirname, '..', dir) return path.join(__dirname, '..', dir)
} }
@ -19,7 +19,10 @@ const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, { const devWebpackConfig = merge(baseWebpackConfig, {
mode: 'development', mode: 'development',
module: { module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) rules: utils.styleLoaders({
sourceMap: config.dev.cssSourceMap,
usePostCSS: true
})
}, },
// cheap-module-eval-source-map is faster for development // cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool, devtool: config.dev.devtool,
@ -40,7 +43,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
proxy: config.dev.proxyTable, proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: { watchOptions: {
poll: config.dev.poll, poll: config.dev.poll
} }
}, },
plugins: [ plugins: [
@ -55,7 +58,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
inject: true, inject: true,
favicon: resolve('favicon.ico'), favicon: resolve('favicon.ico'),
title: 'vue-element-admin' title: 'vue-element-admin'
}), })
] ]
}) })
@ -71,14 +74,20 @@ module.exports = new Promise((resolve, reject) => {
devWebpackConfig.devServer.port = port devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin // Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ devWebpackConfig.plugins.push(
compilationSuccessInfo: { new FriendlyErrorsPlugin({
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], compilationSuccessInfo: {
}, messages: [
onErrors: config.dev.notifyOnErrors `Your application is running here: http://${
? utils.createNotifierCallback() devWebpackConfig.devServer.host
: undefined }:${port}`
})) ]
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
})
)
resolve(devWebpackConfig) resolve(devWebpackConfig)
} }

View File

@ -19,8 +19,8 @@ function resolve(dir) {
const env = require('../config/prod.env') const env = require('../config/prod.env')
// For NamedChunksPlugin // For NamedChunksPlugin
const seen = new Set(); const seen = new Set()
const nameLength = 4; const nameLength = 4
const webpackConfig = merge(baseWebpackConfig, { const webpackConfig = merge(baseWebpackConfig, {
mode: 'production', mode: 'production',
@ -62,7 +62,7 @@ const webpackConfig = merge(baseWebpackConfig, {
removeAttributeQuotes: true removeAttributeQuotes: true
// more options: // more options:
// https://github.com/kangax/html-minifier#options-quick-reference // https://github.com/kangax/html-minifier#options-quick-reference
}, }
// default sort mode uses toposort which cannot handle cyclic deps // default sort mode uses toposort which cannot handle cyclic deps
// in certain cases, and in webpack 4, chunk order in HTML doesn't // in certain cases, and in webpack 4, chunk order in HTML doesn't
// matter anyway // matter anyway
@ -74,28 +74,30 @@ const webpackConfig = merge(baseWebpackConfig, {
// keep chunk.id stable when chunk has no name // keep chunk.id stable when chunk has no name
new webpack.NamedChunksPlugin(chunk => { new webpack.NamedChunksPlugin(chunk => {
if (chunk.name) { if (chunk.name) {
return chunk.name; return chunk.name
} }
const modules = Array.from(chunk.modulesIterable); const modules = Array.from(chunk.modulesIterable)
if (modules.length > 1) { if (modules.length > 1) {
const hash = require("hash-sum"); const hash = require('hash-sum')
const joinedHash = hash(modules.map(m => m.id).join("_")); const joinedHash = hash(modules.map(m => m.id).join('_'))
let len = nameLength; let len = nameLength
while (seen.has(joinedHash.substr(0, len))) len++; while (seen.has(joinedHash.substr(0, len))) len++
seen.add(joinedHash.substr(0, len)); seen.add(joinedHash.substr(0, len))
return `chunk-${joinedHash.substr(0, len)}`; return `chunk-${joinedHash.substr(0, len)}`
} else { } else {
return modules[0].id; return modules[0].id
} }
}), }),
// keep module.id stable when vender modules does not change // keep module.id stable when vender modules does not change
new webpack.HashedModuleIdsPlugin(), new webpack.HashedModuleIdsPlugin(),
// copy custom static assets // copy custom static assets
new CopyWebpackPlugin([{ new CopyWebpackPlugin([
from: path.resolve(__dirname, '../static'), {
to: config.build.assetsSubDirectory, from: path.resolve(__dirname, '../static'),
ignore: ['.*'] to: config.build.assetsSubDirectory,
}]) ignore: ['.*']
}
])
], ],
optimization: { optimization: {
splitChunks: { splitChunks: {
@ -129,7 +131,7 @@ const webpackConfig = merge(baseWebpackConfig, {
// Compress extracted CSS. We are using this plugin so that possible // Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped. // duplicated CSS from different components can be deduped.
new OptimizeCSSAssetsPlugin() new OptimizeCSSAssetsPlugin()
], ]
} }
}) })
@ -141,9 +143,7 @@ if (config.build.productionGzip) {
asset: '[path].gz[query]', asset: '[path].gz[query]',
algorithm: 'gzip', algorithm: 'gzip',
test: new RegExp( test: new RegExp(
'\\.(' + '\\.(' + config.build.productionGzipExtensions.join('|') + ')$'
config.build.productionGzipExtensions.join('|') +
')$'
), ),
threshold: 10240, threshold: 10240,
minRatio: 0.8 minRatio: 0.8
@ -152,21 +152,26 @@ if (config.build.productionGzip) {
} }
if (config.build.generateAnalyzerReport || config.build.bundleAnalyzerReport) { if (config.build.generateAnalyzerReport || config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
.BundleAnalyzerPlugin
if (config.build.bundleAnalyzerReport) { if (config.build.bundleAnalyzerReport) {
webpackConfig.plugins.push(new BundleAnalyzerPlugin({ webpackConfig.plugins.push(
analyzerPort: 8080, new BundleAnalyzerPlugin({
generateStatsFile: false analyzerPort: 8080,
})) generateStatsFile: false
})
)
} }
if (config.build.generateAnalyzerReport) { if (config.build.generateAnalyzerReport) {
webpackConfig.plugins.push(new BundleAnalyzerPlugin({ webpackConfig.plugins.push(
analyzerMode: 'static', new BundleAnalyzerPlugin({
reportFilename: 'bundle-report.html', analyzerMode: 'static',
openAnalyzer: false reportFilename: 'bundle-report.html',
})) openAnalyzer: false
})
)
} }
} }