下载资源文件、相关配置

基础配置

将支持以下功能:

  • 分离开发环境、生产环境配置
  • 模块化开发
  • sourceMap 定位警告和错误
  • 动态生成引入 bundle.js 的 HTML5 文件
  • 实时编译
  • 封装编译、打包命令
  • 加载图片、字体、CSS、SCSS、PostCSS
  • React+TypeScript

1.新建项目

1
2
3
4
5
6
7
mkdir REACT-CLI
cd REACT-CLI
// 初始化项目
npm init -y
mkdir src
cd src
code index.js

2.安装 webpack

1
yarn add webpack webpack-cli -D

目录结构如下:

1
2
3
4
5
- node_modules
- src
- index.js
- package.json

3.新建 webpack 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// 创建 config 目录
mkdir config

// 进入 config 目录
cd ./config

// Windows环境vscode创建通用环境配置文件
code webpack.common.js

// 创建开发环境配置文件
code webpack.dev.js

// 创建生产环境配置文件
code webpack.prod.js

4.使用 webpack-marge 合并通用配置和特定环境配置

1
yarn add webpack-merge -D

通用环境配置::

1
2
// webpack.common.js
module.exports = {};

开发环境配置:

1
2
3
4
5
6
// webpack.dev.js

const { merge } = require("webpack-merge");
const common = require("./webpack.common");

module.exports = merge(common, {}); // 暂不添加配置

生产环境配置:

1
2
3
4
5
6
// webpack.prod.js

const { merge } = require("webpack-merge");
const common = require("./webpack.common");

module.exports = merge(common, {}); // 暂不添加配置

项目结构如下:

1
2
3
4
5
6
7
8
9

- config
- webpack.common.js
- webpack.dev.js
- webpack.prod.js
- node_modules
- src
- index.js
- package.json

入口 entry

修改 webpack.commom.js:

1
2
3
4
5
6
7
module.exports = {
// 入口
entry: {
index: './src/index.js',
},
}

输出(output)

output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。
生产环境的 output 需要通过 contenthash 值来区分版本和变动,可达到清缓存的效果,而本地环境为了构建效率,则不引入contenthash。

config目录下新增 paths.js,封装路径方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const fs = require('fs')
const path = require('path')

const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

module.exports = {
resolveApp,
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appSrc: resolveApp('src'),
appDist: resolveApp('dist'),
appTsConfig: resolveApp('tsconfig.json')
}

修改开发环境配置文件 webpack.dev.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const paths = require('./paths')
module.exports = merge(common, {
// 输出
output: {
// bundle 文件名称
filename: '[name].bundle.js',

// bundle 文件路径
path: paths.appDist,

// 编译前清除目录
clean: true
},
})

修改生产环境配置文件 webpack.prod.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const paths = require('./paths')
module.exports = merge(common, {
// 输出
output: {
// bundle 文件名称 【只有这里和开发环境不一样】
filename: '[name].[contenthash].bundle.js',

// bundle 文件路径
path: paths.appDist,

// 编译前清除目录
clean: true
},
})

  • [name] - chunk name(例如 [name].js -> app.js)。如果 chunk 没有名称,则会使用其 id 作为名称
  • [contenthash] - 输出文件内容的 md4-hash(例如 [contenthash].js -> 4ea6ff1de66c537eb9b2.js

模式mode

通过 mode 配置选项,告知 webpack 使用相应模式的内置优化。

  • development

会将 DefinePluginprocess.env.NODE_ENV 的值设置为 development。为模块和 chunk 启用有效的名称。

  • production

会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。为模块和 chunk 启用确定性的混淆名称,FlagDependencyUsagePluginFlagIncludedChunksPluginModuleConcatenationPluginNoEmitOnErrorsPlugin 和 TerserPlugin 。

修改开发环境配置文件 webpack.dev.js:

1
2
3
4
5
6
module.exports =  merge(common, {
// 开发模式
mode: 'development',

...
})

修改生产环境配置文件 webpack.prod.js:

1
2
3
4
5
6
7

module.exports = merge(common, {
// 生产模式
mode: 'production',

...
})

Source Map

开发时,webpack 将我们写的代码经过编译后,很难追踪到 error 和 warning 在源代码中的原始位置。source map可以将编译后的代码映射回原始源代码。

修改开发环境配置文件 webpack.dev.js:

1
2
3
4
5
module.exports =  merge(common, {
// 开发工具,开启 source map,编译调试
devtool: 'eval-cheap-module-source-map',
})

source map 有许多 可用选项。本例选择的是 eval-cheap-module-source-map

完成上述配置后,可以通过 npx webpack --config config/webpack.prod.js 打包编译

编译后,会生成这样的目录结构:

打包编译结果

webpack-dev-server

在每次编译代码时,手动运行npx webpack --config config/webpack.prod.js会显得很麻烦,webpack-dev-server 可以帮助我们在代码发生变化后自动编译代码。
安装:

1
yarn add webpack-dev-server  -D

修改开发环境配置文件 webpack.dev.js:

1
2
3
4
5
6
module.exports = merge(common, {
devServer: {
// 告诉服务器从哪里提供内容,只有在你想要提供静态文件时才需要。
static: './dist',
},
})

完成上述配置后,可以通过 npx webpack serve --open --config config/webpack.dev.js 实时编译。

HtmlWebpackPlugin

引入 HtmlWebpackPlugin 插件,生成一个 HTML5 文件,其中会引用打包生成的 bundle 文件。
安装:

1
yarn add  html-webpack-plugin -D

修改通用环境配置文件 webpack.commom.js:

1
2
3
4
5
6
7
8
9
10
module.exports = {
...
plugins: [
// 生成html,自动引入所有bundle
new HtmlWebpackPlugin({
title: 'release_v0',
}),
],
}

重新 webpack 编译 npx webpack --config config/webpack.prod.js,生成的目录结构如下

打包入html

新生成了 index.html,修改了title动态引入了 bundle.js 文件:

1
2
3
4
5
6
7
8
9
10
11
12
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>release_v0</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script defer="defer" src="index.7d02a7c9bcd86981b6f4.bundle.js"></script>
</head>
<body>
</body>
</html>

优化执行命令

上述配置文件完成后,优化 webpack 的实时编译、打包编译指令。

通过 cross-env 配置环境变量,区分开发环境生产环境

安装:

1
yarn add cross-env -D

修改 package.json:

1
2
3
4
"scripts": {
"dev": "cross-env NODE_ENV=development webpack serve --open --config config/webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js"
}

现在可以运行 webpack 指令:

  • npm run dev:本地构建
  • npm run build:生产打包

接下来继续完善配置,用 Webpack 搭建一个 SASS + TS + React 的项目,并且支持:

  • 加载图片
  • 加载字体
  • 加载 CSS
  • 使用 SASS
  • 使用 PostCSS,并自动为 CSS 规则添加前缀,解析最新的 CSS 语法,引入 css-modules 解决全局命名冲突问题
  • 使用 React
  • 使用 TypeScript

加载图片、字体

在 webpack 5 中,有内置的 Asset Modules,不必再引入file-loaderurl-loader处理文件了。

在实际开发过程中,推荐将大图片、字体文件压缩上传至 CDN,提高加载速度。

修改通用环境配置文件 webpack.commom.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const paths = require('./paths');
module.exports = {
...
module: {
rules: [
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
include: paths.appSrc,
type: 'asset/resource',
},
{
test: /.(woff|woff2|eot|ttf|otf)$/i,
include: [
paths.appSrc,
],
type: 'asset/resource',
},
],
},
...
}

