当前位置: 首页 > article >正文

手敲Webpack 5:React + TypeScript项目脚手架搭建实践

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
    }),

http://www.kler.cn/a/374685.html

相关文章:

  • Unity 6 中的新增功能
  • vue3标签中的ref属性如何使用$refs获取元素
  • dify的ChatFlow自定义上传图片并通过HTTP请求到SpringBoot后端
  • Docker 部署 plumelog 最新版本 实现日志采集
  • 蓝牙协议——音量控制
  • 【项目实战】NGINX 实现会话保持
  • Java面试题十四
  • C++中的依赖注入
  • 记录一次企业外部通过ssh 连接数据库的事DBeaver
  • Apache Solr 身份认证绕过导致任意文件读取漏洞复现(CVE-2024-45216)
  • Apache paimon表管理
  • 稀土抗菌剂:食品包装中的安全保障
  • Ubuntu 22.04 的Python3.11.8 安装
  • 本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——8ResNet模型的使用
  • CCNA对学历有要求吗?看看你是否有资格报考
  • Android OpenGL ES详解——模板Stencil
  • 鸿蒙生态给我们带来的机遇和挑战
  • 【CSS/SCSS】@layer的介绍及使用方法
  • 二百七十六、ClickHouse——Hive和ClickHouse非常不同的DWS指标数据SQL语句
  • NPM 包开发与优化全面指南
  • Resnet50进行迁移学习实现图片二分类
  • vue vxeui 上传组件 vxe-upload 全局配置上传方法,显示上传进度,最完美的配置方案
  • 音视频听译:助力多维度沟通与发展的大门
  • 预告帖|在MATLAB/Simulink中调用C语言的几种方法
  • 【neo4j】 neo4j cypher单一语句 optional 可选操作的技巧
  • 【CSS in Depth 2 精译_055】8.3 伪类 :is() 和 :where() 的正确打开方式