构建环境下的配置:
loading动画
删除创建目标文件夹
webpack编译;输出信息
//这是vue-cli脚手架工具的生产环境配置入口 package.json中的"build": "node build/build.js"的直接指向。
// js的严格模式
'use strict'
//检查node+npm的版本,引用./check-versions.js文件
require('./check-versions')()
//设置环境变量为生产环境
process.env.NODE_ENV = 'production'
//导入ora模块 实现loading效果,实际是一个命令行转圈圈动画插件,好看用的
const ora = require('ora')
//导入rimraf模块 以包的形式包装rm -rf命令,用来删除文件和文件夹的,不管文件是否为空
//rimraf插件是用来执行UNIX命令rm和-rf的用来删除文件夹和文件,清空旧的文件
const rm = require('rimraf')
//导入node的path模块,node.js路径模块 连接路径,例子:path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
const path = require('path')
//导入chalk模块 用来改变文字颜色
const chalk = require('chalk')
//导入webpack模块使用内置插件和webpack方法
const webpack = require('webpack')
//导入config/index.js,commonJs风格,引入文件模块,引入模块分为内置模块与文件模块两种
const config = require('../config')
//导入webpack.prod.conf
const webpackConfig = require('./webpack.prod.conf')
//实现loading的模块
const spinner = ora('building for production...')
//开始动画
spinner.start()
/*
rm方法删除dist/static文件夹
path.join是将路径片段以'\'连接成新的路径,任何一个路径片段有错误就会报错
调用rm方法,第一个参数的结果就是 绝对/工程名/dist/static,表示删除这个路径下面的所有文件
若删除中有错误则抛出异常并终止程序
若没有错误则继续执行,构建webpack
结束动画
若有异常则抛出
标准输出流,类似于console.log
*/
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
//如果删除的过程中出现错误,就抛出这个错误,同时程序终止
if (err) throw err
//没有错误,就执行webpack编译 构建webpack
webpack(webpackConfig, (err, stats) => {
//停止动画
spinner.stop()
if (err) throw err //如果有错误就抛出错误
//process.stdout.write是标准输出,相当于console.log
process.stdout.write(stats.toString({
//stats对象中保存着编译过程中的各种消息
colors: true, // 增加控制台颜色开关
modules: false, // 是否增加内置模块信息
children: false, // 不增加子级信息
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'
))
})
})
node和npm的版本检查
// js的严格模式
'use strict'
// 导入chalk模块 用来改变文字颜色
const chalk = require('chalk')
// 控制版本
const semver = require('semver')
// 获取版本,commonJs风格,引入文件模块,引入模块分为内置模块与文件模块两种
const packageConfig = require('../package.json')
// shell.js插件,执行unix系统命令
const shell = require('shelljs')
function exec (cmd) {
// 脚本可以通过child_process模块新建子进程,从而执行Unix系统命令
// 将cmd参数传递的值转换成前后没有空格的字符串,也就是版本号
// require('child_process')调用nodejs子进程,
// execSync同步的exec方法执行command
return require('child_process').execSync(cmd).toString().trim()
}
//声明常量数组,数组内容为有关node相关信息的对象
const versionRequirements = [
{
//对象名称为node
name: 'node',
//使用semver插件,把版本信息转换成规定格式
currentVersion: semver.clean(process.version),
//规定package.json中engines选项的node版本信息
versionRequirement: packageConfig.engines.node
}
]
//which为linux指令,在$path规定的路径下查找符合条件的文件
//shell.which('npm')就是寻找npm命令
if (shell.which('npm')) {
//将npm添加到versionRequirements
versionRequirements.push({
name: 'npm',
调用npm --version命令,并且把参数返回给exec函数获取纯净版本
//exec()方法就是执行一个命令(在这里是执行npm --version),返回版本信息的函数。
currentVersion: exec('npm --version'),
//规定package.json中engines选项的npm版本信息
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
// 如果版本号不符合package.json文件中指定的版本号,就执行warning.push...
// 当前版本号用红色标识,要求版本号用绿色标识,当前版本不符合要求版本那么就将提示信息添加到wranings
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)
}
//执行失败
process.exit(1)
}
}
配置静态资源路径;
生成cssLoaders用于加载.vue文件中的样式;
生成styleLoaders用于加载不在.vue文件中的单独存在的样式文件
'use strict'
// 引入nodejs路径模块
const path = require('path')
// 引入config下的index.js文件
const config = require('../config')
// 一个插件,抽离css样式,防止将样式打包在js中引起样式加载错乱
// 引入extract-text-webpack-plugin插件,用来将css提取到单独的css文件中
// 详情请看(1)
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
// exports其实就是一个对象,用来导出方法的,最终还是使用module.exports,此处导出assetsPath
exports.assetsPath = function (_path) {
// 如果是生产环境assetsSubDirectory就是'static',否则还是'static'
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
// path.join返回绝对路径(在电脑上的实际位置);path.posix.join返回相对路径
return path.posix.join(assetsSubDirectory, _path)
// 此方法返回一个干净的相对根路径
}
// 下面是导出cssLoaders的相关配置
exports.cssLoaders = function (options) {
// options如果不为null或者undefined,0,""等等就原样,否则就是{}。在js里面,||运算符,A||B,A如果为真,直接返回A。如果为假,直接返回B(不会判断B是什么类型)
options = options || {}
// cssLoader的基本配置
const cssLoader = {
loader: 'css-loader',
// options是用来传递参数给loader的
options: {
// minimize表示压缩,如果是生产环境就压缩css代码,这里全部压缩
// minimize: process.env.NODE_ENV === 'production',
// 是否开启cssmap,默认是false
sourceMap: options.sourceMap
}
}
// 为css自动生成兼容性前缀
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// 适配移动端屏幕自适应像素兼容性75px==1rem,这里没有用到
// const px2remLoader = {
// loader: 'px2rem-loader',
// options: {
// remUnit: 75
// }
// }
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
// 将上面的基础cssLoader配置、兼容性前缀配置、自适应配置放在一个数组里面
// const loaders = options.usePostCSS ? [cssLoader, postcssLoader, px2remLoader] : [cssLoader, px2remLoader]
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
// 如果该函数传递了单独的loader就加到这个loaders数组里面,这个loader可能是less,sass之类的
if (loader) {
loaders.push({
// 加载对应的loader
loader: loader + '-loader',
// Object.assign是es6的方法,主要用来合并对象的,浅拷贝
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
// 注意这个extract是自定义的属性,可以定义在options里面,主要作用就是当配置为true就把文件单独提取,
// false表示不单独提取,这个可以在使用的时候单独配置
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
// 上面这段代码就是用来返回最终读取和导入loader,来处理对应类型的文件
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
// css对应 vue-style-loader 和 css-loader
css: generateLoaders(),
// postcss对应 vue-style-loader 和 css-loader
postcss: generateLoaders(),
// less对应 vue-style-loader 和 less-loader
less: generateLoaders('less'),
// sass对应 vue-style-loader 和 sass-loader
sass: generateLoaders('sass', { indentedSyntax: true }),
// scss对应 vue-style-loader 和 sass-loader
scss: generateLoaders('sass'),
// stylus对应 vue-style-loader 和 stylus-loader
stylus: generateLoaders('stylus'),
// styl对应 vue-style-loader 和 styl-loader
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
// 下面这个主要处理import这种方式导入的文件类型的打包,上面的exports.cssLoaders是为这一步服务的
exports.styleLoaders = function (options) {
const output = []
// 下面就是生成的各种css文件的loader对象
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
// 把每一种文件的laoder都提取出来
const loader = loaders[extension]
output.push({
// 把最终的结果都push到output数组中,大事搞定
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')
})
}
}
// extract-text-webpack-plugin插件是用来将文本从bundle中提取到一个单独的文件中
// 基本使用方法如下
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/, //主要用来处理css文件
use: ExtractTextPlugin.extract({
fallback: "style-loader", // fallback表示如果css文件没有成功导入就使用style-loader导入
use: "css-loader" // 表示使用css-loader从js读取css文件
})
}
],
plugins: [
new ExtractTextPlugin("styles.css") //表示生成styles.css文件
]
}
}
处理.vue文件的配置文件
'use strict'
const utils = require('./utils')
const config = require('../config')
// 是否为生产环境
const isProduction = process.env.NODE_ENV === 'production'
// 根据环境来获取相应的productionSourceMap或者cssSourceMap
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
// 导出
module.exports = {
// 载入utils中的cssloaders返回配置好的css-loader和vue-style=loader
loaders: utils.cssLoaders({
// 是否开始sourceMap 用来调试
sourceMap: sourceMapEnabled,
// 是否将单独的css文件(一般为引入的外部文件)进行提取单独打包
extract: isProduction
}),
// 记录压缩的代码,用来找到源码位置
cssSourceMap: sourceMapEnabled,
// 是否缓存破坏
cacheBusting: config.dev.cacheBusting,
// transformToRequire的作用是在模块编译的过程中,编译器可以将某些属性,比如src转换为require调用
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}
基本的webpack配置
配置webpack编译入口
配置webpack输出路径和命名规则
配置模块resolve规则
配置不同类型模块的处理规则
'use strict'
// 引入node path 中间件 可以获取到 path 路径的一些信息
const path = require('path')
// 引入utils工具模块 utils主要用来处理css-loader和vue-style-loader的
const utils = require('./utils')
// 引入config下面的index文件 主要是配置一些开发环境和生产环境的配置
const config = require('../config')
// 用来解决各种css 文件 sass less stulys 等
const vueLoaderConfig = require('./vue-loader.conf')
// 定义了一个路径函数 返回当前的目录的平行目录下的dir 因为有'..'也就是获取绝对路径,方便对import时引入地址的方便填写
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
// eslint 的检测规则
const createLintingRule = () => ({
// 对js和vue 文件进行eslint 检查
test: /\.(js|vue)$/,
// 使用eslint-loader
loader: 'eslint-loader',
// enforce执行的意思 有两个值 pre post
// pre是在其他规则执行之前执行 post是在其他规则执行之后执行
enforce: 'pre',
// 进行检测的文件目录包括哪些 调用了路径函数
include: [resolve('src'), resolve('test')],
options: {
// 使用第三方的插件进行eslint 检测
formatter: require('eslint-friendly-formatter'),
// 是否输出eslint报错信息
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
// webpack的配置,可以理解成是开发环境和正式环境的一些公共配置
module.exports = {
// webpack 解析时根目录地址如果此文件在跟目录这句话就不用写
context: path.resolve(__dirname, '../'),
// 项目的入口文件
entry: {
app: './src/main.js'
},
// 项目出口文件配置
output: {
// 项目buid的出口文件的目录地址,这里指的是(../dist文件目录) 引入的是config.build 下面的配置
path: config.build.assetsRoot,
// 文件的名字
filename: '[name].js',
// 输出解析文件的目录,url 相对于 HTML 页面(生成的html文件中,css和js等静态文件的url前缀)
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
// 指定哪些文件在引用时可以省略后缀名
extensions: ['.js', '.vue', '.json'],
// 别名,在引用文件时 使用别名代理真实目录 后面再在目录时以别名代替
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
module: {
// 转换解析规则
// 1.test是用来解析所有此后缀名的文件,
// 2.loader我们用什么npm什么形式的loader去解析
// 3.include是代表我们解析的文件只包含那些东西
// 4.options解析文件参数设置 具体看下面的解释
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/,
loader: 'vue-loader',
// 对vue的css进行解析
options: vueLoaderConfig
},
{
// 对js文件使用babel-loader进行解析 主要是解析es6
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
// 对图片使用url-loader解析 这个插件的作用是将一个足够小的文件生成一个64位的DataURL
// 当limit小于10000进行将图片生成base64
// name 指的是引入的utils里面的一个方法 将name ,7位哈希 .ext 代表后缀名
// 传入方法 返回结果是 /static/img/[name].[hash:7].[ext]
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
// 对一些音频文件进行解析
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
// 对字体文件进行解析
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
// 是否 polyfill 或 mock
// 源包含它(虽然仅在本地使用)
// 预防webpack从注入模拟节点到node原生模块
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
},
// webpack不处理下面的库,压缩包的大小,但是依然可以引用
externals: {
'AMap': 'AMap',
}
}
'use strict'
// 引入当前目录下的utils.js文件模块
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
// 进行合并对象,相同项目会进行覆盖
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')\
// 一个拷贝资源的插件
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 生成html文件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 下面这个插件是用来把webpack的错误和日志收集起来,漂亮的展示给用户
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// 一个自动打开可用端口的包
const portfinder = require('portfinder')
// 当前环境的host
const HOST = process.env.HOST
// //当前环境的port
const PORT = process.env.PORT && Number(process.env.PORT)
// 开发环境的配置
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
// loader的配置,创建模块时匹配请求的规则数组,这里调用了utils中的配置模板styleLoaders
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
// //debtool是开发工具选项,用来指定如何生成sourcemap文件,cheap-module-eval-source-map此款soucemap文件性价比最高
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
// webpack服务器配置
devServer: {
// 重新加载server时,控制台对一些错误以warning的方式提示
clientLogLevel: 'warning',
// 当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
// 是否启用webpack的模块热替换特性。这个功能主要是用于开发过程中,对生产环境无帮助。效果上就是界面无刷新更新。
hot: true,
// 告诉服务器从哪里提供内容。只有在你想要提供静态文件时才需要,这里我们禁用
contentBase: false, // since we use CopyWebpackPlugin.
// 一切服务都启用gzip压缩
compress: true,
// 指定一个host,默认是localhost。如果有全局host就用全局,否则就用index.js中的设置。
host: HOST || config.dev.host,
// 指定端口
port: PORT || config.dev.port,
// 是否在浏览器开启本dev server,是否自动打开浏览器
open: config.dev.autoOpenBrowser,
// 当有编译器错误时,是否在浏览器中显示全屏覆盖。
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
// 静态内容的路径,此路径下的打包文件可在浏览器中访问
publicPath: config.dev.assetsPublicPath,
// 如果你有单独的后端开发服务器api,并且希望在同域名下发送api请求,那么代理某些URL会很有用。
// 接口代理
proxy: config.dev.proxyTable,
// 启用 quiet 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见
quiet: true, // necessary for FriendlyErrorsPlugin
// webpack 使用文件系统(file system)获取文件改动的通知。在某些情况下,不会正常工作。例如,当使用 Network File System (NFS) 时。Vagrant 也有很多问题。在这些情况下使用轮询。
// 监视文件的选项
watchOptions: {
poll: config.dev.poll, //是否使用轮询
}
},
plugins: [
// DefinePlugin 允许创建一个在编译时可以配置的全局常量。这里生成了一个当前环境的常量
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
// 模块热替换插件,修改模块时不需要刷新页面
new webpack.HotModuleReplacementPlugin(),
// 当开启 HMR 的时候使用该插件会显示模块的相对路径
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
// 在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。这样可以确保输出资源不会包含错误。
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
// 模块HtmlWebpackPlugin
new HtmlWebpackPlugin({
filename: 'index.html', //生成的文件的名称
template: 'index.html', //可以指定模块html文件
inject: true //打包后js文件放在body的最后
}),
// copy custom static assets
// 将static的内容拷贝到开发路径,忽略这个文件夹下“.XX”的文件
// 模块CopyWebpackPlugin 将单个文件或整个文件复制到构建目录
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
// webpack将运行由配置文件导出的函数,并且等待promise返回,便于需要异步地加载所需的配置变量。
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: { // build成功的话会执行者块
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors // 如果出错就执行这块,其实是utils里面配置好的提示信息
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})
'use strict'
const path = require('path')
// utils工具配置文件,主要用来处理css类文件的loader
const utils = require('./utils')
const webpack = require('webpack')
// config目录下的index.js配置文件,主要用来定义了生产和开发环境的相关基础配置
const config = require('../config')
// 下面是webpack的merger插件,主要用来处理配置对象合并的,可以将一个大的配置对象拆分成几个小的,合并,相同的项将覆盖
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
// copy-webpack-plugin使用来复制文件或者文件夹到指定的目录的
const CopyWebpackPlugin = require('copy-webpack-plugin')
// html-webpack-plugin是生成html文件,可以设置模板
const HtmlWebpackPlugin = require('html-webpack-plugin')
// extract-text-webpack-plugin这个插件是用来将bundle中的css等文件产出单独的bundle文件的
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// optimize-css-assets-webpack-plugin插件的作用是压缩css代码的,还能去掉extract-text-webpack-plugin插件抽离文件产生的重复代码,因为同一个css可能在多个模块中出现所以会导致重复代码,换句话说这两个插件是两兄弟
// 详情见(1)
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
// 压缩js 的插件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('../config/prod.env')
// 把当前的配置对象和基础的配置对象合并
const webpackConfig = merge(baseWebpackConfig, {
module: {
// 下面就是把utils配置好的处理各种css类型的配置拿过来,和dev设置一样,就是这里多了个extract: true,此项是自定义项,设置为true表示,生成独立的文件
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
// devtool开发工具,用来生成个sourcemap方便调试
// 按理说这里不用生成sourcemap多次一举,这里生成了source-map类型的map文件,只用于生产环境
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
// 打包后的文件放在dist目录里面
path: config.build.assetsRoot,
// 文件名称使用 static/js/[name].[chunkhash].js, 其中name就是main,chunkhash就是模块的hash值,用于浏览器缓存的
filename: utils.assetsPath('js/[name].[chunkhash].js'),
// chunkFilename是非入口模块文件,也就是说filename文件中引用了chunckFilename
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
// 下面是利用DefinePlugin插件,定义process.env环境变量为env
new webpack.DefinePlugin({
'process.env': env
}),
// UglifyJsPlugin插件是专门用来压缩js文件的
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false // 禁止压缩时候的警告信息,给用户一种vue高大上没有错误的感觉
}
},
// 压缩后生成map文件
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
// 生成独立的css文件,下面是生成独立css文件的名称
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
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
// 在dist目录生成html文件
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
// 将js文件放到body标签的结尾
inject: true,
minify: {
// 压缩产出后的html页面
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
// 分类要插到html页面的模块
chunksSortMode: 'dependency'
}),
// keep module.id stable when vendor modules does not change
// 该插件会根据模块的相对路径生成一个四位数的hash作为模块id, 建议用于生产环境
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
// 去 webpack 打包时的一个取舍是将 bundle 中各个模块单独打包成闭包。这些打包函数使你的 JavaScript 在浏览器中处理的更慢。相比之下,一些工具像 Closure Compiler 和 RollupJS 可以提升(hoist)或者预编译所有模块到一个闭包中,提升你的代码在浏览器中的执行速度。
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
// 将第三方的包分离出来
new webpack.optimize.CommonsChunkPlugin({
// common 模块的名称
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
// 将所有依赖于node_modules下面文件打包到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
// 为了避免每次更改项目代码时导致venderchunk的chunkHash改变,我们还会单独生成一个manifestchunk
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
// 主要逻辑的js文件
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets
// 拷贝资源
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
// 开启Gzi压缩打包后的文件
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp( // 这里是把js和css文件压缩
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
// 打包编译后的文件打印出详细的文件信息,vue-cli默认把这个禁用了,个人觉得还是有点用的,可以自行配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
// optimize-css-assets-webpack-plugin插件
// 在生产环境中使用extract-text-webpack-plugin,最好也使用这个插件
// 使用方法如下
// 安装 npm install --save-dev optimize-css-assets-webpack-plugin
// 还要安装 cssnano 这是一个css编译器 npm install --save-dev cssnano 这个vue-cli脚手架并没有使用cssnano
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.optimize\.css$/g, // 不写默认是/\.css$/g
cssProcessor: require('cssnano'), // 编译器选项,不写默认是cssnano,所以使用这个插件不管怎样都要cssnano
cssProcessorOptions: { discardComments: {removeAll: true } }, // 传递给编译器的参数
canPrint: true // 是否能够输出信息
})