最近使用最新的webpack5搭建了一个react新版本的项目,希望对大家有帮助,不足也请热心评论一起讨论
yarn add webpack webpack-cli webpack-dev-server webpack-merge webpack-bundle-analyzer -D
build
----webpack.ananly.ts
----webpack.base.ts
----webpack.dev.ts
----webpack.prod.ts
package.json
"scripts": {
"dev:dev": "cross-env NODE_ENV=development BASE_ENV=development webpack serve -c build/webpack.dev.ts",
"dev:test": "cross-env NODE_ENV=development BASE_ENV=test webpack serve -c build/webpack.dev.ts",
"dev:pre": "cross-env NODE_ENV=development BASE_ENV=pre webpack serve -c build/webpack.dev.ts",
"dev:prod": "cross-env NODE_ENV=development BASE_ENV=production webpack serve -c build/webpack.dev.ts",
"build:dev": "cross-env NODE_ENV=production BASE_ENV=development webpack -c build/webpack.prod.ts",
"build:test": "cross-env NODE_ENV=production BASE_ENV=test webpack -c build/webpack.prod.ts",
"build:pre": "cross-env NODE_ENV=production BASE_ENV=pre webpack -c build/webpack.prod.ts",
"build:prod": "cross-env NODE_ENV=production BASE_ENV=production webpack -c build/webpack.prod.ts",
"build:analy": "cross-env NODE_ENV=production BASE_ENV=production webpack -c build/webpack.analy.ts"
},
webpack.base.ts公共配置 基本五大核心结构
主要:对 ts tsx css less sass 图片 字体 媒体 json文件 等静态资源进行解析
import { Configuration, DefinePlugin } from 'webpack'
import path from 'path'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import * as dotenv from 'dotenv'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import WebpackBar from 'webpackbar'
//判断当前运行属于什么环境下
const isDev = process.env.NODE_ENV === 'development'
//开发和生产环境下css解析配置
const styleLoadersArray=[
isDev?'style-loader':MiniCssExtractPlugin.loader,
{
loader:'css-loader',
options:{
modules:{
localIdentName:'[path][name]__[local]--[contenthash:8]'
}
}
},
'postcss-loader'
]
// 加载配置文件
const envConfig = dotenv.config({
path: path.resolve(__dirname, '../env/.env.' + process.env.BASE_ENV)
})
const baseConfig:Configuration = {
entry:path.join(__dirname,'../src/index.tsx'),//入口文件
//打包出口文件
output:{
filename:'static/js[name][chunkhash:8].js',//每个输出js的名称
path:path.join(__dirname,"../dist"),//打包结果输出路径
clean:true,//webpack4需要配置clean-webpack-plugin来删除dist文件,webpack5内置了
publicPath:"/" //打包后文件的公共前缀路径
},
module:{
rules:[
{
test:/.(ts|tsx)$/, //匹配ts tsx文件
exclude:/node_modules/, //不匹配 小优化
use:["thread-loader","babel-loader"]//项目大以后开启thread-loader
},
{
test:/\.css$/,//匹配css文件
use:styleLoadersArray
},
{
test: /\.less$/,//匹配less文件
use: [
...styleLoadersArray,
{
loader: 'less-loader',
options: {
lessOptions: {
importLoaders: 2,
// 可以加入modules: true,这样就不需要在less文件名加module了
modules: true,
// 如果要在less中写类型js的语法,需要加这一个配置
javascriptEnabled: true
}
}
}
]
},
{
test: /\.(scss|sass)$/,
use: [...styleLoadersArray, 'sass-loader']
},
{
test: /\.styl$/,
use: [...styleLoadersArray, 'stylus-loader']
},
{
test:/\.(png|jpe?g|gif|svg)$/i,//匹配图片文件
type:"asset",
parser:{
dataUrlCondition: {
maxSize: 20 * 1024 // 小于20kb转base64
}
},
generator: {
filename: 'static/images/[contenthash:8][ext][query]' // 文件输出目录和命名
}
},
{
test: /.(woff2?|eot|ttf|otf)$/, // 匹配字体图标文件
type: 'asset', // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb转base64
}
},
generator: {
filename: 'static/fonts/[contenthash:8][ext][query]' // 文件输出目录和命名
}
},
{
test: /.(mp4|webm|ogg|mp3|wav|flac|aac)$/, // 匹配媒体文件
type: 'asset', // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb转base64
}
},
generator: {
filename: 'static/media/[contenthash:8][ext][query]' // 文件输出目录和命名
}
},
{
// 匹配json文件
test: /\.json$/,
type: 'asset/resource', // 将json文件视为文件类型
generator: {
// 这里专门针对json文件的处理
filename: 'static/json/[name].[contenthash:8][ext][query]'
}
}
]
},
resolve:{
extensions: ['.ts', '.tsx', '.js', '.jsx', '.less', '.css'],
// 别名需要配置两个地方,这里和 tsconfig.json
alias: {
'@': path.join(__dirname, '../src')
},
modules: [path.resolve(__dirname, '../node_modules')] // 查找第三方模块只在本项目的node_modules中查找
},
cache:{
type: 'filesystem' // 使用文件缓存
},
plugins:[
new HtmlWebpackPlugin({
title: 'webpack5-react-ts',
filename: 'index.html',
// 复制 'index.html' 文件,并自动引入打包输出的所有资源(js/css)
template: path.join(__dirname, '../public/index.html'),
inject: true, // 自动注入静态资源
hash: true,
cache: false,
// 压缩html资源
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true, //去空格
removeComments: true, // 去注释
minifyJS: true, // 在脚本元素和事件属性中缩小JavaScript(使用UglifyJS)
minifyCSS: true // 缩小CSS样式元素和样式属性
},
nodeModules: path.resolve(__dirname, '../node_modules')
}),
// 注入到业务
new DefinePlugin({
'process.env': JSON.stringify(envConfig.parsed),
'process.env.BASE_ENV': JSON.stringify(process.env.BASE_ENV),
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}),
new WebpackBar({
color: '#85d', // 默认green,进度条颜色支持HEX
basic: false, // 默认true,启用一个简单的日志报告器
profile: false // 默认false,启用探查器。
})
].filter(Boolean),
}
export default baseConfig
webpakc.dev.ts主要对于开发模式下webpack-dev-server主要进行配置和热更新和对选择合适的source-map
import path from 'path';
import baseConfig from "./webpack.base";
import webpack, { Configuration as WebpackConfiguration } from 'webpack';
import { merge } from 'webpack-merge';
import WebpackDevServer, {
Configuration as WebpackDevServerConfiguration,
} from 'webpack-dev-server';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'
// 运行命令的时候重启一次打开一个tab 页很烦,所以呢优化一下
// 参考:create-react-app 的启动方式
// https://github.com/facebook/create-react-app/blob/main/packages/react-dev-utils/openChrome.applescript
// 记得关闭webpack-dev-server的配置中的自动打开 open: false 或者注释
// const openBrowser = require("./util/openBrowser");
interface Configuration extends WebpackConfiguration {
devServer?: WebpackDevServerConfiguration;
}
const host = '127.0.0.1';
const port = '8082';
const devConfig: Configuration = merge(baseConfig, {
mode: 'development', // 开发模式,打包更加快速,省了代码优化步骤
/**
开发环境推荐:eval-cheap-module-source-map
● 本地开发首次打包慢点没关系,因为 eval 缓存的原因, 热更新会很快
● 开发中,我们每行代码不会写的太长,只需要定位到行就行,所以加上 cheap
● 我们希望能够找到源代码的错误,而不是打包后的,所以需要加上 module
*/
devtool: 'eval-cheap-module-source-map',
plugins: [
new ReactRefreshWebpackPlugin()// 添加热更新插件
],
})
const devServer = new WebpackDevServer(
{
host, // 地址
port, // 端口
open: false, // 是否自动打开,关闭
setupExitSignals: true, // 允许在 SIGINT 和 SIGTERM 信号时关闭开发服务器和退出进程。
compress: false, // gzip压缩,开发环境不开启,提升热更新速度
hot: true, // 开启热更新,后面会讲react模块热替换具体配置
historyApiFallback: true, // 解决history路由404问题
static: {
directory: path.join(__dirname, '../public'), // 托管静态资源public文件夹
},
headers: { 'Access-Control-Allow-Origin': '*' },
},
webpack(devConfig),
);
devServer.start().then(() => {
// 启动界面
console.log(`http://${host}:${port}`);
});
export default devConfig;
webpack.prod.ts 生产环境下主要进行代码的多线程压缩 公共代码分割提取
import path from 'path'
import { Configuration } from 'webpack'
import { merge } from 'webpack-merge'
import CopyPlugin from 'copy-webpack-plugin'
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
import TerserPlugin from 'terser-webpack-plugin'
import CompressionPlugin from 'compression-webpack-plugin'
import baseConfig from './webpack.base'
const globAll = require('glob-all')
const { PurgeCSSPlugin } = require('purgecss-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const prodConfig: Configuration = merge(baseConfig, {
mode: 'production', // 生产模式,会开启tree-shaking和压缩代码,以及其他优化
/**
* 打包环境推荐:none(就是不配置devtool选项了,不是配置devtool: 'none')
* ● none话调试只能看到编译后的代码,也不会泄露源代码,打包速度也会比较快。
* ● 只是不方便线上排查问题, 但一般都可以根据报错信息在本地环境很快找出问题所在。
*/
plugins: [
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, '../public'), // 复制public下文件
to: path.resolve(__dirname, '../dist'), // 复制到dist目录中
filter: source => !source.includes('index.html') // 忽略index.html
}
]
}),
new MiniCssExtractPlugin({
filename: 'static/css/[name][contenthash:8].css' // 抽离css的输出目录和名称
}),
// 打包时生成gzip文件
new CompressionPlugin({
test: /\.(js|css)$/, // 只生成css,js压缩文件
filename: '[path][base].gz', // 文件命名
algorithm: 'gzip', // 压缩格式,默认是gzip
threshold: 10240, // 只有大小大于该值的资源会被处理。默认值是 10k
minRatio: 0.8 // 压缩率,默认值是 0.8
}),
new PurgeCSSPlugin({
paths: globAll.sync(
[`${path.join(__dirname, '../src')}/**/*`, path.join(__dirname, '../public/index.html')],
{
nodir: true
}
),
// 用 only 来指定 purgecss-webpack-plugin 的入口
// https://github.com/FullHuman/purgecss/tree/main/packages/purgecss-webpack-plugin
only: ['dist'],
safelist: {
standard: [/^ant-/] // 过滤以ant-开头的类名,哪怕没用到也不删除
}
})
],
optimization: {
// splitChunks: {
// chunks: "all",
// },
runtimeChunk: {
name: 'mainifels'
},
minimize: true,
minimizer: [
new CssMinimizerPlugin(), // 压缩css
new TerserPlugin({
parallel: true, // 开启多线程压缩
terserOptions: {
compress: {
pure_funcs: ['console.log'] // 删除console.log
}
}
})
],
splitChunks: {
// 分隔代码
cacheGroups: {
vendors: {
// 提取node_modules代码
test: /node_modules/, // 只匹配node_modules里面的模块
name: 'vendors', // 提取文件命名为vendors,js后缀和chunkhash会自动加
minChunks: 1, // 只要使用一次就提取出来
chunks: 'initial', // 只提取初始化就能获取到的模块,不管异步的
minSize: 0, // 提取代码体积大于0就提取出来
priority: 1 // 提取优先级为1
},
commons: {
// 提取页面公共代码
name: 'commons', // 提取文件命名为commons
minChunks: 2, // 只要使用两次就提取出来
chunks: 'initial', // 只提取初始化就能获取到的模块,不管异步的
minSize: 0 // 提取代码体积大于0就提取出来
}
}
}
},
performance: {
hints: false,
maxAssetSize: 4000000, // 整数类型(以字节为单位)
maxEntrypointSize: 5000000 // 整数类型(以字节为单位)
}
})
export default prodConfig
先这样