build文件的配置
# build.js
// 此文件是webpack 的基本项目配置文件
'use strict'
// 版本检查 node 版本信息,版本有要求 "engines": {"node": ">= 4.0.0","npm": ">= 3.0.0"}
// 立即执行
require('./check-versions')()
// process 是 node 中的global全局对象的属性,process 是 node 中的全局变量。env 设置环境变量
process.env.NODE_ENV = 'production'
// ora 是一个命令行转圈圈动画插件,好看用的
const ora = require('ora')
// rimraf 插件是用来执行 UNIX 命令 rm 和 -rf 的用来删除文件夹和文件,清空旧的文件
const rm = require('rimraf')
// node.js 路径模块 连接路径
const path = require('path')
// chalk 插件,用来在命令行中显示彩色文字
const chalk = require('chalk')
// 引入 webpack 模块使用内置插件和 webpack 方法
const webpack = require('webpack')
// 引入 config 目录下的 index.js 配置文件
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
// 开启转圈圈动画
const spinner = ora('building for production...')
spinner.start()
// 调用 rm 方法,第一个参数的结果就是 绝对/工程名/dist/static,表示删除这个路径下面的所有文件
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), (err) => {
// 如果删除的过程中出现错误,就抛出这个错误,同时程序终止
if(err) throw err
// 没有错误,就执行webpack编译
webpack(webpackConfig, function(err, stats) {
// 这个回调函数是webpack编译过程中执行
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')
// 以上就是在编译过程中,持续打印消息
// 下面是编译成功的消息
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'
))
})
})
// end
// 注: 如果你想自己编写一个高质量的脚手架工具,建议你:
// 去补习nodejs,然后补习 es6,然后再来看webpack官方文档,然后自己独立编写一个和vue-cli类似的脚手架,如果上面的东西看不懂,更要这样做
// vue-cli还有一部分内容是关于代码测试的,可以说这块内容的复杂度不亚于webpack,这些内容对nodejs要求比较熟悉,说白了就是基础弱的很难入门,但是测试这块内容也是非常有价值的,可以借助无界面的浏览器解析引擎,通过一句命令就可以把你的代码在不同的平台上运行,还能指出问题所在
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
check-versions.js
Check-versions.js 用来检查版本的js文件
https://www.npmjs.com/package/semver
https://www.npmjs.com/package/shelljs
https://juejin.cn/post/6844903847064764429
// 开启严格模式
'use strict'
// 引入的是一个用来在命令行输出不同颜色文字的模块,通过chalk.yellow("想添加颜色的文字")来实现改变文字颜色的;
const chalk = require('chalk')
// semver 插件也就是语义化版本文件的npm包,是用来判断特定的版本号
const semver = require('semver')
// 导入 package.json 文件,要使用里面的 engines 用来获取版本。
// 要注意 require 是直接可以导入 json 文件的,并且 requrie 返回的就是 json 对象
const packageConfig =require('../package.json')
// shelljs 插件,作用是用来执行 Unix 系统命令
const shell = require('shelljs')
function exec(cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
// 检查 node 版本信息
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version), //返回的是一个版本信息,
versionRequirements: package.config.node //是在package.json中了,指的是>=3.0.0
}
]
// 检查 npm 版本信息
// shell.which(command) 在环境变量PATH中寻找指定命令的地址,判断该命令是否可执行,返回该命令的绝对地址
// exec(command, options, callback)
// 注意:对于长期存在的进程,最好以exec()异步方式运行
if(shell.which('npm'))) { // 就是寻找npm指令
versionRequirments.push({
name: 'npm',
currentVersion:exec('npm --version'), // 自动调用npm --version命令,并且把参数返回给exec函数,从而获取纯净的版本号
versionRequirement: packageConfig.engines.npm // 这是规定的pakage.json中engines选项的node版本信息 "npm": ">= 3.0.0"
})
}
// 输出提示
// 分别检查当前的node和npm版本是否符合当前的package.json里面的定义的,若不符合就提示warning。
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 the following to the modules:'))
console.log()
for(let i = 0; i < warnings.length; i++) {
const warning = warnings[i];
console.log(' ' + warning)
}
console.log()
precess.exit(1)
// 提示用户更新版本
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
utils.js
Utils.js文件的配置主要处理css loader。比如css-loader/less-loader
主要用来处理css-loader和vue-style-loader
extract-text-webpack-plugin插件是用来将文本从bundle中提取到一个单独的文件中
'use strict'
// 引入nodejs路径模块
const path = require('path');
//引入之前的config模块
const config = require('../config')
//引入“extract-text-webpack-plugin”,它的作用是打包后将生成css文件通过link的方式引入到html中,如果不使用这个插件,那么css就打包到<head>的style中
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 引入package.json
const packageConfig = require('../package.json')
//其实,Module.exports才是真正的接口,exports只不过是它的一个辅助工具。 最终返回给调用的是Module.exports而不是exports。所有的exports收集到的属性和方法,都赋值给了Module.exports。当然,这有个前提,就是Module.exports本身不具备任何属性和方法。如果,Module.exports已经具备一些属性和方法,那么exports收集来的信息将被忽略
// 此处导出 assetsPath
exports.assetsPath = function(_path) {
//结合config文件的代码判断静态资源目录名,当环境为生产环境时,assetSubDirectory为“static”,开发环境也是“static”
const assetsSubDirectory = process.env.NODE_ENV === 'production'?
config.dev.assetsSubDirectory : config.build.assetsSubDirectory
// path.posix.join()是path.join的一种兼容性写法,它的作用是路径的拼接,比如path.posix.join("/c/aa',"bb"); // "c/aa/bb"
return path.posix.join(assetsSubDirectory, _path)
}
//用来生成Loader的函数 ,本身可以用在vue-loader的配置上(vue-loader.config.js文件,以后我会提这个文件),同时也是为styleLoader函数使用
exports.cssLoader = function(options) {
// 如果没有传参就默认空对象
options = options || {}
//配置css-loader,css-loader可以让处理css中的@import或者url()
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
//配置postcss-loader,主要功能是补全css中的兼容性前缀,比如“-webkit-”等
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// 生成插件字符串加载器。生成loader的私有方法
function generateLoaders(loader, loaderOptions) {
//参数的usePostCss属性是否为true,是就使用两个loader,否则只使用css-loader
const loaders = options.usePostCSS? [cssLoader, postcssLoader] : [cssLoader];
//给generateLoaders传loader参数的话,比如“less或者sass”,就将这个loader的配置传到loaders数组中
if(loader) {
loaders.push({
loader: loader + '-loader',
// Object.assign()是ES6 的基语法 用于合并对象,实现浅拷贝
options: Object.assign({}, loaderOptions, {
sourceMap : options.sourceMap
})
})
}
// extract 是 定义在options自定义属性
// 当options.extract == true 时候, 就使用extract-text-plugin插件把css文件单独抽离出来成文件
// 否则 就不抽离,直接将css写进style中
// 这个可以在使用的时候单独配置,用来返回最终读取和导入 loader,来处理对应类型的文件
if(options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader',
})
} else {
//vue-style-loader可以理解为vue版的style-loader,它可以将css放进style中
return ['vue-style-loader'].concat(loaders)
}
}
return {
// 返回各种loader
// 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')
}
}
//生成开发环境下loader的配置,使用在(webpack.dev.conf.js中)
// 下面这个主要处理 import 这种方式导入的文件类型的打包,上面的 exports.cssLoaders 是为这一步服务的
exports.styleLoaders = function(options) {
const output = []
// 下面就是生成的各种 css 文件的 loader 对象
const loaders = exports.cssLoaders(options)
for(const extension in loaders) {
// 把每一种loader都提取出来
const loader = loaders[extension]
output.push({
use: loader,
test: new RegExp('\\.' + extension + '&')
})
}
return output
}
// node-notifier 是一个跨平台系统通知的页面,当遇到错误时,它能用系统原生的推送方式推送信息
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return(severity, errors) => {
if(severity !== 'error') return
const error = error[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')
})
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
vue-loader.conf.js
'use strict'
// 引入配置文件
const utils = require('./utils')
const config = require('../config') // 引入config目录下的index.js配置文件
// 根据环境来获取相应的 productionSourceMap 或者 cssSourceMap
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction?
config.build.productionSourceMap:config.dev.cssSourceMap
module.exports = {
// 配置在 .vue 文件中的 css 相关处理规则
// 调用utils配置文件中的cssLoaders方法,用来返回配置好的css-loader和vue-style-loader
loaders:utils.cssLoaders({
sourceMap: sourceMapEnabled, // 这一句话表示如何生成map文
extract: isProduction, // 这一项是自定义配置项,设置为true表示生成单独样式文件
})
// 记录压缩的代码,用来找到源码位置
cssSourceMap: sourceMapEnabled,
// 是否缓存破坏
cacheBusting: config.dev.cacheBusting,
// transformToRequire 的作用是在模块编译的过程中,编译器可以将某些属性,比如 src 转换为 require 调用
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
vue.base.conf.js
此配置文件是vue开发环境的wepack相关配置文件,主要用来处理各种文件的配置
'use strict'
const path = require('path') // 引入nodeJS内置的路径插件
const utils = require('./utils') // 引入当前目录中的utils工具配置文件
const config = require('../config') // 引入config目录中的index.js配置文件
const vueLoaderConfig = require('./vue-loader.conf') // 引入当前目录下的vue-loader.conf.js文件
// 封装resolve函数返回 根路径下的平行目录的文件和文件夹
function resolve(dir) {
return path.join(__dirname, '...', dir)
}
// ????
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.join(__dirname, '../')
// 设置入口文件为src/main.js
entry: {
app: './src/main.js'
},
// 出口文件
output: {
// 路径就是cnfig目录下的index.js文件中build配置的assetsRoots, 也就是dist目录
path: config.build.assetsRoot,
// 文件名称这里使用默认的name也就是main
filename: '[name].js',
// 打包之后的文件的真正引用路径
publicPath: process.env.NODE_ENV === 'production'?
config.build.assetsPublicPath : config.dev.assetsPublicPath
},
// 对模块进行解析
// // resolve是webpack的内置选项,顾名思义,决定要做的事情,也就是说当使用 import "jquery",该如何去执行这件事情就是resolve配置项要做的,import jQuery from "./additional/dist/js/jquery" 这样会很麻烦,可以起个别名简化操作
resolve: {
// 省略扩展名,也就是说.js,.vue,.json文件导入可以省略后缀名,这会覆盖默认的配置,所以要省略扩展名在这里一定要写上
extensions: ['.js','.vue','json'],
alias: {
//精准匹配,使用vue来替代vue/dist/vue.esm.js路径
'vue$': 'vue/dist/vue.esm.js',
//使用@替代src路径,当你引入src下的文件是可以使用import xx from "@/XX.js"
'@': resolve('src'),
}
},
// module 用来解析不同的模块
module: {
rules: {
{
test: /\.(js|vue)$/,
// 也就是说,对.js和.vue文件在编译之前进行检测,检查有没有语法错误
loader: 'eslint-loader',
// 此选项指定enforce: 'pre'选项可以确保,eslint插件能够在编译之前检测,如果不添加此项,就要把这个配置项放到末尾,确保第一个执行
enforce: 'pre',
// include选项指明这些目录下的文件要被eslint-loader检测,还有一个exclude表示排除某些文件夹
include: [resolve('src'), resolve('test')],
// options表示传递给eslint-loader的参数
options: {
// formatter是参数的名称,eslint-friendly-formatter是eslint的一个报告总结插件,也就是说eslint的检测报告非常难看懂,这个插件就是整理这些报告方便查阅的
formatter: require('eslint-friendly-formatter')
}
},
{
test: /\.vue$/,
// 对vue文件使用vue-loader,该loader是vue单文件组件的实现核心,专门用来解析.vue文件的
loader: 'vue-loader',
// 将vueLoaderConfig当做参数传递给vue-loader,就可以解析文件中的css相关文件
options: vueLoaderConfig
},
{
test: /\.js$/,
// 对js文件使用babel-loader转码,该插件是用来解析es6等代码
loader: 'babel-loader',
// 指明src和test目录下的js文件要使用该loader
include: [resolve('src'), resolve('test')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
// 对图片相关的文件使用 url-loader 插件,这个插件的作用是将一个足够小的文件生成一个64位的DataURL
// 可能有些老铁还不知道 DataURL 是啥,当一个图片足够小,为了避免单独请求可以把图片的二进制代码变成64位的
// DataURL,使用src加载,也就是把图片当成一串代码,避免请求,神不神奇??
loader: 'url-loader',
options: {
// 限制 10000 个字节以下的图片才使用DataURL
limit: 10000,
// 下面这个应该是指将[name].[hash:7].[ext]对应的图片使用url-loader测试
name: utils.assetsPath('img/[name].[hash:7].[ext]') // 这个函数执行结果是 /img/[name].[hash:7].[ext]
// 不知道把 name 设置成 /img/[name].[hash:7].[ext] 意欲何为,猜测应该是输出图片的路径或者是解析图片的路径
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
// 字体文件处理,和上面一样
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
vue.dev.conf.js
此配置文件是vue开发环境的wepack相关配置文件
https://github.com/ampedandwired/html-webpack-plugin
'use strict'
// 引入配置文件
const utils = require('./utils') // 引入当前目录中的utils工具配置文件
const webpack = require('webpack') // 引入webpack来使用webpack内置插件
const config = require('../config') // 引入config目录中的index.js配置文件
const merge = require('webpack-merge') // 引入webpack-merge插件用来合并webpack配置对象,也就是说可以把webpack配置文件拆分成几个小的模块,然后合并
const path = require('path') // 引入nodeJS内置的路径模块
const baseWebpackConfig = require('./webpack.base.conf') // 引入当前目录下的webpack.base.conf.js配置文件,主要配置的是打包各种文件类型的配置
// 拷贝资源插件
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 下面是一个自动生成html的插件,能够把资源自动加载到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)
// add hot-reload related code to entry chunks
// Object.keys(obj) 是取对象的索引,并放在一个数组里面的方法
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
// 下面这个结果就是把webpack.base.conf.js中的入口entry改成如下配置
// app: ['./build/dev-client','./src/main.js']
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
const devWebpackConfig = merge(baseWebpackConfig, {
moudle: {
// 下面是把utils配置中的处理css类似文件的处理方法拿过来,并且不生成cssMap文件
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', // '#cheap-module-eval-source-map'
// 此配置项应该写在 ./config/index.js
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,
//是否压缩
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 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: { //监视文件的选项
poll: config.dev.poll,
}
},
plugins: [
// DefinePlugin内置webpack插件,专门用来定义全局变量的,下面定义一个全局变量 process.env 并且值是如下
/* 'process.env': {
NODE_ENV: '"development"'
} 这样的形式会被自动转为
'process.env': '"development"'
各位骚年看好了,development如果不加双引号就当做变量处理,程序会报错
*/
new webpack.DefinePlugin({
'process.env': require('../config/dev.env') //'development'
}),
// 重点
// 模块热替换插件,修改模块时不需要刷新页面
new webpack.HotModuleReplacementPlugin(),
// 当开启 HMR 的时候使用该插件会显示模块的相对路径
new webpack.NamedModulesPlugin(),
// 在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。这样可以确保输出资源不会包含错误。
new webpack.NoEmitOnErrorsPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true //设置为true表示把所有的js文件都放在body标签的后面
}),
new FriendlyErrorsPlugin(),
]
}
})
// 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: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})
/*
1. html-webpack-plugin插件是用来生成html文件的,有很灵活的配置项,下面是基本的一些用法
plugins: [
new HtmlWebpackPlugin(), // Generates default index.html
new HtmlWebpackPlugin({ // Also generate a test.html
filename: 'test.html', // 生成的文件的名称
title: 'Custom template', // 文件的标题
template: 'my-index.ejs' //可以指定模块html文件
})
]
2. friendly-errors-webpack-plugin插件,把webpack编译出来的错误展示给我们,方便调试
安装 npm install friendly-errors-webpack-plugin --save-dev
基本使用
plugins: [
new FriendlyErrorsWebpackPlugin(),
]
3. HotModuleReplacementPlugin解释如下
老哥们知道什么是webpack模块不
webpack支持如下模块
CoffeeScript
TypeScript
ESNext (Babel)
Sass
Less
Stylus
这些模块支持如下导入的方式
ES2015 import 语句
CommonJS require() 语句
AMD define 和 require 语句
css/sass/less 文件中的 @import 语句。
样式(url(...))或 HTML 文件(<img src=...>)中的图片链接(image url)
看到了把,webpack就是这么强大,几乎囊括了前端所有的东西
这个插件的作用就是当你的程序在运行时,而你现在要替换、添加或删除某个模块,又不想重新加载页面,
这个插件帮助你实现无刷新加载,关于内部实现原理,其实有点意思,各位请自行查看
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
vue.prod.conf.js ???
这个配置文件是webpack生产环境的核心配置文件
http://vuejs.github.io/vue-loader/en/workflow/production.html
https://github.com/ampedandwired/html-webpack-plugin
https://github.com/kangax/html-minifier#options-quick-reference
'use strict'
// 引入配置文件
const path = require('path') //引入nodejs的路径模块
const utils = require('./utils') // utils工具配置文件,主要用来处理css类文件的loader
const webpack = require('webpack') //引入webpack,来使用webpack内置插件
//config目录下的index.js配置文件,主要用来定义了生产和开发环境的相关基础配置
const config = require('../config')
//webpack的merger插件,主要用来处理配置对象合并的,可以将一个大的配置对象拆分成几个小的合并,相同的项将覆盖
const merge = require('webpack-merge')
//webpack.base.conf.js配置文件,我其他博客文章已经解释过了,用来处理不同类型文件的loader
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 用来抽离 css 防止 css打包压缩到 js 中,这个插件是用来将bundle中的css等文件产出单独的bundle文件的
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 导入 optimize-css-assets-webpack-plugin插件的作用是压缩css代码的,还能去掉extract-text-webpack-plugin插件抽离文件产生的重复代码,因为同一个css可能在多个模块中出现所以会导致重复代码,换句话说这两个插件是两兄弟, 用来压缩单独的 css 文件
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
// 导入 uglifyjs-webpack-plugin 用来压缩js文件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
// 如果当前环境变量NODE_ENV的值是testing,则导入 test.env.js配置文,设置env为"testing"
// 如果当前环境变量NODE_ENV的值不是testing,则设置env为"production"
var env = process.env.NODE_ENV === 'testing'
? require('../config/test.env')
: require('../config/prod.env')
// 把当前的配置对象和基础的配置对象合并
var 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 ? '#source-map' : 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: [
// 下面是利用DefinePlugin插件,定义process.env环境变量为env
new webpack.DefinePlugin({
'process.env': env
})
new webpack.optimize.UglifyJsPlugin({
// UglifyJsPlugin插件是专门用来压缩js文件的
compress: {
warnings: false // 禁止压缩时候的警告信息,给用户一种vue高大上没有错误的感觉
},
// 压缩后生成map文件
sourceMap: true
}),
// extract css into its own file
new ExtractTextPlugin({
// 生成独立的css文件,下面是生成独立css文件的名称
filename: utils.assetsPath('css/[name].[contenthash].css')
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
// 压缩css文件
cssProcessorOptions: {
safe: true
}
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// 生成html页面
new HtmlWebpackPlugin({
//非测试环境生成index.html
filename: process.env.NODE_ENV === 'testing'
? 'index.html'
: config.build.index,
// 模板是index.html加不加无所谓
template: 'index.html',
// 将js文件放到body标签的结尾
inject: true,
minify: {
// 压缩产出后的html页面
removeComments: true, // 去掉注释
collapseWhitespace: true, // 去掉空格
removeAttributeQuotes: true // 去掉属性的引号
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
// 分类要插到html页面的模块
chunksSortMode: 'dependency'
}),
// split vendor js into its own file
// 下面的插件是将打包后的文件中的第三方库文件抽取出来,便于浏览器缓存,提高程序的运行速度
new webpack.optimize.CommonsChunkPlugin({
// common 模块的名称
name: 'vendor',
minChunks: function (module, count) {
// 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
)
}
}),
// 把webpack的runtime代码和module manifest代码提取到manifest文件中,防止修改了代码但是没有修改第三方库文件导致第三方库文件也打包的问题
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
// chunks: ['vendor']
chunk: Ininity,
}),
// 抽取第三方库到 app.js
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// 下面是复制文件的插件,我认为在这里并不是起到复制文件的作用,而是过滤掉打包过程中产生的以.开头的文件
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
// 压缩
if (config.build.productionGzip) {
// 开启Gzi压缩打包后的文件,老铁们知道这个为什么还能压缩吗??,就跟你打包压缩包一样,把这个压缩包给浏览器,浏览器自动解压的
// 你要知道,vue-cli默认将这个神奇的功能禁用掉的,理由是Surge 和 Netlify 静态主机默认帮你把上传的文件gzip了
var 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默认把这个禁用了,个人觉得还是有点用的,可以自行配置
var 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,但是这个插件的官方说要安装cssnano,这是不是一个bug??
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.optimize\.css$/g, // 不写默认是/\.css$/g
cssProcessor: require('cssnano'), // 编译器选项,不写默认是cssnano,所以使用这个插件不管怎样都要cssnano
cssProcessorOptions: { discardComments: {removeAll: true } }, // 传递给编译器的参数
canPrint: true // 是否能够输出信息
})
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
编辑 (opens new window)