从零开始的Webpack + React项目搭建 Mar 2nd 2020 Words: 1.3k

废话

最近因为有项目需要,开始学习React。第一步是工程环境的搭建,这肯定要用到当下最火的Webpack(才不是装比)。因为是初学,为了更好的理解配置(给自己找事),我没有用脚手架。
Youtube上有一个不错的教学视频,我基本上就是跟着教程照葫芦画瓢。
水一篇文章记录过程,免得过段时间忘了。

安装Webpack

创建Node package:

1
yarn init

private回答“是”以免package被发布。

安装Webpack:

1
yarn add -D webpack webpack-cli

修改package.json,添加为webpack打包命令添加入口:

1
2
3
"scripts": {
"build": "webpack --mode production"
}

新建./src/index.js,这是webpack默认的入口文件。

测试
可以先在index.js里写个HelloWorld:

1
console.log("Hello Webpack");

再运行

1
yarn build

应当产出了./dist/main.js,Webpack安装完成。

安装开发服务器

如果每次修改代码后都要编译部署才能看到效果,效率太感人了。我们需要一个一键运行的开发服务器,并且所有修改都应该立即展现。

1
yarn add -D webpack-dev-server

修改package.json

1
2
3
4
"scripts": {
"start": "webpack-dev-server --mode development --open",
"build": "webpack --mode production"
}

以后用yarn start就能启动开发服务器了,默认端口8080。

安装React

添加React依赖:

1
yarn add -D react react-dom

现在JS里面可以导入了:

1
import React from "react";

其它第三方库的安装以此类推。

添加Babel

为了让过气浏览器也能运行ES6,用babel编译代码。

添加Babel依赖:

1
yarn add -D @babel/core babel-loader @babel/preset-env

修改package.json

1
2
3
4
"scripts": {
"start": "webpack-dev-server --mode development --open",
"build": "webpack --mode production --module-bind js=babel-loader"
}

Babel添加React支持:

1
yarn add -D @babel/preset-react

新建.babelrc

1
2
3
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

新建webpack.config.js,添加规则把js和jsx文件交给babel-loader处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
};

使用Hash区分文件版本

浏览器缓存/CDN缓存的同步很蛋疼,JS、CSS等文件名添加hash就可以确保index所引用的文件版本正确。index.html的缓存时长可以调得相对小一些。

修改webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const isDevelopment = process.env.NODE_ENV === 'development';

module.exports = {
module: {
entry: {
main: "./src/index.js"
},
output: {
filename: isDevelopment? "[name].js" : "[name].[contentHash].js"
},
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader"
}
]
}
]
}
};

这里为了省事用isDevelopment变量来区分配置,但推荐的做法是使用webpack-merge,并为不同环境分别建立配置文件。

添加HTML支持

新建./src/index.html

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello Webpack</title>
</head>
<body>
</body>
</html>

添加依赖:

1
yarn add -D html-webpack-plugin html-loader

修改webpack.config.js

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
const HtmlWebPackPlugin = require("html-webpack-plugin");

const isDevelopment = process.env.NODE_ENV === 'development';

module.exports = {
entry: {
main: "./src/index.js"
},
output: {
filename: isDevelopment? "[name].js" : "[name].[contentHash].js"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader"
}
]
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: {
minimize: true
}
}
]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html", //默认模板文件
filename: "./index.html" //产出的文件名
})
]
};

添加SASS/SCSS支持

安装相关loader和plugin:

1
yarn add -D node-sass sass-loader style-loader css-loader mini-css-extract-plugin

修改webpack.config.js

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
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const isDevelopment = process.env.NODE_ENV === 'development';

module.exports = {
entry: {
main: "./src/index.js"
},
output: {
filename: isDevelopment? "[name].js" : "[name].[contentHash].js"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader"
}
]
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: {
minimize: true
}
}
]
},
{
test: /\.module\.s[ac]ss$/,
use: [
isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: isDevelopment
}
},
{
loader: 'sass-loader',
options: {
sourceMap: isDevelopment
}
}
]
},
{
test: /\.s[ac]ss$/,
exclude: /\.module.s([ac]ss)$/,
use: [
isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: false,
sourceMap: isDevelopment
}
},
{
loader: 'sass-loader',
options: {
sourceMap: isDevelopment
}
}
]
}
]
},
resolve: {
extensions: ['.js', '.jsx', '.scss']
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
new MiniCssExtractPlugin({
filename: isDevelopment ? '[name].css' : '[name].[hash].css',
chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css'
})
]
};

use参数的数组执行顺序是从后向前。style-loader会将CSS注入DOM,而mini-css-extract-plugin会将所有CSS打包成一个独立文件,并在index.html的head中引用。

添加静态资源支持

添加依赖:

1
yarn add -D file-loader

编辑webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
rules: [
//...
{
test: /\.(svg|png|jpg|jpeg|webp|bmp|gif)$/,
use: [
{
loader: "file-loader",
options: {
name: isDevelopment ? "[name].[ext]" : "[name].[hash].[ext]",
outputPath: "assets/img" //所有图片打包到 ./assets/img 目录
}
}
]
},
//...
]

自动清理编译目录

添加依赖:

1
yarn add -D clean-webpack-plugin

编辑webpack.config.js

1
2
3
4
5
6
7
8
9
const {CleanWebpackPlugin} = require('clean-webpack-plugin');

//...

plugins: [
new CleanWebpackPlugin(),
//...
]
//...

参考资料

EOF