加载CSS相关

  • style-loader

style-loader 就是把 CSS 插入到 DOM 中,通过使用多个 <style></style> 自动把 styles 插入到 DOM 中.

  • css-loader

css-loader@importurl() 进行处理,就像 js 解析import/require()一样,让 CSS 也能模块化开发。

  • sass-loader

sass-loader 会加载sass/scss文件并且将他们编译成CSS

  • postcss-loader
    1.可以自动为 CSS 规则添加前缀
    2.将最新的 CSS 语法转换成大多数浏览器都能理解的语法
    3.css-modules 解决全局命名冲突问题

安装相关库和依赖:

1
2
3
yarn add style-loader css-loader -D
yarn add sass-loader sass -D
yarn add postcss-loader postcss postcss-preset-env -D

修改通用环境配置文件 webpack.commom.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
module.exports = {
...
module: {
...
rules: [
{
test: /\.css$/,
include: paths.appSrc,
use: [
// 将 JS 字符串生成为 style 节点
"style-loader",
// 将 CSS 转化成 CommonJS 模块
"css-loader",
],
},
{
test: /\.module\.(scss|sass)$/,
include: paths.appSrc,
use: [
// 将 JS 字符串生成为 style 节点
"style-loader",
// 将 CSS 转化成 CommonJS 模块
{
loader: "css-loader",
options: {
// Enable CSS Modules features
modules: true,
importLoaders: 2,
// 0 => no loaders (default);
// 1 => postcss-loader;
// 2 => postcss-loader, sass-loader
},
},
// 将 PostCSS 编译成 CSS
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
// postcss-preset-env 包含 autoprefixer
"postcss-preset-env",
],
],
},
},
},
// 将 Sass 编译成 CSS
"sass-loader",
],
},
]
}
...
}

JS相关

1
yarn add  babel-loader @babel/core @babel/preset-env -D
  • babel-loader 使用 Babel 加载 ES2015+ 代码并将其转换为 ES5
  • @babel/core Babel 编译的核心包
  • @babel/preset-env Babel 编译的预设,可以理解为 Babel 插件的超集

使用 React + TypeScript

1
2
3
4
yarn add react react-dom @types/react @types/react-dom -D

yarn add typescript esbuild-loader -D
//为提高性能,摒弃了传统的 ts-loader,选择最新的 esbuild-loader。

修改通用环境配置文件 webpack.commom.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module.exports = {
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
module: {
rules: [
{
test: /\.(js|ts|jsx|tsx)$/,
include: paths.appSrc,
use: [
{
loader: 'esbuild-loader',
options: {
loader: 'tsx',
target: 'es2015',
},
}
]
},
]
}
}

为兼容 TypeScript 文件,新增 typescript 配置文件 tsconfig.jsontsconfig相关提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"compilerOptions": {
"outDir": "./dist/",
"noImplicitAny": true,
"module": "es6",
"target": "es5",
"strict": true,//通过启用TypeScript的strict编译器选项,编译器将在最严格的模式下运行,在运行之前捕获更多类型问题。
"jsx": "react",
"allowJs": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,//强制统一不同操作系统的文件名
"importHelpers": true,//通过启用TypeScript的importlpers编译器选项,编译器将使用tslib包,并减少输出的大小
}
}