前言
之前做项目都是用的现成的 umi 、 antd-pro 、create-react-app 等现成的 react 框架或脚手架,这次重新找工作打算沉淀一下 webpack 技术,前 2 天心血来潮打算构建一份可用的 react 基础脚手架,从webpack 初始化搞起,到配置 Babel、配置 Plugin,再到配置 react-router,用的基本都是目前主流的,也方便随时回头看,每一步在 README.md 都有对应的解析
项目目录
│ .babelrc
│ .gitignore
│ LICENSE
│ package.json
│ postcss.config.js
│ README.md
│ index.html
├─build
│ ├─webpack.base.js
│ ├─webpack.dev.js
│ └─webpack.prod.js
├─dist
├─mock
│ └─index.json
├─public
├─readmeimgs (仅供 readme 图片存储用)
└─src
│ index.js
│ global.less
├─components
├─pages
├─router
├─static
├─store
└─utils
utils.js
环境
必须 node v18 +
初始化项目
yarn init
配置webpack
安装 webpack 5
yarn add webpack webpack-cli webpack-dev-server webpack-merge -D
出入口配置
{
entry: path.resolve(__dirname, "../src/*.js"), // *配置成对应的入口文件
output: {
filename: "[name].[hash:8].js",
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
}
配置 resolve模块解析
extensions
指定模块引入时不需要的扩展名,例如 .js、.jsx 等,这样在引入组件时可以省略
alias
配置模块的别名,可以简化模块的引用路径
resolve: {
extensions: [".js", ".jsx", ".less", ".css"],
alias: {
"@": path.resolve(__dirname, "../src"),
},
},
例如:import Index from "../src/home/index.js|jsx",可以写成 import Index from "@/home/index",省略后缀名,简化路径
modules
配置解析模块时应该搜索的目录,默认是 ['node_modules']
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
},
};
mainFiles
配置在目录中优先解析的文件,默认是 ['index']
module.exports = {
resolve: {
mainFiles: ['main', 'index'],
},
};
mainFields
配置在寻找包时应该查找的字段,例如 ['browser', 'module', 'main']
module.exports = {
resolve: {
mainFields: ['browser', 'module', ]
},
};
配置loader(Less样式、图片等资源、js)
yarn add less less-loader style-loader css-loader url-loader mini-css-extract-plugin postcss-loader autoprefixer -D
处理js|jsx
rules:[
{
test: /\.(js|jsx)$/,
include: path.join(__dirname, "../src"),
use: [
{
loader: "babel-loader",
options: {
cacheDirectory: true,
},
},
],
}
]
处理less
yarn add less less-loader style-loader css-loader postcss-loader autoprefixer -D
style-loader:将样式注入到html的 style 标签
css-loader:解析css文件,将CSS转化为CommonJS
postcss-loader:处理css中的css前缀,新版配置在postcss.config.js中,webpack 里仅保留名称即可
autoprefixer:自动添加css前缀,配置在postcss.config.js中
less-loader:将less编译成css
rules:[
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
"postcss-loader",
"less-loader",
]
},
{
test: /\.css$/,
use: [
"style-loader",
"css-loader",
"postcss-loader"
]
},
]
处理图片、视频、字体、文件等资源
webpack 5内置了 asset 资源处理功能,可以自动处理图片、视频、字体、文件等资源,无需额外配置模块
rules:[
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, // 匹配图片文件
type: "asset", // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64位
}
},
generator:{
filename:'static/images/[name][ext]', // 文件输出目录和命名
},
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, // 匹配视频和音频文件
type: 'asset/resource',
generator: {
filename: 'static/videos/[name][ext]',
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, // 匹配字体文件
type: "asset", // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64位
}
},
generator: {
filename: 'static/fonts/[name][ext]',
}
}
]
注意
存在html 标签使用相对路径页面不显示问题
在 webpack + react 的项目中,直接写相对路径无法被 webpack 的 loader 正确处理,最终打包后路径会失效, 正确做法:用 import 或 require 引入图片,视频同理
兼容处理方案:
可自行探索尝试
配置Babel
Babel 是一个广泛使用的 JavaScript 编译器,主要用于将 ES6/ESNext 代码转换为向后兼容的 JavaScript 代码,以便在旧版浏览器或其他环境中运行。随着 Babel 的发展,从 Babel 6 到 Babel 7 发生了许多重大变化,特别是在配置方式和插件系统上,提到Babel版本的时候,通常是指@babel/core这个Babel核心包的版本
安装
yarn add babel-loader @babel/core @babel/preset-react @babel/preset-env @babel/plugin-transform-runtime -D
babel-loader
用于将 ES6+ 语法转换为 ES5 语法,使代码在旧版本的浏览器中也能运行。例如对于我们项目中的jsx文件我们需要通过一个"转译器"将项目中的jsx文件转化成js文件,babel-loader在这里充当的就是这个转译器。
@babel/core
babel-loader仅仅识别出了jsx文件,内部核心转译功能需要@babel/core这个核心库,@babel/core模块就是负责内部核心转译实现的
@babel/preset-env
@babel/prest-env是babel转译过程中的一些预设,它负责将一些基础的es 6+语法,比如const/let...转译成为浏览器可以识别的低级别兼容性语法,但并不会对于一些es6+并没有内置一些高版本语法的实现比如Promise等polyfill,你可以将它理解为语法层面的转化不包含高级别模块(polyfill)的实现
@babel/plugin-transform-runtime
上边我们提到了对于一些高版本内置模块,比如Promise/Generate等等@babel/preset-env并不会转化,所以@babel/plugin-transform-runtime就是帮助我们来实现这样的效果的,他会在我们项目中如果使用到了Promise之类的模块之后去实现一个低版本浏览器的polyfill
1.减少重复代码 它会把一些 Babel 转换后常用的辅助函数(比如 _extends, _classCallCheck 等)提取到单独的模块里,避免每个文件都生成一份,减小打包体积。
2.避免全局污染 它会把 Promise、Symbol、Generator 等新特性用到的 polyfill 变成“局部引入”,不会污染全局对象,避免和第三方库冲突。
3.支持 async/await 配合 regenerator: true,可以让 Babel 正确转换 async/await 语法。
@babel/preset-react
是一组预设,内置了一系列babel plugin去转化jsx代码成为我们想要的js代码,可以将.jsx文件转化为js文件的同时将jsx标签转化为React.createElement的形式。
babel-loader + @babel/preset-react 一般一起使用,在webpack的构建过程中,babel-loader作为中间人,处理.js文件。当webpack遇到.js文件时,它会调用babel-loader来处理这些文件。babel-loader再调用Babel进行代码转译,最终返回给webpack继续打包过程
babel-plugin-import (版本不适用)
是 Babel 的插件,用于按需加载第三方库,减少项目大小。antd V5 采用了CSS-in-JS 本身具有按需加载的能力,因此无需使用 babel-plugin-import 来按需加载。
旧的一些Babel:
babel-plugin-import 用于按需加载第三方库,减少项目大小。antd V5 采用了CSS-in-JS 本身具有按需加载的能力,因此无需使用
@babel/plugin-proposal-class-properties 用于支持 ES6 的类Class属性语法(已废弃)
@babel/plugin-transform-class-properties 是将类的属性(properties)提升至类的构造函数中,已预设在@babel/preset-env中(已淘汰)
Babel 6 配置
在 Babel 6 中,配置通常是通过 .babelrc 文件或 package.json 中的 babel 字段来完成的
.babelrc 文件
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-transform-runtime"]
}
.package.json 文件
{
"babel": {
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-transform-runtime"]
}
}
Babel 7 配置
Babel 7 引入了几个重大变化,其中最重要的是对预设(presets)和插件(plugins)的拆分,以及对配置方式的改进。Babel 7 使用 @babel/core 作为核心库,并使用 @babel/preset-env 和其他具体的预设或插件。
使用 .babelrc 或 babel.config.js
.babelrc 文件
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": [
[
"import",
{
"libraryName": "antd",
"libraryDirectory": "es",
"style": true
}
],
[
"@babel/plugin-transform-runtime",
{
"regenerator": true
}
]
]
}
babel.config.js 文件
module.exports = {
presets: [
"@babel/preset-env",
"@babel/preset-react"
],
plugins: [
[
// antd V5 已不再支持
"import",
{
libraryName: "antd",
libraryDirectory: "es",
style: true
}
],
[
"@babel/plugin-transform-runtime",
{
"regenerator": true
}
]
]
};
配置 Plugin
压缩文件(Css 压缩、Js 压缩)
yarn add css-minimizer-webpack-plugin terser-webpack-plugin -D
css-minimizer-webpack-plugin 压缩、去重css,比较耗时,只用在打包项目时,在webpack.prod.js中配置
terser-webpack-plugin 实现打包后JS代码的压缩,比较耗时,只用在打包项目时,在webpack.prod.js中配置
Html、服务
yarn add html-webpack-plugin webpack-dev-server clean-webpack-plugin mini-css-extract-plugin -D
html-webpack-plugin 处理HTML资源。它会为你创建一个空的HTML文件,并自动引入打包输出的所有资源(比如JavaScript和CSS文件)
webpack-dev-server 创建服务,方便你本地开发时调试
clean-webpack-plugin 打包前自动清理上一次的输出目录文件
mini-css-extract-plugin 将CSS单独提取出来,生成独立的CSS文件
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: path.join(__dirname, "../index.html"),
}),
new MiniCssExtractPlugin({
filename: "[name].[contenthash:8].css",
chunkFilename: "chunk/[id].[contenthash:8].css"
}),
new CleanWebpackPlugin(),
]
tree-shaking、source-map、webpack-bundle-analyzer、gzip 优化
yarn add webpack-bundle-analyzer -D
yarn add compression-webpack-plugin -D (开启 gzip用)
webpack5默认开启tree-shaking(当打包的mode为production时),默认开启source-map,可以查看打包后代码的源码,方便调试。
tree-shaking 只打包用到的代码,没用到的代码不打包
{
mode: 'production'
}
source-map 源代码,体积较大,方便排查错误,一般在开发环境配置
{
mode: 'development',
devtool: 'eval-cheap-module-source-map'
}
webpack-bundle-analyzer 生成一个分析打包结果的页面,方便查看打包结果,一般在生产环境配置
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
plugins: [
new BundleAnalyzerPlugin(),
]
gzip优化
开启Gzip后,大大提高用户的页面加载速度,因为gzip的体积比原文件小很多,需要后端的配合,在webpack.prod.js中配置
const CompressionPlugin = require('compression-webpack-plugin')
plugins: [
// gzip
new CompressionPlugin({
algorithm: 'gzip',
threshold: 10240,
minRatio: 0.8
})
]
安装 react 18
yarn add react@18.2.0 react-dom@18.2.0 react-router-dom@6.30.0 -S
安装redux(需要就安装)
yarn add redux react-redux -S
安装UI(antd5.x)
yarn add antd @ant-design/icons -S
配置router
1.安装 router 后,在根目录src 里面的 index.js文件中引入配置 BrowserRouter 或者 HashRouter , 2.在src下新建pages文件夹建好对应的页面, 3.在src下新建router文件夹,然后在router文件夹下新建index.js文件,然后在index.js文件中写入以下代码
以上两种,一种用 routers 直接包裹 router,一种用 useRoutes Hooks ,都可以实现路由的配置
后续可能会加上权限控制、动态路由等功能
提示
loader 加载器的执行顺序是从右到左,从下到上,仅在同一个 rule 中起作用
1、-S (--save)
用于将包添加到项目的 dependencies,表示这是在生产环境中运行时所需的依赖。
例如:npm install package-name -S,这将把 package-name 添加到 dependencies 中。
2、-D (--save-dev)
用于将包添加到项目的 devDependencies,表示这是在开发阶段所需的依赖,例如测试工具、构建工具等。
例如:npm install package-name -D,这将把 package-name 添加到 devDependencies 中
最后
项目 git 地址:github.com/nothingobje…
官网地址: react-router v6、 Ant Design
参考资料
从零搭建完整的React项目模板(Webpack + React hooks + Mobx + Antd)
感谢大家
文章写的很草,都是构建项目时在 README.md里敲,用新的东西总要踩一些坑,少了很多概念性的东西,如果觉得对你有帮助,帮忙点个赞👍,感谢!