一直以来都是使用脚手架创建应用,现在有空认真研究了一下webpack5,从零开始搭建项目,受益颇多,记下心得,以供参考,开发环境请参考开发环境配置。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="favicon.ico" ref="shortcut" type="image/x-iocn" />
<title>Webpack React Clititle>
head>
<body>
<div id="app">div>
body>
html>
// main.js
import React from "react";
import ReactDom from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
const root = ReactDom.createRoot(document.getElementById("app"));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
// App.jsx
import React, { lazy, Suspense } from "react";
import { Link, Route, Routes } from "react-router-dom";
// 路由懒加载
const Home = lazy(() => import(/* webpackChunkName: 'home' */ "./pages/Home"));
const About = lazy(() => import(/* webpackChunkName: 'about' */"./pages/About"));
function App() {
return (
-
home
-
about
loading }>
}>
}>
在这里把所有需要的依赖全部安装一下
{
"name": "webpack_react_cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "npm run dev",
"dev": "cross-env NODE_ENV=development webpack server --config ./config/webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack --config ./config/webpack.prod.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"browserslist": [
"last 2 version",
"> 1%",
"not dead"
],
"devDependencies": {
"@babel/core": "^7.21.5",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"babel-loader": "^9.1.2",
"babel-preset-react-app": "^10.0.1",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^5.0.0",
"eslint": "^8.39.0",
"eslint-config-react-app": "^7.0.1",
"eslint-webpack-plugin": "^4.0.1",
"html-webpack-plugin": "^5.5.1",
"image-minimizer-webpack-plugin": "^3.8.2",
"imagemin": "^8.0.1",
"imagemin-gifsicle": "^7.0.0",
"imagemin-jpegtran": "^7.0.0",
"imagemin-optipng": "^8.0.0",
"imagemin-svgo": "^10.0.1",
"less-loader": "^11.1.0",
"mini-css-extract-plugin": "^2.7.5",
"postcss-loader": "^7.3.0",
"postcss-preset-env": "^8.3.2",
"progress-bar-webpack-plugin": "^2.1.0",
"react-refresh": "^0.14.0",
"sass-loader": "^13.2.2",
"style-loader": "^3.3.2",
"terser-webpack-plugin": "^5.3.7",
"thread-loader": "^4.0.1",
"webpack": "^5.81.0",
"webpack-cli": "^5.0.2",
"webpack-dev-server": "^4.13.3",
"webpackbar": "^5.0.2",
"workbox-webpack-plugin": "^6.5.4"
},
"dependencies": {
"antd": "^5.4.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.11.0"
},
"resolutions": {
"//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it",
"bin-wrapper": "npm:bin-wrapper-china",
"rollup": "^2.72.0"
}
}
通过entry节点指定打包的入口,通过output节点指定打包的出口,配置模式mode,自动解析文件类型等。
// webpack.prod.js
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "../dist"),
filename: "static/js/[name].[contenthash:10].js",
chunkFilename: "static/js/[name].[contenthash:10].chunk.js",
assetModuleFilename: "static/media/[hash:10][ext][query]",
clean: true,
},
mode: "production",
devtool: "source-map",
// 关闭性能分析提高打包速度
performance: false,
resolve: {
// 自动解析文件扩展名
extensions: [
".web.mjs",
".mjs",
".web.js",
".js",
".web.ts",
".ts",
".web.tsx",
".tsx",
".json",
".web.jsx",
".jsx",
],
},
}
loader 从右到左(或从下到上)的解析执行,所以写法要谨慎。
// babel.config.js
module.exports = {
// 智能预设,能够编译es6语法
// https://github.com/facebook/create-react-app/blob/main/packages/babel-preset-react-app/create.js
presets: [
"react-app"
],
"compact": true
};
// webpack.prod.js
const path = require("path");
// 本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,支持link引入
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const os = require("os");
const threads = os.cpus().length; // 获取cpu核数
function getStyLoader(pre) {
return [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: {
mode: "local",
auto: true,
localIdentName: "[path][name]__[local]--[hash:5]",
exportLocalsConvention: "camelCase",
},
},
},
// 处理兼容性,配合package.json中的browserslist使用
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
// 其他选项
},
],
],
},
},
},
pre,
].filter(Boolean); // 过滤掉underfind
}
module.exports = {
module: {
rules: [
// 处理css
{
test: /\.css$/i,
use: getStyLoader(),
},
{
test: /\.less$/i,
use: getStyLoader("less-loader"),
},
{
test: /\.s[ac]ss$/i,
use: getStyLoader("sass-loader"),
},
// 处理图片
{
test: /\.(png|jpe?g|gif|webp|svg)$/i,
// asset可以转换base64
type: "asset",
parser: {
dataUrlCondition: {
// 小于4kb转化成base64.减少请求,资源会变大一些
maxSize: 4 * 1024, // 4kb
},
},
generator: {
// :8,hash钱8位
filename: "static/images/[hash:8][ext]",
},
},
{
test: /\.(ttf|mp4)$/i,
// asset/resource 原封不动输出
type: "asset/resource",
// generator: {
// // :8,hash钱8位
// filename: "static/fonts/[hash:8][ext]",
// },
},
// 处理js
{
test: /\.jsx?$/,
// exclude: /(node_modules)/, // 排除的文件
include: [path.resolve(__dirname, "../src")],
use: [
{
loader: "thread-loader",
options: {
works: threads, // 进程数
},
},
{
loader: "babel-loader",
// 可以在外面写
options: {
cacheDirectory: true, // 开启缓存
cacheCompression: false, // 关闭缓存压缩
},
},
],
},
],
},
};
// .eslintrc.js
module.exports = {
extends: ["react-app"], // 继承 react 官方规则
parserOptions: {
babelOptions: {
presets: [
// 解决页面报错问题
["babel-preset-react-app", false],
"babel-preset-react-app/prod",
],
},
},
};
// webpack.prod.js
const path = require("path");
// eslint检验
const ESLintPlugin = require("eslint-webpack-plugin");
// html模板
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,支持link引入
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// css压缩插件
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
// js压缩插件
const TerserWebpackPlugin = require("terser-webpack-plugin");
// images压缩插件
const ImageMinimizerWebpackPlugin = require("image-minimizer-webpack-plugin");
// 复制文件
const CopyPlugin = require("copy-webpack-plugin");
// 无网也能访问
const WorkboxPlugin = require("workbox-webpack-plugin");
// 显示打包进度条
const WebpackBar = require("webpackbar");
const ProgressBarWebpackPlugin = require("progress-bar-webpack-plugin");
const os = require("os");
const threads = os.cpus().length; // 获取cpu核数
module.exports = {
// 配置plugins
plugins: [
new ESLintPlugin({
// 检查哪些文件
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true, // 开启缓存
// 缓存目录
cacheLocation: path.resolve(__dirname, "../node_modules/.cache/eslint"),
// // 进程数
threads,
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, "../public"),
to: path.resolve(__dirname, "../dist"),
globOptions: {
// 忽略html文件
ignore: ["**/index.html"],
},
},
],
options: {
concurrency: 100,
},
}),
// 显示打包进度
new WebpackBar(),
new ProgressBarWebpackPlugin(),
],
optimization: {
// 代码分割
splitChunks: {
// include all types of chunks
chunks: "all",
cacheGroups: {
// react react-dom react-router-dom 一起打包成一个js文件
react: {
test: /[\\/]node_modules[\\/]react(.*)?[\\/]/,
name: "chunk-react",
priority: 40,
},
// antd 单独打包
antd: {
test: /[\\/]node_modules[\\/]antd[\\/]/,
name: "chunk-antd",
priority: 30,
},
// 剩下node_modules单独打包
libs: {
test: /[\\/]node_modules[\\/]/,
name: "chunk-libs",
priority: 20,
},
},
},
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}`,
},
minimizer: [
// 压缩css
new CssMinimizerWebpackPlugin(),
// 压缩js
new TerserWebpackPlugin({
// 进程数
parallel: threads,
}),
// 压缩图片
new ImageMinimizerWebpackPlugin({
minimizer: {
implementation: ImageMinimizerWebpackPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical",
},
},
],
},
],
],
},
},
}),
],
},
};
// webpack.prod.js
const path = require("path");
// eslint检验
const ESLintPlugin = require("eslint-webpack-plugin");
// html模板
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,支持link引入
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// css压缩插件
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
// js压缩插件
const TerserWebpackPlugin = require("terser-webpack-plugin");
// images压缩插件
const ImageMinimizerWebpackPlugin = require("image-minimizer-webpack-plugin");
// 复制文件
const CopyPlugin = require("copy-webpack-plugin");
// 无网也能访问
const WorkboxPlugin = require("workbox-webpack-plugin");
// 显示打包进度条
const WebpackBar = require("webpackbar");
const ProgressBarWebpackPlugin = require("progress-bar-webpack-plugin");
const os = require("os");
const threads = os.cpus().length; // 获取cpu核数
function getStyLoader(pre) {
return [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: {
mode: "local",
auto: true,
localIdentName: "[path][name]__[local]--[hash:5]",
exportLocalsConvention: "camelCase",
},
},
},
// 处理兼容性,配合package.json中的browserslist使用
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
// 其他选项
},
],
],
},
},
},
pre,
].filter(Boolean); // 过滤掉underfind
}
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "../dist"),
filename: "static/js/[name].[contenthash:10].js",
chunkFilename: "static/js/[name].[contenthash:10].chunk.js",
assetModuleFilename: "static/media/[hash:10][ext][query]",
clean: true,
},
mode: "production",
devtool: "source-map",
// 关闭性能分析提高打包速度
performance: false,
resolve: {
// 自动解析文件扩展名
extensions: [
".web.mjs",
".mjs",
".web.js",
".js",
".web.ts",
".ts",
".web.tsx",
".tsx",
".json",
".web.jsx",
".jsx",
],
},
module: {
rules: [
// 处理css
{
test: /\.css$/i,
use: getStyLoader(),
},
{
test: /\.less$/i,
use: getStyLoader("less-loader"),
},
{
test: /\.s[ac]ss$/i,
use: getStyLoader("sass-loader"),
},
// 处理图片
{
test: /\.(png|jpe?g|gif|webp|svg)$/i,
// asset可以转换base64
type: "asset",
parser: {
dataUrlCondition: {
// 小于4kb转化成base64.减少请求,资源会变大一些
maxSize: 4 * 1024, // 4kb
},
},
generator: {
// :8,hash钱8位
filename: "static/images/[hash:8][ext]",
},
},
{
test: /\.(ttf|mp4)$/i,
// asset/resource 原封不动输出
type: "asset/resource",
// generator: {
// // :8,hash钱8位
// filename: "static/fonts/[hash:8][ext]",
// },
},
// 处理js
{
test: /\.jsx?$/,
// exclude: /(node_modules)/, // 排除的文件
include: [path.resolve(__dirname, "../src")],
use: [
{
loader: "thread-loader",
options: {
works: threads, // 进程数
},
},
{
loader: "babel-loader",
// 可以在外面写
options: {
// presets: ['@babel/preset-env'],
cacheDirectory: true, // 开启缓存
cacheCompression: false, // 关闭缓存压缩
},
},
],
},
],
},
//
plugins: [
new ESLintPlugin({
// 检查哪些文件
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true, // 开启缓存
// 缓存目录
cacheLocation: path.resolve(__dirname, "../node_modules/.cache/eslint"),
// // 进程数
threads,
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, "../public"),
to: path.resolve(__dirname, "../dist"),
globOptions: {
// 忽略html文件
ignore: ["**/index.html"],
},
},
],
options: {
concurrency: 100,
},
}),
// 显示打包进度
new WebpackBar(),
new ProgressBarWebpackPlugin(),
],
optimization: {
// 代码分割
splitChunks: {
// include all types of chunks
chunks: "all",
cacheGroups: {
// react react-dom react-router-dom 一起打包成一个js文件
react: {
test: /[\\/]node_modules[\\/]react(.*)?[\\/]/,
name: "chunk-react",
priority: 40,
},
// antd 单独打包
antd: {
test: /[\\/]node_modules[\\/]antd[\\/]/,
name: "chunk-antd",
priority: 30,
},
// 剩下node_modules单独打包
libs: {
test: /[\\/]node_modules[\\/]/,
name: "chunk-libs",
priority: 20,
},
},
},
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}`,
},
minimizer: [
// 压缩css
new CssMinimizerWebpackPlugin(),
// 压缩js
new TerserWebpackPlugin({
// 进程数
parallel: threads,
}),
// 压缩图片
new ImageMinimizerWebpackPlugin({
minimizer: {
implementation: ImageMinimizerWebpackPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical",
},
},
],
},
],
],
},
},
}),
],
},
};