webpack 搭建开发环境: React、TSX、Antd
目录
webpack 是一个模块打包工具,它引入各种模块(包括但不限于 js),经过依赖关系处理、优化和压缩后,能生成特定平台运行的代码包(兼容浏览器的代码)。
本实验从 0 开始配置 webpack,搭建一个可用的 React+TSX+Antd 界面应用。
首先用 yarn 初始化一个项目。
yarn init
webpack CLI
webpack CLI 提供核心的打包功能,它会读取当前工作目录的配置文件开始打包的工作。
webpack 可以读取多种格式的配置文件,在这里我推荐用 TypeScript 编写配置文件,可以提供代码提示,减少出错。
安装 webpack
- webpack 打包工具 webpack 的核心包
- webpack-cli 打包工具 webpack CLI
- ts-node 类似 node,但可以直接运行
.ts
代码 - typescript TS 编译器
- @types/node nodejs 类型声明,提供 nodejs 代码提示
- @types/webpack webpack 类型声明,提供 webpack 配置代码提示
yarn add -D webpack webpack-cli ts-node typescript @types/node @types/webpack
安装完成后,初始化 TS,并新建配置文件 webpack.config.ts
yarn tsc --init
Entry: 配置入口
入口文件 Entry 类似于 go、c 中带 main()
的文件,执行 entry 应该启动运行整个项目。
在项目根目录新建入口文件 index.entry.js
:
window.onload = () => console.log('hello')
编写文件 webpack.config.ts
配置 webpack:
- clean 每次打包前清理上次结果,这里的输出结果存放在
dist
import path from 'path'
import webpack from 'webpack'
const config: webpack.Configuration = {
entry: {
index: { import: './index.entry.js' },
},
output: {
path: path.resolve(__dirname, 'dist'),
clean: true,
},
}
export default config
执行 yarn webpack -c webpack.config.ts
后,可以在 dist
看到打包结果。
Loader: 引入其他格式的代码
webpack 本身只支持解析 js,为了能读取并引入 .ts
、.tsx
、.css
的代码,webpack 提供了 Loader 的概念。
配置 Loader 需要告诉 webpack 读取哪些文件(以正则判断,哪些文件用哪些 Loader)
加载 CSS 样式模块
- style-loader 可以处理样式,然后把css写到js里面,用js处理css。不会解析任何样式文件。
- mini-css-extract-plugin 可以处理样式,然后直接输出到指定的css文件。不会解析任何样式文件。
- css-loader 可以引入并解析
.css
模块文件。
mini-css-extract-plugin 可以把样式单独存放成一个文件,推荐使用。
style-loader 把CSS以字符串形式写到JS里面,再调用 CSS API 动态添加样式。
yarn add -D mini-css-extract-plugin css-loader
配置文件 webpack.config.ts
添加 CSS Loader:
+import MiniCssExtractPlugin from 'mini-css-extract-plugin'
const config: webpack.Configuration = {
entry: {...},
output: {...},
+ module: {
+ rules: [
+ {
+ test: /\.css$/i,
+ use: [{ loader: MiniCssExtractPlugin.loader }, { loader: 'css-loader' }],
+ },
+ ],
+ },
}
加载 JS(X)、TS(X) 模块
JS 的代码格式受到浏览器的影响,如 箭头函数 在一些浏览器无法运行。
() => console.log();
Babel 就是用来解决这个问题的,它可以把这个箭头函数编译成:
function() { console.log() }
Babel 支持插件扩展和整合包,一般开发环境可以直接引入 preset(预设、整合包),需要一些扩展的JS功能时,可以配置插件。
- babel-loader 给 webpack 专用的 Babel Loader。
- @babel/core Babel 核心包
- @babel/preset-env Babel 整合包,包含常用的兼容性插件,Babel 项目基本都装。
- @babel/preset-typescript Babel TS整合包,能让 Babel 解析 TS 文件。
- @babel/preset-react Babel React整合包,能让 Babel 解析 React 组件。
yarn add -D babel-loader @babel/core @babel/preset-env @babel/preset-typescript @babel/preset-react
安装完毕后,根目录新建文件 babel.config.js
,Babel 会在运行时读取。
module.exports = {
presets: [
"@babel/preset-env",
"@babel/preset-typescript",
"@babel/preset-react"
]
}
配置文件 webpack.config.ts
添加 Babel Loader:
Babel Loader 需要 exclude
去掉 node_modules
第三方包的代码,因为第三方包在分发时,js 代码就已经是编译过的,不需要再用 Babel 再次编译,否则会拖慢编译速度。
const config: webpack.Configuration = {
entry: {...},
output: {...},
module: {
rules: [
{ test: /\.css$/i, ... },
+ {
+ test: /\.(m?js|jsx?|tsx?)$/,
+ exclude: /node_modules/,
+ use: { loader: 'babel-loader' },
+ },
],
},
}
加载图片、文档资源
webpack 5 起开箱自带资源管理,如引入图片,会在打包结果附上 URL。
配置文件 webpack.config.ts
:
const config: webpack.Configuration = {
entry: {...},
output: {...},
module: {
rules: [
{ test: /\.css$/i, ... },
{ test: /\.(m?js|jsx?|tsx?)$/, ... },
+ {
+ test: /\.(png|svg|jpg|jpeg|gif)$/i,
+ type: 'asset/resource',
+ },
],
},
}
Plugin: 扩展 webpack 功能
webpack 的官方文档已解释 Plugin 的重要性,插件可以用于实现辅助代码注入、环境变量定义、目标代码压缩等很多的功能。
这里用到的插件如下:
- [自带] ProgressPlugin 让 webpack 编译的时候可以显示进度
- [自带] DefinePlugin 可以注入变量,如注入
ENV="prod"
,则生成目标代码时替换:- console.log(ENV) + console.log("prod")
- [terser-webpack-plugin] TerserPlugin 生成目标代码时,执行 Terser 压缩
- [html-webpack-plugin] HtmlWebpackPlugin 以模板 HTML 生成目标 HTML
- @types/html-webpack-plugin 提供 HtmlWebpackPlugin 配置的代码提示
yarn add -D terser-webpack-plugin html-webpack-plugin @types/html-webpack-plugin
HTML 模板
安装完成后,根目录新建 HTML 模板文件 index.template.html
:
body 这里,预定了一个 div 占位符,下文会描述一段 React 代码,把组件渲染在这个 div 上。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>LuoyzHouseReactApp</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<div id="app"></div>
</body>
</html>
增加 webpack Plugins 配置
配置文件 webpack.config.ts
新增插件配置:
+ import HtmlWebpackPlugin from 'html-webpack-plugin'
const config: webpack.Configuration = {
entry: {...},
output: {...},
module: {...},
+ plugins: [
+ new webpack.ProgressPlugin(),
+ new webpack.DefinePlugin({
+ 'process.env.NODE_ENV': JSON.stringify(
+ process.env.NODE_ENV ?? 'production'
+ ),
+ }),
+ new HtmlWebpackPlugin({
+ template: './index.template.html', // 模板文件
+ filename: '[name].html', // name 等同Entry名称,这里入口为 index,则生成 index.html
+ }),
+ new MiniCssExtractPlugin(), // 除了添加它的 Loader,还要应用它的插件
+ ],
}
现在执行 yarn webpack -c webpack.config.ts
编译打包,可以在 dist/
目录看到模板 HTML 输出,并在 script 标签中引用了入口 Entry 的 JS 代码,且这个 JS 代码也已经编译过了。
devServer: 配置开发服务器
devServer 是 webpack 的热更新服务器,当文件保存时,可以触发重新编译。相比静态命令行编译,它有这些优点:
- 常驻: 常驻端口,浏览器可以直接访问
- 热更新: 代码修改后,会触发部分模块的重新编译
- 及时反应: 重编译后,会通过 websocket 通知浏览器更新页面
综上,借助 devServer 可以把页面和代码模块的改动实时显示出来。
devServer 是 webpack 的一个独立的功能,这里额外安装以下包:
- webpack-dev-server devServer 功能包
- @types/webpack-dev-server devServer 类型声明,提供代码提示
yarn add -D webpack-dev-server @types/webpack-dev-server
配置文件 webpack.config.ts
新增 devServer (wds) 配置:
+import wds from 'webpack-dev-server'
const config: webpack.Configuration = {
entry: {...},
output: {...},
module: {...},
plugins: [...],
+ devServer: {
+ hot: true, // 启用部分模块的热重载后,浏览器不需要每次都刷新页面了
+ port: 9000, // 端口号 9000
+ } as wds.Configuration
}
配置完成,现执行 webpack serve 指令可以启动开发服务器。浏览器打开 localhost:9000
即可看到编译结果。
yarn webpack serve -c webpack.config.ts
编写 React 应用
现在开发环境准备好了,开始编写一个 React 的 web 应用。选型蚂蚁金服的 UI 框架 Ant Design。
yarn add react @types/react antd
编写界面 App.tsx
新建一张图片 src/mineavatar.jpeg
。
新建文件 src/App.module.css
,编写标题样式。
.title {
font-weight: bold;
font-style: italic;
}
新建文件 src/App.tsx
,编写界面代码。
import React, { useState } from 'react'
import { Button, Space, Row } from 'antd'
import mineavatar from './mineavatar.jpeg' // 引入图片模块,解释成一个 URL
import styles from './App.module.less' // 引入样式模块,解释成 { title: string }
const App = () => {
const Greetings = ['Hello', '你好', 'Bonjour', 'Bon不jour了']
const [index, setIndex] = useState(0)
return (
<Row justify="center">
<Space direction="vertical" align="center">
<h1 className={styles['title']}>LuoyzHouse, {Greetings[index % Greetings.length]}</h1>
<img src={mineavatar} />
<br />
<Button onClick={() => setIndex(index + 1)}>打招呼</Button>
</Space>
</Row>
)
}
export default App
定义好了一个 React 组件,现在在入口代码处把组件挂载到 DOM 上。
编写入口代码
把 index.entry.js
改名为 index.entry.tsx
,因为在 Loader 配置阶段,已经让 webpack Babel 具备识别 TypeScript 代码的功能了。
引入 React DOM,让组件挂载到浏览器的 DOM 树上。
yarn add react-dom @types/react-dom
重新编写入口代码。
import React from 'react'
import { createRoot } from 'react-dom/client'
import App from './src/App'
import 'antd/dist/antd.min.css' // 全局 css
const root = createRoot(document.getElementById('app')!)
root.render(<App />) // 挂载 App 组件
自动引用 React
Babel 会把 JSX 渲染的逻辑改写成 JS 调用的方式:
<div style={{ width: 100 }} />
// Babel 转译后
// React.createElement('MyComponent', { style: 100 })
转译后的代码会引用 React,所以在渲染 JSX 的同时,必须显式引入 React:
// 不显式引入会导致报错: React not defined
import React from 'react'
const elem = <div style={{ width: 100 }} />
Babel 插件 babel-plugin-auto-import 可以给每个模块自动引入 React。
yarn add -D babel-plugin-auto-import
安装完毕后,babel.config.js
增加 auto-import 插件:
module.exports = {
presets: [...],
+ plugins: [
+ ["babel-plugin-auto-import", {
+ declarations: [
+ { default: "React", "path": "react" }
+ ]
+ }]
+ ]
}
webpack 修改入口配置
配置文件 webpack.config.ts
修改入口配置:
const config: webpack.Configuration = {
entry: {
- index: { import: './index.entry.js' },
+ index: { import: './index.entry' },
},
output: {...},
module: {...},
plugins: [...],
devServer: {...},
}
Resolve: 让 webpack 尝试其他后缀的文件
在上方的 React 代码,它引入了 src/App
import App from './src/App'
但是 src/App
实际上并不存在,而应该引入 src/App.tsx
。
webpack 如果找不到 src/App
,它会尝试找这些文件或目录:
src/App.js
src/App.json
src/App.wasm
src/App/
修改 webpack 配置的 resolve,可以让 webpack 尝试搜索其他后缀的文件,从而使 src/App
能找到 src/App.tsx
配置文件 webpack.config.ts
修改 Resolve 配置:
设置 .js
、.json
、.jsx
、.tsx
、.ts
后缀搜索,对于 src/App
,能找到以下文件:
src/App.js
src/App.json
src/App.jsx
src/App.tsx
src/App.ts
const config: webpack.Configuration = {
entry: {...},
output: {...},
module: {...},
plugins: [...],
devServer: {...},
+ resolve: {
+ extensions: ['.js', '.json', '.jsx', '.tsx', '.ts'],
+ },
}
启动 webpack 开发服务器,即可看到页面的效果。

