手敲Webpack 5:React + TypeScript项目脚手架搭建实践
webpacak5_react_typescript_cli
- 1.项目创建
- 2.基本项目结构
- 3.引入react
- 4.引入typescript
- 5.配置webpack
- 6.配置环境变量
- 7.文件别名
- 8.引入less,sass(scss)
- 9.处理其他常用资源
- 10.webpack构建速度优化
- 11.webpack构建产物优化
1.项目创建
初始化package.json文件,在文件根目录打开终端输入
pnpm init
会在根目录生成一个 package.json 文件:
{
"name": "webpack5_react_typescript_cli", //文件名
"version": "1.0.0", //版本号
"description": "",
"main": "index.js",
"scripts": { //命令
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "", //作者
"license": "ISC", //开源
}
2.基本项目结构
在根目录下创建项目的基本结构
index.html
内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack5_react_ts</title>
</head>
<body>
<!-- 容器组件 -->
<div id="root"></div>
</body>
</html>
3.引入react
安装依赖
pnpm i react react-dom
pnpm i @types/react @types/react-dom -D
index.tsx
import React from "react";
import { createRoot } from "react-dom/client";
import App from './App';
const root = document.querySelector('#root')
if(root){
createRoot(root).render(<App/>)
}
App.css
h2{
color:red;
}
App.tsx
import React from "react";
import './App.css'
function App(){
return <h2>Helle East_White</h2>
}
export default App
4.引入typescript
安装依赖
pnpm i typescript -D
pnpm i babel-loader ts-node @babel/core @babel/preset-react @babel/presettypescript @babel/preset-env core-js -D
初始化tsconfig.json
npx tsc --init
就会在根目录生成tsconfig.json文件
{
"compilerOptions": {
"jsx": "react-jsx",
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
5.配置webpack
安装依赖
pnpm i webpack webpack-cli -D
5.1webpack.base.ts
配置webpack.base.ts文件
import { Configuration } from 'webpack'
import path from 'path';
import HtmlWebpackPlugin from "html-webpack-plugin";
const baseConfig: Configuration = {
entry: path.join(__dirname, '../src/index.tsx'),
output: {
filename: 'static/js/[name].js',
path: path.join(__dirname, "../dist"),
clean: true,
publicPath: '/'
},
module: {
rules: [
{
test: /.(ts|tsx)$/,
use: {
"babel-loader",
presets: [
[
"@babel/preset-env",
{
targets: { browsers: [">1%", "last 2 versions", "not ie <=8"] },
useBuiltIns: 'usage',
corejs: 3,
loose: true,
}
],
["@babel/preset-react", { runtime: "automatic" }],
"@babel/preset-typescript",
]
}
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
resolve: {
extensions: [".tsx", ".ts", ".jsx", ".js"],
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, "../public/index.html"),
minify: {
collapseWhitespace: true, //去空格
removeComments: true, // 去注释
}
})
]
}
export default baseConfig
另外因为我们在 App.tsx 中引入了 css 文件,所以还需要安装相关的 loader
pnpm i style-loader css-loader html-webpack-plugin -D
因为webpack.base.ts包含所有的基本配置,随着webpack 做的事情越来越多,会逐渐变得很
庞大,我们可以将其中的 babel-loader 相关的配置抽离出来进行管理。在根目录新建
babel.config.js :
module.exports = {
presets: [
[
"@babel/preset-env",
{
targets: { browsers: [">1%", "last 2 versions", "not ie <=8"] },
useBuiltIns: 'usage',
corejs: 3,
loose: true,
}
],
["@babel/preset-react", { runtime: "automatic" }],
"@babel/preset-typescript",
]
}
再简化webpack.base.ts,将babel-loader配置简化为
module: {
rules: [
{
test: /.(ts|tsx)$/,
use: "babel-loader"
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
5.2webpack.dev.ts
接下来配置启动文件,通过webpack-dev-server启动项目,安装相关的依赖
pnpm i webpack-dev-server webpack-merge -D
配置webpack.dev.ts
import path from "path";
import { merge } from "webpack-merge";
import { Configuration as WebpackConfiguration } from "webpack";
import { Configuration as WebpackDevServerConfiguration } from "webpack-dev-server";
import baseConfig from "./webpack.base";
interface Configuration extends WebpackConfiguration {
devServer?: WebpackDevServerConfiguration;
}
const host = "127.0.0.1";
const port = "8082";
// 合并公共配置,并添加开发环境配置
const devConfig: Configuration = merge(baseConfig, {
mode: "development", // 开发模式,打包更加快速,省了代码优化步骤
devtool: "eval-cheap-module-source-map",
devServer: {
host,
port,
open: true, // 是否自动打开
compress: false, // gzip压缩,开发环境不开启,提升热更新速度
hot: true, // 开启热更新
historyApiFallback: true, // 解决history路由404问题
setupExitSignals: true, // 允许在 SIGINT 和 SIGTERM 信号时关闭开发服务器和退出进程。
static: {
directory: path.join(__dirname, "../public"), // 托管静态资源public文件夹
},
headers: { "Access-Control-Allow-Origin": "*" }, // HTTP响应头设置,允许任何来源进行跨域请求
},
});
export default devConfig;
在package.json中添加启动脚本
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack serve -c build/webpack.dev.ts"
},
在终端中输入命令 pnpm dev 运行,发现页面自动启动了
5.3webpack.prod.ts
配置webpack.prod.ts
import { Configuration } from "webpack";
import { merge } from "webpack-merge";
import baseConfig from "./webpack.base";
const prodConfig: Configuration = merge(baseConfig, {
mode: "production", // 生产模式,会开启tree-shaking和压缩代码,以及其他优化
});
export default prodConfig;
在package.json中添加生产环境的命令
"scripts": {
...
"build": "webpack -c build/webpack.prod.ts"
},
运行 pnpm build ,根目录下自动生成dist文件夹
然后通过 serve -S dist 命令,启动一个服务来查看打包结果,如果不出意外,打开控制台启动的
服务,就能看到页面了
可以通过 npx serve -S dist 命令启动一个服务,打开控制台启动的服务就可以看到页面了
5.4copy静态资源
一般 public 文件夹都会放一些静态资源,可以直接根据绝对路径引入,比如图片、 css 、 js 文件
等,不需要 webpack 进行解析,只需要打包的时候把 public 下内容复制到构建出口文件夹中,可以借
助 copy-webpack-plugin 插件,安装依赖:
pnpm i copy-webpack-plugin -D
修改webpack.base.ts:
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样式元素和样式属性
}
})
]
开发环境已经在 devServer 中配置了 static 托管了 public 文件夹,在开发环境使用绝对路径可以访问到 public 下的文件,但打包构建时不做处理会访问不到,所以现在需要在打包配置文件webpack.prod.ts 中新增 copy 插件配置
import { Configuration } from "webpack";
import { merge } from "webpack-merge";
import baseConfig from "./webpack.base";
import CopyPlugin from "copy-webpack-plugin";
import path from "path";
const prodConfig: Configuration = merge(baseConfig, {
mode: "production", // 生产模式,会开启tree-shaking和压缩代码,以及其他优化
plugins: [
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, "../public"), // 复制public下文件
to: path.resolve(__dirname, "../dist"), // 复制到dist目录中
filter: (source) => !source.includes("index.html"), // 忽略index.html
},
],
}),
]
});
export default prodConfig;
此时在public中添加静态资源,并运行bulid命令会将资源打包到dist中
6.配置环境变量
使用cross-env + DefinePlugin配置环境变量
安装cross-env
pnpm i cross-env -D
修改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"
},
在webpack.base.ts中打印一下环境变量
console.log('NODE_ENV', process.env.NODE_ENV)
console.log('BASE_ENV', process.env.BASE_ENV)
运行pnpm dev:dev 结果为
NODE_ENV development
BASE_ENV development
7.文件别名
在webpack.base.ts中配置:
resolve: {
extensions: [".tsx", ".ts", ".jsx", ".js"],
alias: {
"@": path.join(__dirname, "../src")
},
},
还需要在tsconfig.json中配置
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
},
这样就可以把引入地址的”." 改成@,并且不用担心更改文件位置后引用地址失效
8.引入less,sass(scss)
8.1基本使用
安装相关依赖
pnpm i less less-loader sass-loader sass -D
在webpack.base.ts中修改module
module: {
rules: [
......
{
test: /\.css$/, // 匹配css文件
use: ["style-loader", "css-loader"],
},
{
test: /\.less$/, // 匹配less文件
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.(scss|sass)$/, // 匹配sass文件
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
},
8.2处理CSS3前缀在浏览器中的兼容
安装依赖
pnpm i postcss-loader autoprefixer -D
在webpack.base.ts中给css,sass,less添加规则
module: {
rules: [
......
{
test: /\.css$/, // 匹配css文件
use: ["style-loader", "css-loader",'postcss-loader'],
},
{
test: /\.less$/, // 匹配less文件
use: ['style-loader', 'css-loader','postcss-loader','less-loader']
},
{
test: /\.(scss|sass)$/, // 匹配sass文件
use: ['style-loader', 'css-loader','postcss-loader','sass-loader']
}
]
},
配置完成后,需要有一份要兼容浏览器的清单,让 postcss-loader 知道要加哪些浏览器的前缀,在根
目录创建 .browserslistrc 文件:
IE 9 # 兼容IE 9
chrome 35 # 兼容chrome 35
9.处理其他常用资源
9.1处理图片
在webpack5可以使用自带的asset-module处理
修改 webpack.base.ts ,添加图片解析配置
module: {
rules: [
......
// 处理图片
{
test: /\.(png|jpe?g|gif|svg)$/i, // 匹配图片文件
type: "asset", // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64
}
},
generator: {
filename: 'static/images/[hash][ext][query]', // 文件输出目录和命名
},
},
]
由于我们希望通过 ES6 的新语法 ESModule 的方式导入资源,为了使 TypeScript 可以识别图片模
块,需要在 src/typings/global.d.ts 中加入声明:
/* IMAGES */
declare module '*.svg' {
const ref: string;
export default ref;
}
declare module '*.bmp' {
const ref: string;
export default ref;
}
declare module '*.gif' {
const ref: string;
export default ref;
}
declare module '*.jpg' {
const ref: string;
export default ref;
}
declare module '*.jpeg' {
const ref: string;
export default ref;
}
declare module '*.png' {
const ref: string;
export default ref;
}
9.2处理字体和媒体
字体文件和媒体文件的处理方式是一样的,只需要修改文件的匹配路径和打包后放置的位置就可以了
在webpack.base.ts中添加module的loader
module: {
rules: [
......
// 处理字体图标
{
test: /.(woff2?|eot|ttf|otf)$/, // 匹配字体图标文件
type: "asset", // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64
}
},
generator: {
filename: 'static/fonts/[hash][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/[hash][ext][query]', // 文件输出目录和命名
},
}
]
},
9.3处理json资源
webpack.base.ts中添加处理json的loader配置
......
{
// 匹配json文件
test: /\.json$/,
type: "json", // 模块资源类型为json模块
generator: {
// 这里专门针对json文件的处理
filename: "static/json/[name].[hash][ext][query]",
},
},
......
9.4babel处理js非标准语法
新增scr/components/Cls.tsx,在文件中配置
import React, { PureComponent } from "react";
// 装饰器为,组件添加age属性
function addAge(Target: Function) {
Target.prototype.age = 111
}
// 使用装饰器
@addAge
class Cls extends PureComponent {
age?: number
render() {
return (
<h2>我是类组件---{this.age}</h2>
)
}
}
export default Cls
需要开启一下 ts 装饰器支持,修改 tsconfig.json 文件
"experimentalDecorators": true,
上面Cls组件代码中使用了装饰器目前 js 标准语法是不支持的,现在运行或者打包会报错,不识别装饰
器语法,需要借助 babel-loader 插件,安装依赖
pnpm i @babel/plugin-proposal-decorators -D
修改babel.config.js中添加插件
const isDEV = process.env.NODE_ENV === "development"; // 是否是开发模式
module.exports = {
// 执行顺序由右往左,所以先处理ts,再处理jsx,最后再试一下babel转换为低版本语法
presets: [
[
"@babel/preset-env",
{
// 设置兼容目标浏览器版本,也可以在根目录配置.browserslistrc文件,babelloader会自动寻找上面配置好的文件.browserlistrc
// "targets": {
// "chrome": 35,
// "ie": 9
// },
targets: { browsers: ["> 1%", "last 2 versions", "not ie <= 8"] },
useBuiltIns: "usage", // 根据配置的浏览器兼容,以及代码中使用到的api进行引入polyfill按需添加
corejs: 3, // 配置使用core-js使用的版本
loose: true,
},
],
// 如果您使用的是 Babel 和 React 17,您可能需要将 "runtime": "automatic" 添加到配置中。
// 否则可能会出现错误:Uncaught ReferenceError: React is not defined
["@babel/preset-react", { runtime: "automatic" }],
"@babel/preset-typescript",
],
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
].filter(Boolean), // 过滤空值
};
10.webpack构建速度优化
10.1webpack进度条
安装依赖
pnpm i webpackbar -D
在webpack.base.ts中引入
import WebpackBar from 'webpackbar';
......
plugins: [
......
// 进度条配置
new WebpackBar({
color: "#85d", // 默认green,进度条颜色支持HEX
basic: false, // 默认true,启用一个简单的日志报告器
profile: false, // 默认false,启用探查器。
})
]
10.2构建耗时
安装依赖
pnpm i speed-measure-webpack-plugin -D
为了不影响到正常的开发/打包模式,我们选择新建一个配置文件,新增 webpack 构建分析配置文件 build/webpack.analy.ts
import { Configuration } from 'webpack'
import prodConfig from './webpack.prod' // 引入打包配置
import { merge } from 'webpack-merge' // 引入合并webpack配置方法
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); // 引入webpack打包速度分析插件
const smp = new SpeedMeasurePlugin(); // 实例化分析插件
// 使用smp.wrap方法,把生产环境配置传进去,由于后面可能会加分析配置,所以先留出合并空位
const analyConfig: Configuration = smp.wrap(merge(prodConfig, {
}))
export default analyConfig
在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"
},
执行 pnpm build:analy 命令,可以看到启动webpack的各个模块耗时
10.3开启持久化存储缓存
在 webpack5 之前做缓存是使用 babel-loader 缓存解决 js 的解析结果, cache-loader 缓存 css 等资
源的解析结果,还有模块缓存插件 hard-source-webpack-plugin ,配置好缓存后第二次打包,通过对
文件做哈希对比来验证文件前后是否一致,如果一致则采用上一次的缓存,可以极大地节省时间。
修改webpack.base,ts
const baseConfig: Configuration = {
entry: path.join(__dirname, '../src/index.tsx'),
output: {
filename: 'static/js/[name].js',
path: path.join(__dirname, "../dist"),
clean: true,
publicPath: '/'
},
// 开启持久化存储缓存
cache: {
type: 'filesystem', // 使用文件缓存
},
}
10.4开启多线程loader
安装依赖
pnpm i thread-loader -D
修改webpack.base.ts
rules: [
{
test: /.(ts|tsx)$/,
use: [
// 开启多进程打包。
// 进程启动大概为600ms,进程通信也有开销。
// 只有工作消耗时间比较长,才需要多进程打包
{
loader: 'thread-loader',
options: {
wokers: 4 // 进程数
}
},
'babel-loader']
},
......
]
10.5缩小构建目标
使用 include 和 exclude 两个配置项,可以实现这个功能,常见的例如:
- include :只解析该选项配置的模块
- exclude :不解该选项配置的模块,优先级更高
修改webpack.base.ts
rules: [
{
test: /.(ts|tsx)$/,
// 缩小构造域
exclude: /node_modules/,
use: [
// 开启多进程打包。
// 进程启动大概为600ms,进程通信也有开销。
// 只有工作消耗时间比较长,才需要多进程打包
{
loader: 'thread-loader',
options: {
wokers: 4 // 进程数
}
},
'babel-loader']
},
......
]
10.6devtools配置
修改webpack.dev.ts
const devConfig: Configuration = merge(baseConfig, {
......
devtool: "eval-cheap-module-source-map",
......
});
export default devConfig;
11.webpack构建产物优化
11.1bundle体积分析工具
安装依赖
pnpm i webpack-bundle-analyzer -D
修改webpack.analy.ts
import { Configuration } from 'webpack'
import prodConfig from './webpack.prod' // 引入打包配置
import { merge } from 'webpack-merge' // 引入合并webpack配置方法
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
// 引入webpack打包速度分析插件
const smp = new SpeedMeasurePlugin();
// 使用smp.wrap方法,把生产环境配置传进去,由于后面可能会加分析配置,所以先留出合并空位
const analyConfig: Configuration = smp.wrap(merge(prodConfig, {
plugins: [
new BundleAnalyzerPlugin() // 配置分析打包结果插件
]
}))
export default analyConfig;
11.2样式提取
在开发环境我们希望 css 嵌入在 style 标签里面,方便样式热替换,但打包时我们希望把 css 单独抽离
出来,方便配置缓存策略。而插件mini-css-extract-plugin就是来帮我们做这件事的,安装依赖:
安装依赖
pnpm i mini-css-extract-plugin -D
修改webpack.base.ts
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const isDev = process.env.NODE_ENV === 'development'
const styleLoadersArray = [
isDev ? "style-loader" : MiniCssExtractPlugin.loader, // 开发环境使用stylelooader,打包模式抽离css
{
loader: "css-loader",
options: {
modules: {
localIdentName: "[path][name]__[local]--[hash:5]",
},
},
},
'postcss-loader'
];
module:{
rules:[
// 处理css
{
test: /\.css$/, // 匹配css文件
use: styleLoadersArray,
},
// 处理less
{
test: /\.less$/, // 匹配less文件
use: [...styleLoadersArray, 'postcss-loader', 'less-loader']
},
// 处理sass
{
test: /\.(scss|sass)$/, // 匹配sass文件
use: [...styleLoadersArray, 'postcss-loader', 'sass-loader']
},
]
}
再修改 webpack.prod.ts ,打包时添加抽离 css 插件:
......
// 抽离css
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const prodConfig: Configuration = merge(baseConfig, {
mode: "production", // 生产模式,会开启tree-shaking和压缩代码,以及其他优化
plugins: [
......
new MiniCssExtractPlugin({
filename: 'static/css/[name].css' // 抽离css的输出目录和名称
}),
]
});
export default prodConfig;
11.3样式压缩
安装依赖
pnpm i css-minimizer-webpack-plugin -D
修改 webpack.prod.ts 文件
const prodConfig: Configuration = merge(baseConfig, {
mode: "production", // 生产模式,会开启tree-shaking和压缩代码,以及其他优化
optimization: {
minimizer: [
new CssMinimizerPlugin(), // 压缩css
],
},
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].css' // 抽离css的输出目录和名称
}),
]
});
export default prodConfig;
11.4压缩js
安装依赖
pnpm i terser-webpack-plugin compression-webpack-plugin -D
修改webpack,prod.ts
import { Configuration } from "webpack";
import { merge } from "webpack-merge";
import baseConfig from "./webpack.base";
import CopyPlugin from "copy-webpack-plugin";
import path from "path";
// 抽离css
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 压缩css
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
// 压缩js
import TerserPlugin from 'terser-webpack-plugin'
import CompressionPlugin from 'compression-webpack-plugin'
const prodConfig: Configuration = merge(baseConfig, {
mode: "production", // 生产模式,会开启tree-shaking和压缩代码,以及其他优化
optimization: {
runtimeChunk: {
name: 'mainifels'
},
minimize: true,
minimizer: [
new CssMinimizerPlugin(), // 压缩css
new TerserPlugin({
parallel: true, // 开启多线程压缩
terserOptions: {
compress: {
pure_funcs: ['console.log'] // 删除console.log
}
}
})
],
},
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].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
})
],
performance: { // 配置与性能相关的选项的对象
hints: false, // 设置为false将关闭性能提示。默认情况下,Webpack会显示有关入口点和资产大小的警告和错误消息。将hints设置为false可以禁用这些消息。
maxAssetSize: 4000000, // 设置一个整数,表示以字节为单位的单个资源文件的最大允许大小。如果任何资源的大小超过这个限制,Webpack将发出性能警告。在你提供的配置中,这个值被设置为4000000字节(约4MB)。
maxEntrypointSize: 5000000 // 设置一个整数,表示以字节为单位的入口点文件的最大允许大小。入口点是Webpack构建产生的主要JS文件,通常是应用程序的主要代码。如果入口点的大小超过这个限制,Webpack将发出性能警告。在你提供的配置中,这个值被设置为5000000字节(约5MB)。
}
});
export default prodConfig;
11.5文件指纹
修改 webpack.base.ts ,把js输出的文件名称格式加上 chunkhash ,把 css 和图片媒体资源输出格式
加上 contenthash
修改webpack.base.ts
rules: [
{
test: /.(ts|tsx)$/,
// 缩小构造域
exclude: /node_modules/,
use: [
// 开启多进程打包。
// 进程启动大概为600ms,进程通信也有开销。
// 只有工作消耗时间比较长,才需要多进程打包
{
loader: 'thread-loader',
options: {
wokers: 4 // 进程数
}
},
'babel-loader']
},
// 处理css
{
test: /\.css$/, // 匹配css文件
use: styleLoadersArray,
},
// 处理less
{
test: /\.less$/, // 匹配less文件
use: [...styleLoadersArray, 'postcss-loader', 'less-loader']
},
// 处理sass
{
test: /\.(scss|sass)$/, // 匹配sass文件
use: [...styleLoadersArray, 'postcss-loader', 'sass-loader']
},
// 处理图片
{
test: /\.(png|jpe?g|gif|svg)$/i, // 匹配图片文件
type: "asset", // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64
}
},
generator: {
filename: 'static/images/[name].[contenthash:8][ext]', // 文件输出目录和命名
},
},
// 处理字体图标
{
test: /.(woff2?|eot|ttf|otf)$/, // 匹配字体图标文件
type: "asset", // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64
}
},
generator: {
filename: 'static/json/[name].[chunkhash:8][ext]', // 文件输出目录和命名
},
},
// 处理媒体
{
test: /.(mp4|webm|ogg|mp3|wav|flac|aac)$/, // 匹配媒体文件
type: "asset", // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64
}
},
generator: {
filename: 'static/media/.[chunkhash:8][ext]', // 文件输出目录和命名
},
},
// 处理json资源
{
// 匹配json文件
test: /\.json$/,
type: "json", // 模块资源类型为json模块
generator: {
// 这里专门针对json文件的处理
filename: "static/json/[name].[hash][ext][query]",
},
},
]
再修改 webpack.prod.ts ,修改抽离 css 文件名称格式:
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:8].css' // 抽离css的输出目录和名称
}),
11.6代码分割
修改 webpack.prod.ts :
optimization: {
......
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就提取出来
}
}
}
},
11.7 tree-shaking清理未使用css
安装依赖
pnpm i purgecss-webpack-plugin glob-all -D
修改webpack.prod.ts
//树摇清理css
const globAll = require('glob-all')
const { PurgeCSSPlugin } = require('purgecss-webpack-plugin')
plugins: [
......
// 只打包这些文件中用到的样式
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/purgecsswebpack-plugin
only: ["dist"],
safelist: {
standard: [/^ant-/] // 过滤以ant-开头的类名,哪怕没用到也不删除
}
}),
],
11.8gzip压缩
安装依赖
pnpm i compression-webpack-plugin -D
修改webpack.prod.ts文件
// 打包时生成gzip文件
new CompressionPlugin({
test: /.(js|css)$/, // 只生成css,js压缩文件
filename: '[path][base].gz', // 文件命名
algorithm: 'gzip', // 压缩格式,默认是gzip
threshold: 10240, // 只有大小大于该值的资源会被处理。默认值是 10k
minRatio: 0.8 // 压缩率,默认值是 0.8
}),