Webpack 入门 4

天天 file 协议,我受够了,来个 server!Webpack 其之四


点我下载本章代码

webpack-dev-server

查看 webpack 官方文档,发现有一个东西叫做 webpack-dev-server,而且也就它带一个 server 单词,试试看!

先安装它

1
npm install --save-dev webpack-dev-server

然后照抄一下官方给的配置,发现是在 module.exports 下新增以下字段

1
2
3
4
5
6
module.exports = {
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
}
}

以及在 node 配置中新增以下字段

1
"start": "webpack-dev-server --open"

意思是当运行 npm run start 的时候,执行其值对应的命令行,其中 --open 的意思是自动打开默认浏览器

抄进我们的配置,start 一下

1
npm run start

发现报错了……

1
Error: Cannot find module 'webpack-cli/bin/config-yargs'

搜索发现,是默认安装的 webpack-cli 版本太高了,以至于和 webpack-dev-server 不兼容

这时候需要重装 webpack-cli 为合适的版本

依次运行下述命令可以重装 webpack-cli 为 3 版本

1
2
npm uninstall webpack-cli
npm install webpack-cli@3 --save-dev

现在再试试 start,发现成功打开了浏览器!

此时坐标 localhost:8080,默认根路径 / 就是我们指定的输出 index.html 的位置

尝试修改一下 style,比如背景色改成 blue

发现随着你在本地保存,浏览器里的页面也会自动更新

不只是 http 协议,webpack-dev-server 也支持热更新!

不同的配置

但是这个 server 再怎么 server,终究还是个 dev,它在编译的时候就应该使用 development mode 而不是 production

让我们复制一下原来的 config,保存成两份

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
// webpack.config.js 默认使用 development 模式
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js'
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
},
plugins: [
new HtmlWebpackPlugin({
title: 'My App',
filename: 'index.html',
template: 'index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
// use: [
// 'style-loader',
// 'css-loader'
// ]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
}
};
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
// webpack.config.prod.js 使用 production 模式
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js'
},
plugins: [
new HtmlWebpackPlugin({
title: 'My App',
filename: 'index.html',
template: 'index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
}
};

这时候,我们直接 npm run build 的话,会使用默认的 development 模式的配置,怎么办呢

答案是修改 node 脚本配置,其中 build 字段改为如下值

1
"build": "rimraf ./dist && webpack --config ./webpack.config.prod.js"

通过 –config 来指示此时使用哪个配置文件进行构建

现在再次 npm run build,也不会用错配置文件了!

管理配置

分析

配置分开后,我们又发现了新的问题:配置内容重复不少

重复可是编程一大弊病,必须要避免,容易想到抽出一个公共的配置

现在让我们把目录变成这样

  • webpack.config.js,保存公共配置
  • webpack.config.dev.js,保存开发配置
  • webpack.config.prod.js,保存部署配置

然后修改 node 脚本的 start 字段

1
"start": "webpack-dev-server --config ./webpack.config.dev.js --open"

接下来提取刚才两个配置文件中的公共部分

分离

显然除了模式、dev-server 有关配置外,其余部分基本一致

那么把共有部分提取一下

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
// webpack.config.js 共有部分配置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js'
},
plugins: [
new HtmlWebpackPlugin({
title: 'My App',
filename: 'index.html',
template: 'index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
// use: [
// 'style-loader',
// 'css-loader'
// ]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
}
};
1
2
3
4
5
6
7
8
// webpack.config.dev.js 使用 development 模式
module.exports = {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
}
};
1
2
3
4
// webpack.config.prod.js 使用 production 模式
module.exports = {
mode: 'production'
};

现在怎么引入呢

引入

既然结尾是 js,那这个配置就是按照 js 的引入办法,通过 require 引入

插播一下,在 webpack 配置这边,遵循的是 AMD 规范,使用 require

而平时的 node 代码是遵循 CommonJS 规范,使用 import

1
2
3
4
5
6
7
8
9
10
11
// webpack.config.dev.js 使用 development 模式
const base = require('./webpack.config.js')

module.exports = {
...base,
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
}
};
1
2
3
4
5
6
7
// webpack.config.prod.js 使用 production 模式
const base = require('./webpack.config.js')

module.exports = {
...base,
mode: 'production'
};

此处使用 spread 语法展开引入的 base,base 就是 require 的模块里的 module.exports 的值

现在就可以使得两个不同 mode 的配置,都继承同样的公共配置了

但是注意,现在不能随便覆写字段,比如我要在 dev mode 新增一个 loader 处理 scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const base = require('./webpack.config.js')

module.exports = {
...base,
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
},
module: {
rules: [
{
test: /\.scss$/,
use: [{
loader: "style-loader" // 将 JS 字符串生成为 style 节点
}, {
loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
}, {
loader: "sass-loader" // 将 Sass 编译成 CSS
}]
}
]
}
};

如果这样写,会导致覆写了从 base 引入的 loader,让 css loader 和 file loader 都消失,只有 sass loader

所以要引入新的专有 loader 的话,还要再应用一次 spread 语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module: {
...base.module,
rules: [
...base.module.rules,
{
test: /\.scss$/,
use: [{
loader: "style-loader" // 将 JS 字符串生成为 style 节点
}, {
loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
}, {
loader: "sass-loader" // 将 Sass 编译成 CSS
}]
}
]
}

只有这样才可以保证不会覆写掉共有部分引入的 loader

其余部分的覆写也要注意这一点

合并

手动引入还要注意使用 spread 语法以及避免错误覆写,有没有更方便的呢?有

搜索 webpack merge,找到其 github 项目主页

发现要安装,那就安装一下

1
npm install webpack-merge --save-dev

刚才 dev 的文件已经用了,现在就用 prod 的文件吧

先引入 merge

1
const { merge } = require('webpack-merge')

注意此处要使用解构赋值,否则引入后还要取模块的 merge 字段才行

然后连接 base 和新的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = merge(base, {
mode: 'production',
module: {
rules: [{
test: /\.scss$/,
use: [{
loader: "style-loader" // 将 JS 字符串生成为 style 节点
}, {
loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
}, {
loader: "sass-loader" // 将 Sass 编译成 CSS
}]
}]
}
});

ok!最终效果和使用 spread 语法手动展开是一样的

merge 这个方法起如下的效果

1
2
3
4
5
6
7
8
9
10
11
const a = {
a: [11],
b: 45,
c: 1919
}
const b = {
a: [514],
b: 114514,
d: 810
}
const result = merge(a,b)

最终 result 结果如下

1
2
3
4
5
6
result === {
a: [114,514],
b: 114514,
c: 1919,
d: 810
}

数组取合并,相同取新值,不同均保留

就是这样~~

感言

Webpack 入门系列就到这里了,有兴趣深入的同学,可以查看 webpack 官方文档以及各个插件的官方文档

学好这几章提及的 webpack 基本原理,对于深入理解 vue 和 react 框架的打包机制是很有必要的(正是基于 webpack)

勘误可以联系我,谢谢!


感谢阅读

--It's the end.Thanks for your read.--