webpack + React 热重载效果
打包应用
webpack 配置文件的 optimization
提供优化定制,目标代码的其中一个特点是压缩,在这里可以用 TerserPlugin(高效率JS压缩混淆工具) 进行压缩。
同时为了避免启用了压缩影响开发环境速度,可以引入一个环境变量控制压缩开关。
配置文件 webpack.config.ts
修改压缩配置:
+import TerserPlugin from 'terser-webpack-plugin'
const config: webpack.Configuration = {
entry: {...},
output: {...},
module: {...},
plugins: [...],
devServer: {...},
resolve: {...},
+ optimization: {
+ minimize: process.env.NODE_ENV === 'production' && true,
+ minimizer: [new TerserPlugin()],
+ },
}
用 cross-env 可以消除跨平台的环境变量语法差异:
yarn add -D cross-env
带环境变量 NODE_ENV
执行编译
yarn cross-env NODE_ENV=production webpack -c webpack.config.ts
整合项目
经过上方的一通配置,现在 webpack 已经分为开发环境和线上环境:
- 开发环境 特点:不启用压缩,启用热更新,增量编译
- 线上环境 特点:编译一次,生成尽量小的目标代码
编写 package.json
{
...
+ "scripts": {
+ "dev": "cross-env NODE_ENV=development webpack serve -c webpack.config.ts",
+ "prod": "cross-env NODE_ENV=production webpack -c webpack.config.ts"
+ },
...
}
配置文件 webpack.config.ts
指定 webpack 模式:
webpack 会根据 mode(none、production、development)的值进行模块处理和优化。
const config: webpack.Configuration = {
+ mode: process.env.NODE_ENV === "production" ? "production" : "development",
entry: {...},
output: {...},
module: {...},
plugins: [...],
devServer: {...},
resolve: {...},
optimization: {...},
}
搭建结果
至此已顺利搭建了一个 webpack 的开发环境。
# 启动开发环境
yarn dev
# 打包
yarn prod
项目打包后,结果存放于 dist/
目录,可以使用 npm包 serve
启动 HTTP 文件服务,给浏览器访问。
# 安装
yarn add -D serve
# 运行
yarn serve dist/
附录
webpack Official (https://webpack.js.org/)