前言
基于cli搭建的系统webpack(模块加载打包工具)的版本3.6.0进行解读
1.webpack.base.conf.js
'use strict'
// 配置webpack编译入口
// 配置webpack输出路径和命名规则
// 配置模块resolve规则
// 配置不同类型模块的处理规则
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
// 获取绝对路径 必须是绝对路径(使用 Node.js 的 path 模块)__dirname指的是根目录
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
// esLint配置 是静态代码检测工具 使用eslint可以保证一致的代码风格
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
// 上下文选项用来决定根目录(即绝对路径)的入口文件
context: path.resolve(__dirname, '../'),
// entry 是打包文件的入口,将从这个文件路径下的文件作为打包入口,如果赋值一个字符串:这个字符串将为在开始编译的时候被加载,
// 如果赋值为一个数组,这个数组的所有模块在编译启动时都将被加载,例如如果依赖第三方库如bootstrap,[name].js会被追加到打包好的[name].js中,最后一个数组的元素将被export entry: ['./src/main1.js', './src/main2.js']
// 如果给一个entry 赋值为一个对象,这时将时一个多入口的{app: '', app1: ''}
// vendor: ['wangeditor'] 打包第三方
entry: {
app: './src/main.js'
},
// 生成的文件输出 path打包完成后 文件的路径 webpack输出的目标文件夹路径(例如:/dist) 读取config里面build 对象中assetsRoot属性
// filename用来配置生成的文件名 webpack输出bundle文件命名格式 「入口分块(entry chunk)」的文件名模板 chunkFilename: "[chunkhash].js", 长效缓存 附加分块(非入口)的文件名模板
// publicPath webpack编译输出的发布路径(例如'//cdn.aa.com/app/') webpack的output.publicPath 就相当于ajax的publicpath webpack打包时候会再异步模块的url上加上他
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
// 文件与文件直接的引用关系变复杂时,用alias 例如 '~': resolve(_dirname, 'src') 使用import a from '~/components/hello.vue'
// 或'components': path.resolve(__dirname, '../src/components') import a from 'components/hello.vue'
// extensions 用来解析模块的扩展数组 如果您希望扩展(如)所需的模块得到正确解析,则必须在数组中包含一个空字符串
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
// 不同类型模块的处理规则
// test 必须满足的配置条件,每个选项都会接收一个正则表达式或字符串
// exclude 一个不能满足的条件,使用绝对路径,尽量避免 尽可能使用include
// include导入的文件将由加载器转换的路径或文件的数组,使用绝对路径
// loader 一串!分开的加载器,它相对上下文解析
// loaders 作为字符串的一个加载器数组
module: {
// 模块规则(配置loader 解析器等选项)
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
// 对src和test文件夹下的.js文件使用babel-loader将es6+的代码转成es5
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{
// 对图片资源文件使用url-loader
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
// 小于10K的图片转成base64编码的dataURL字符串写到代码中
limit: 10000,
// 其他的图片转移到静态资源文件夹
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
// 对多媒体资源文件使用url-loader
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
// 对字体资源文件使用url-loader
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}
2.webpack.dev.conf.js
'use strict'
// 将webpack的热重载客户端代码添加到每个entry对应的应用
// 合并基础的webpack配置
// 配置样式文件的处理规则,styleLoaders
// 配置Source Maps
// 配置webpack插件
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
// webpack-merge 是一个可以合并数组和对象的插件
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
// html-webpack-plugin用于将webpack编译打包后的产品注入到html模版,
// 自动在index.html里面加上<link><script>标签引用webpack打包后的文件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// friendly-errors-webpack-plugin用于更友好地输出webpack的警告、错误等信息
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
// 样式文件的处理规则,对css/sass/scss等不同内容使用相应的styleLoaders
// 由utils配置出各种类型的预处理语言所需要使用的loader,例如sass需要使用sass-loader
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: true,
hot: true,
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
// webpack插件
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
// 开启webpack热更新
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
// webpack编译过程中出错的时候跳过报错阶段,不会阻塞编译,在编译结束后报错
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
// 自动将依赖注入html模板,并输出最终的html文件到目标文件夹
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})
3.vue-loader.conf.js
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = {
// 处理.vue文件中的样式
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
// 是否提取样式到单独的文件
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}
4.build.js
'use strict'
// loading动画
// 删除目标文件夹 webpack编译之后会输出到配置里面指定的目标文件夹;删除目标文件夹之后再创建是为了去除旧的内容,以免产生不可预测的影响
// 执行webpack构建
// 输出信息
// 检查NodeJS和npm的版本
require('./check-versions')()
process.env.NODE_ENV = 'production'
// ora,一个可以在终端显示spinner的插件
const ora = require('ora')
// rm,用于删除文件或文件夹的插件
const rm = require('rimraf')
const path = require('path')
// chalk,用于在控制台输出带颜色字体的插件
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const server = require('pushstate-server')
// 开启loading动画
const spinner = ora('building for production...')
spinner.start()
// 首先将整个dist 文件夹以及里面的内容删除 一面遗留旧的没用的文件 删除完成后开始webpack打包
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
// 执行webpack构建打包, 完成后在终端输出构建完成的相关信息或者输出报错信息并退出程序
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // if you are using ts-loader, setting this to true will make tyescript errors show up during build
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
if(process.env.npm_config_preview){
server.start({
port: 9526,
directory: './dist',
file: '/index.html'
});
console.log('> Listening at ' + 'http://localhost:9526' + '\n')
}
})
})
5.webpack.prod.conf.js
'use strict'
// 合并基础的webpack配置
// 配置样式文件的处理规则,styleLoaders
// 配置webpack的输出
// 配置webpack插件
// gzip模式下的webpack插件配置
// webpack-bundle分析
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
// copy-webpack-plugin,用于将static中的静态文件复制到产品文件夹dist
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// optimize-css-assets-webpack-plugin,用于优化和最小化css资源
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = process.env.NODE_ENV === 'testing'
? require('../config/test.env')
: require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
// 样式文件的处理规则,对css/sass/scss等不同内容使用相应的styleLoaders
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
// 是否使用source-map
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
// webpack插件
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
// 压缩JS代码
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
// 将css提取到单独的文件
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
// 优化、最小化css代码,如果只简单使用extract-text-plugin可能会造成css重复
// 具体原因可以看npm上面optimize-css-assets-webpack-plugin的介绍
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
// 将产品文件的引用注入到index.html
new HtmlWebpackPlugin({
// 设置html的文件名 title设置好title名称 cache是否缓存
filename: process.env.NODE_ENV === 'testing'
? 'index.html'
: config.build.index,
// 要使用的模块的路径
template: 'index.html',
// 把模版注入到bosy标签后
inject: true,
minify: {
// 删除index.html中的注释
removeComments: true,
// 删除index.html中的空格
collapseWhitespace: true,
// 删除各种html标签属性值的双引号
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
// 注入依赖的时候按照依赖先后顺序进行注入,比如,需要先注入vendor.js,再注入app.js
chunksSortMode: 'dependency'
}),
// keep module.id stable when vender modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
// 将所有从node_modules中引入的js提取到vendor.js,即抽取库文件
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
// 从vendor中提取出manifest,原因同上
// CommonsChunkPlugin提供公共代码,默认会把所有入口节点的公共代码提取出来,生成一个common.js
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets
// 将static文件夹里面的静态资源复制到dist/static
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
// 如果开启了产品gzip压缩,则利用插件将构建后的产品文件进行压缩
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
//之后通过process.env.npm_config_report来判断是否来启用webpack-bundle-analyzer 生成整个系统的大小图
// 如果启动了report,则通过插件给出webpack构建打包后的产品文件分析报告
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
6.check-versions.js
'use strict'
// chalk, 用于在控制台输出带颜色字体的插件
const chalk = require('chalk')
// semver, 语义化版本检查插件
const semver = require('semver')
const packageConfig = require('../package.json')
// shelljs, 执行Unix命令行的插件
const shell = require('shelljs')
// 开辟子进程执行指令cmd并返回结果
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
// node和npm版本需求
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
// 依次判断版本是否符合要求
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}
7.utils.js
'use strict'
// 计算资源文件存放的路径
// 生成cssLoaders用于加载.vue文件中的样式
// 生成styleLoaders用于加载不在.vue文件中的单独存在的样式文件
const path = require('path')
const config = require('../config')
// extract-text-webpack-plugin 提取bundle中的特定文本 将提取后的文本单独存放到另外的文件
// 这里用来提取 css样式
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
// 资源文件存放的路径
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
// 生成css、sass、scss等各种用来编写样式的语言所对应的loader配置
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
// 是否使用sourceMap
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
// 生成各种loader配置,并且配置了extract-text-pulgin
function generateLoaders (loader, loaderOptions) {
// 默认是css-loader
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
// 如果非css,则增加一个处理预编译语言的loader并设好相关配置属性
// 例如generateLoaders('less'),这里就会push一个less-loader
// less-loader先将less编译成css,然后再由css-loader去处理css
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
// 配置extract-text-plugin提取样式
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
// 如果无需提取样式则简单使用vue-style-loader配合各种样式loader去处理<style>里面的样式
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
// 返回各种不同处理样式的语言所对应的loader
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
// 生成处理单独的.css、.sass、.scss等样式文件的规则
exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}
8.config/index.js
'use strict'
// Template version: 1.2.7
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths 静态资源文件夹
assetsSubDirectory: 'static',
assetsPublicPath: '/',
// 代理配置表,在这里可以配置特定的请求代理到对应的API接口
proxyTable: {},
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
// dev-server监听的端口
port: 9555, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
// 是否自动打开浏览器
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
// 是否开启 cssSourceMap
cssSourceMap: false,
},
build: {
// Template for index.html
// html入口文件
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
// 产品文件的存放路径
assetsRoot: path.resolve(__dirname, '../dist'),
// 存放静态资源文件的目录,位于dist文件夹下
assetsSubDirectory: 'static',
// 发布路径,用于发布CDN或者放到其他域名的服务器,设置之后构建的文件在注入到index.html中的就会带上这个路径
assetsPublicPath: '/',
/**
* Source Maps
*/
// 是否使用source-map
productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
// 是否开启gzip压缩
productionGzip: false,
// gzip模式下需要压缩的文件的扩展名,设置js、css之后就只会对js和css文件进行压缩
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
// 是否展示webpack构建打包之后的分析报告
bundleAnalyzerReport: process.env.npm_config_report
}
}