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

webpack学习

webpack学习笔记

  • 参考文档:
    Webpack官网中文文档
  • 学习视频:
    Webpack原理与实践

温故webpack打包知识,忘记了就回头看看~ 根据学习视频学习整理,不为一一对应笔记,主要是根据对学习视频知识的梳理~
PS:上学习视频是基于webpack4的,我这边运行时基于webpack5的,不对的地方已经按照官方文档进行定义配置

初始化项目

环境要求:node+npm+webpack

  1. 初始化 npm 项目
npm init -y 
  1. 安装 webpack 和 webpack-cli:
npm install --save-dev webpack webpack-cli
  1. 创建项目
  • 建立webpack.config.js文件
  • 新建src文件夹,新建打包入口文件main.js
    webpack.config.js
const path = require('path');
module.exports = {//定义入口文件entry: './src/main.js',// 定义输出文件名output:{//定义输出名称filename:'bundle.js',//定义输出路径path: path.join(__dirname, "/dist"),}
}

main.js

alert('Hello world!')
  1. 定义打包命令package.json中script
  • 运行npm run build 则打包文件,默认根据webpack.config.js配置打包
  "scripts": {"build": "webpack"}
  1. 在根目录建立index.html文件,通过script标签引入打包的文件(后面通过插件htlm-webpack-plugin自动生成,则删除此文件,直接自动打包到dist文件夹中,并会自动引入打包.js文件)
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Webpack Plugin Demo</title><meta name="viewport" content="width=device-width, initial-scale=1"><script src="dist/bundle.js"></script></head><body></body>
</html>
  1. 安装server运行静态服务器

运行serve则打开当前目录,若有index.html默认运行index.html文件

npm install -g serve

运行

  • 当前开始时运行的自定义的index.html看效果,
serve .
  • 后续运行dist文件夹的自动生成的index.html看效果
serve dist

模块化标准规范

  • 浏览器:ES Module (主流的打包方案)
  • Nodejs: Commonjs(内置的环境系统)

Entry(入口):

Webpack 的打包过程从入口文件开始。入口文件可以是一个或多个,指定了应用程序的起点。

// webpack.config.js
module.exports = {
entry: './src/index.js'
};

Output(输出):

配置打包后的文件输出位置和文件名。

// webpack.config.js
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: __dirname + '/dist'
}
};

Loaders(加载器):

Loaders 让 Webpack 能够处理非 JavaScript 文件!!! 如 CSS、图片、字体等。通过配置 loaders,Webpack 可以将这些文件转换为可以被应用程序使用的模块。
具体每个loader作用以及更多loader查看官网,在这里配置最常见的loader

常见loader如下:

  • style-loader & css-loader
  • file-loader & url-loader
  • html-loader
  • babel-loader
  • 自制loader
  1. 安装依赖
npm i style-loader css-loader file-loader url-loader html-loader babel-loader
  1. 配置应用
const webpack = require('webpack');
const path = require("path");
module.exports = {mode: "none",entry: "./src/main.js",output: {filename: "[name]-[chunkhash:8]-bundle.js",path: path.join(__dirname, "/dist"),},module: {rules: [{test: /\.m?js$/,exclude: /(node_modules|bower_components)/,use: {loader: "babel-loader",options: {//将ES Modules转换为CommonJSpresets: [['@babel/preset-env', {//默认是auto自动,写了commonjs是强制转换为commonjs,此时usedExports无法生效,即tree shaking无法生效modules: 'commonjs'}]],}}},{test: /\.html$/,use: 'html-loader',},{test: /\.css$/,use: [MiniCssExtractPlugin.loader,'css-loader']},{test: /\.png$/,use: {loader: "url-loader",options: {name: '[name].[hash:8].[ext]',outputPath: 'images/',publicPath: 'images/',limit: 10 * 1024,}}}]}
}

自制loader详解

自制markdown-loader为了解析引入.md文件

  1. 安装markdown-it(webpack5用这个解析.md文件)
npm i markdown-it
  1. 新建markdown-loader.js
const markdownIt = require('markdown-it')();module.exports = function markdownLoader(source) {const html = markdownIt.render(source);return `module.exports = ${JSON.stringify(html)}`;
};
  1. 配置使用

const webpack = require('webpack');
const path = require("path");
module.exports = {mode: "none",entry: "./src/main.js",output: {filename: "[name]-[chunkhash:8]-bundle.js",path: path.join(__dirname, "/dist"),},module: {rules: [{test: /\.md$/,use: './markdown-loader'}]}
}

style-loader 和 css-loader

这两个 Loaders 通常一起使用,用于处理 CSS 文件并将其引入到 JavaScript 模块中。

  • css-loader:

解析 CSS 文件中的 @import 和 url() 语法,并将 CSS 转换为 JavaScript 模块。
允许你在 JavaScript 文件中通过 import 或 require 引入 CSS 文件。

  • style-loader:

将 CSS 以

示例配置:

module.exports = {module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader']}]}
};

file-loader 和 url-loader

这两个 Loaders 用于处理文件资源,如图片、字体等。

  • file-loader:

将文件解析为 import 或 require 语句,并返回一个相应的 URL。
将文件复制到输出目录,并根据配置返回相对 URL 或绝对 URL。

  • url-loader:

功能类似于 file-loader,但如果文件小于设定的阈值(以字节为单位),它会将文件内容转换为 Base64 编码的 Data URL,嵌入到生成的 JavaScript 文件中。
超过阈值的文件仍然会使用 file-loader 处理

示例配置:

module.exports = {module: {rules: [{test: /\.(png|jpg|gif)$/,use: [{loader: 'url-loader',options: {limit: 8192, // 8KB 以下的文件会被转为 Base64name: '[name].[hash:8].[ext]'}}]}]}
};

html-loader

  • html-loader:

解析 HTML 文件中的 标签、 标签和其他资源引用,将它们转换为 import 或 require 语句,从而使得这些资源能够被 Webpack 打包。
处理 HTML 文件中的资源依赖,生成正确的资源路径。
示例配置

module.exports = {module: {rules: [{test: /\.html$/,use: 'html-loader'}]}
};

babel-loader

babel-loader:
使用 Babel 将 ES6/ES7/ES8 等现代 JavaScript 语法转换为 ES5,从而兼容更多的浏览器环境。
支持 Babel 插件和预设(presets),如 @babel/preset-env,以便使用最新的 JavaScript 特性和语法。

示例配置:

module.exports = {module: {rules: [{test: /\.js$/,exclude: /node_modules/,use: {loader: 'babel-loader',options: {presets: ['@babel/preset-env']}}}]}
};

总结

  • style-loader & css-loader:处理 CSS 文件,解析并注入到 HTML 中。
  • file-loader & url-loader:处理文件资源(如图片、字体),将文件复制到输出目录并生成相应的 URL,url-loader 可以将小文件内联到 JavaScript 中。
  • html-loader:解析 HTML 文件中的资源引用,生成正确的资源路径。
  • babel-loader:使用 Babel 将现代 JavaScript 语法转换为 ES5,以兼容更多浏览器环境。
    通过使用这些 Loaders,Webpack 可以处理多种类型的文件资源,并将它们整合到打包输出中,提高开发效率和代码兼容性。

Plugins(插件):

插件用于执行更广泛的任务,如优化打包结果、资源管理和环境变量注入。插件比 loaders 更强大,提供了更多功能。
具体每个plugins作用以及更多plugins查看官网,在这里配置最常见的plugins

常见plugins如下:

  • clean-webpack-plugin
  • html-webpack-plugin
  • copy-webpack-plugin
  • mini-css-extract-plugin
  • css-minimizer-webpack-plugin
  • terser-webpack-plugin
  • 自制plugins
  • …更多plugins详见官网
  1. 安装依赖
npm i --save-dev clean-webpack-plugin  html-webpack-plugin  copy-webpack-plugin mini-css-extract-plugin css-minimizer-webpack-plugin terser-webpack-plugin
  1. 配置应用
// clean-webpack-plugin 清除上次生成的dist目录 避免遗留文件
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
// html-webpack-plugin 通过webpack输出html文件  index.html中的script路径引用 是否正常
const HTMLWebpackPlugin = require("html-webpack-plugin");
// copy-webpack-plugin 拷贝不需要打包的目录
const CopyWebpackPlugin = require("copy-webpack-plugin");
//提取css文件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 压缩css
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
//js压缩插件
const TerserPlugin = require('terser-webpack-plugin');
const webpack = require('webpack');
const path = require("path");module.exports = {mode: "none",entry: "./src/main.js",output: {filename: "[name]-[chunkhash:8]-bundle.js",path: path.join(__dirname, "/dist"),},//相当于tree shaking:集中配置webpack当中优化功能optimization: {// 压缩代码 负责【摇掉】他们 开发环境一般不压缩minimize: false,//minimizer默认压缩,配置为数组方式则为自定义配置打包压缩,CssMinimizerPlugin压缩css,TerserPlugin压缩jsminimizer: [new CssMinimizerPlugin(),new TerserPlugin()]},plugins: [new CleanWebpackPlugin(),// 用于生成index.htmlnew HTMLWebpackPlugin({title: "Webpack Plugin Demo",meta: {viewport: "width=device-width, initial-scale=1"},// 按照模版输出// template: "./src/index.html",}),//用于热更新new webpack.HotModuleReplacementPlugin(),//按需加载css文件new MiniCssExtractPlugin({// :8指定长度 推荐使用chunkhash// * hash:项目中任何一个地方改动,打包都会造成全部文件变化// * chunkhash推荐: 根据修改代码处,打包只变化修改的代码处文件名// * contenthash:根据代码修改代码处,修改只变化内容的文件名// filename:'[name]-[hash:8].bundle.css',filename: '[name]-[chunkhash:8].bundle.css',// filename:'[name]-[contenthash].bundle.css',}),// 压缩cssnew CssMinimizerPlugin()]
}

自制plugins详解

自制my-plugins插件,用于清除js文件中的注释!

  • 通在webpack生命周期的钩子中挂载函数实现扩展 !!! 类似事件 webpack给每个环节埋下钩子 挂载不同任务 就可扩展webpack能力。
  • webpack要求我们钩子必须是一个函数或者是一个包含apply方法的对象

配置如下:

const path = require("path");
// 自制插件 plugin 通在webpack生命周期的钩子中挂载函数实现扩展  !!! 类似事件 webpack给每个环节埋下钩子 挂载不同任务 就可扩展webpack能力
//webpack要求我们钩子必须是一个函数或者是一个包含apply方法的对象
//编写一个插件 去除js文件中的注释
class MyPlugin {apply(compiler) {compiler.hooks.emit.tap('MyPlugin', compilation => {//compilation 理解为此次打包的上下文for (const name in compilation.assets) {console.log(`Processing asset: ${name}`);if (name.endsWith(".js")) {const content = compilation.assets[name].source();console.log(`Original content: \n${content}`);const withoutComments = content.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');console.log(`Content without comments: \n${withoutComments}`);compilation.assets[name] = {source: () => withoutComments,//必须的方法size: () => withoutComments.length,};}}});}
}module.exports = {mode: "none",entry: "./src/main.js",output: {filename: "[name]-[chunkhash:8]-bundle.js",path: path.join(__dirname, "/dist"),},plugins: [// 自制插件 用于清除js文件中的注释new MyPlugin(),]
}
  1. 配置使用

const webpack = require('webpack');
const path = require("path");
module.exports = {mode: "none",entry: "./src/main.js",output: {filename: "[name]-[chunkhash:8]-bundle.js",path: path.join(__dirname, "/dist"),},module: {rules: [{test: /\.md$/,use: './markdown-loader'}]}
}

clean-webpack-plugin

  • 作用:在每次构建之前清理 /dist 文件夹,确保输出目录中只有构建过程中生成的文件。
  • 主要功能:
    • 删除旧的文件,防止冗余文件堆积,保持输出目录整洁。
      安装依赖
npm i --save-dev clean-webpack-plugin

配置

const { CleanWebpackPlugin } = require('clean-webpack-plugin');module.exports = {plugins: [new CleanWebpackPlugin()]
};

html-webpack-plugin

  • 作用:生成 HTML 文件,并自动注入打包生成的 JavaScript 和 CSS 文件。

  • 主要功能:

    • 根据模板或默认生成 HTML 文件。
    • 自动注入所有打包生成的资源(例如 JS、CSS 文件)。
    • 支持设置模板、文件名、标题等选项。

    安装依赖

npm i --save-dev html-webpack-plugin

配置

const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {plugins: [new HtmlWebpackPlugin({template: './src/index.html',filename: 'index.html',title: 'My App'})]
};

copy-webpack-plugin

  • 作用:将文件或文件夹从一个位置复制到另一个位置(通常是复制静态资源到输出目录)。

  • 主要功能:

    • 复制不需要进行编译处理的文件,如图片、字体、静态 HTML 等。

    安装依赖

npm i --save-dev copy-webpack-plugin

配置

const CopyWebpackPlugin = require('copy-webpack-plugin');module.exports = {plugins: [new CopyWebpackPlugin({patterns: [{ from: 'public', to: 'dist' }]})]
};

mini-css-extract-plugin

通过MiniCssExtractPlugin插件实现css文件的按需加载。建议css文件超过150kb才考虑是否提取到文件当中,会单独生成一个文件。

  • 作用:将 CSS 提取到单独的文件,而不是嵌入到 JavaScript 中。

  • 主要功能:

    • 提高 CSS 加载速度,减少 JavaScript 文件的体积。
    • 支持按需加载 CSS。

    安装依赖

npm i --save-dev mini-css-extract-plugin

配置

const MiniCssExtractPlugin = require('mini-css-extract-plugin');module.exports = {module: {rules: [{test: /\.css$/,use: [MiniCssExtractPlugin.loader, 'css-loader']}]},plugins: [new MiniCssExtractPlugin({filename: '[name].[contenthash].css'})]
};

css-minimizer-webpack-plugin

  • optimize-css-assets-webpack-plugin
  • css-minimizer-webpack-plugin

PS:由于 optimize-css-assets-webpack-plugin 版本与 webpack 版本之间的兼容性问题。
具体来说,optimize-css-assets-webpack-plugin@6.0.1 需要 webpack@^4.0.0,而我当前的项目中使用的是 webpack@5.91.0。
则不适用安装此,我将安装css-minimizer-webpack-plugin代替。
当然,你也可以使用 --legacy-peer-deps 参数来忽略兼容性检查,但这可能会导致其他问题。

  • 作用:压缩和优化 CSS 文件,减少文件体积,提高加载速度。
  • 主要功能:
    • 使用 cssnano 库进行 CSS 的压缩优化。
    • 通常与 mini-css-extract-plugin 配合使用。
    • 通常与 terser-webpack-plugin配合使用,压缩js文件。
  1. 安装依赖
  • 压缩css
npm i --save-dev css-minimizer-webpack-plugin
  • 压缩js

为了区分环境,使用optimization中minimizer进行,一般默认压缩,配置为数组方式则为自定义配置打包压缩,CssMinimizerPlugin压缩css,则需要安装TerserPlugin压缩js)

npm install --save-dev terser-webpack-plugin
  1. 引入并使用
// 引入依赖压缩css
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
//js压缩插件
const TerserPlugin = require('terser-webpack-plugin');module.exports={// 压缩建议在生产环境才输出,建议放在optimization中minimize统一配置optimization:{//可以由这里控制是否压缩,开发环境设置为false则不压缩,生产环境设置为true开启压缩minimize:true,//minimizer默认压缩,配置为数组方式则为自定义配置打包压缩,CssMinimizerPlugin压缩css,TerserPlugin压缩jsminimizer: [new CssMinimizerPlugin(),new TerserPlugin()]},//这里配置直接压缩不区分环境// plugins:[//   // 压缩css//   new CssMinimizerPlugin()// ]
}

terser-webpack-plugin

  • 作用:压缩和优化 JavaScript 文件,减少文件体积,提高加载速度。
  • 主要功能:
    • 使用 Terser 库进行 JavaScript 的压缩优化。
    • 替代 Webpack 4 之前的 UglifyJS 插件。

安装依赖

npm i --save-dev terser-webpack-plugin

配置

const TerserPlugin = require('terser-webpack-plugin');module.exports = {optimization: {minimize: true,minimizer: [new TerserPlugin()]}
};

总结

  • clean-webpack-plugin:在每次构建之前清理输出目录,保持目录整洁。
  • html-webpack-plugin:生成 HTML 文件并自动注入打包生成的资源。
  • copy-webpack-plugin:复制文件或文件夹到输出目录。
  • mini-css-extract-plugin:将 CSS 提取到单独的文件,提高加载速度。
  • css-minimizer-webpack-plugin:压缩和优化 CSS 文件,减少文件体积。
  • terser-webpack-plugin:压缩和优化 JavaScript 文件,减少文件体积。
    通过使用这些插件,Webpack 可以更高效地处理和优化各种资源,提高构建和加载性能。

webpack-dev-server

自动刷新功能:将打包结果暂时存放在内存当中,没有进入磁盘读写,webpack-dev-server从内存当中将变化读取出来,发送到浏览器,添加–open默认自动打开我们的浏览器,这里添加到script命令里,我配置到dev,运行一次就可以看到一边编码一边预览的环境

  1. 安装依赖
npm install --save-dev webpack-dev-server
  1. 配置运行命令在package.json中
 "scripts": {"dev": "webpack-dev-server --open"}

运行

npm run dev

运行之后,在文件中修改浏览器内容在保存后便会直接变化,无需刷新方可更新

devtools(source-map)

设置属性 devtool: ‘source-map’ 一般效果最好的生成的最慢,约为12种,每种方式效率和效果不同

  • 开发环境偏向选择cheap-module-source-map 转换过后差异过大 首次打包慢无所谓 重写打包就快了
  • 生产环境 选择none 为了不暴露源代码 隐患 调试是开发阶段的事情 或者nosources-source-map
module.exports={devtool: 'cheap-module-source-map'
}

点击查看更多source-map

HMR热更新

Hot Module Replacement 模块热替换或者叫模块热更新。
Webpack 的热更新(Hot Module Replacement, HMR)是一种允许在运行时替换、添加或删除模块,而无需重新加载整个页面的技术。HMR 的核心原理包括模块热替换、依赖管理以及状态保持。以下是 Webpack 热更新的详细实现原理

Webpack 热更新的工作流程:

  1. 检测变更:
  • Webpack 使用文件系统监视工具(如 chokidar)监视源代码文件的变更。当开发者修改文件并保存时,Webpack 侦测到这些变化。
  1. 编译模块:
  • 一旦检测到变更,Webpack 重新编译受影响的模块(模块包括 JavaScript、CSS、模板文件等)。这一步只重新编译变更的部分,而不是整个应用。
  1. 生成补丁:
  • 编译完成后,Webpack 生成更新的模块,并将这些模块打包成补丁(Hot Update Chunk),包含变更的模块和相应的依赖关系。
  1. 通知客户端:
    Webpack 开发服务器(Webpack Dev Server)通过 WebSocket 向客户端(浏览器)发送更新通知。通知内容包括更新的模块 ID 和哈希值等信息。
  2. 应用更新:
  • 客户端收到更新通知后,通过 WebSocket 从开发服务器请求补丁数据。
  • 客户端运行 HMR 运行时逻辑,动态地将新的模块替换进运行中的应用。

Webpack HMR 关键组件

  • Webpack Dev Server
    Webpack Dev Server 是一个开发服务器,提供静态文件服务、实时重载以及 HMR 支持。它在内存中保存最新的编译结果,减少文件系统 I/O 操作,提高性能。

  • HMR Runtime
    HMR 运行时是注入到打包输出中的一段 JavaScript 代码,负责处理模块更新。它通过 WebSocket 与 Webpack Dev Server 通信,接收更新通知并应用补丁。

热更新的应用过程:

  1. 更新检查:
  • HMR Runtime 通过 WebSocket 接收来自 Webpack Dev Server 的更新通知。
  1. 请求更新模块:
  • HMR Runtime 发送请求获取更新模块的补丁数据(包含新的模块代码和更新的哈希值)。
  1. 模块热替换:
  • HMR Runtime 加载新的模块代码,调用模块的 accept 或 dispose 钩子函数,执行模块热替换逻辑。
    accept 钩子函数允许模块在更新时执行自定义逻辑,例如重新渲染组件。
    dispose 钩子函数允许模块在被替换前执行清理工作,例如移除事件监听器或保存状态。

例子:当然也可在本main.js中体会

if (module.hot) {
module.hot.accept('./moduleA.js', function() {
console.log('moduleA updated');
// 处理更新后的逻辑
render();
});module.hot.dispose(function() {
console.log('Cleaning up before module is replaced');
// 清理逻辑,例如移除事件监听器
});
}function render() {
const content = require('./moduleA.js');
document.getElementById('app').innerHTML = content;
}render();

在这个示例中,当 moduleA.js 被更新时,HMR Runtime 会调用 accept 钩子函数重新渲染内容,并在模块被替换前调用 dispose 钩子函数进行清理工作。

总结

Webpack 的热更新机制通过监听文件变化、重新编译受影响模块、生成并应用补丁来实现模块的热替换。
关键组件包括 Webpack Dev Server 和 HMR Runtime,它们共同配合,使得开发者可以在不刷新整个页面的情况下高效地进行模块更新。
通过使用 HMR,开发者可以显著提升开发体验和效率。

开启热更新,配置package.json文件 配置–hot

 "scripts": {"dev": "webpack-dev-server --open --hot"
}

不同环境中的配置

通过运行命令env区别:
打包命令 配置在script上

webpack --env production

实际操作 webpack.common.js 根据env判断环境做相应处理

const config = {
}
//生产环境下的配置if(env==='production'){config.mode = 'prodution'config.devtool = falseconfig.plugin =[...config.plugins,new CleanWebpackPlugin(),new CopyWebpackPlugin(['public'])]}
}

大型项目建议

不同环境对应不同配置文件

  • webpack.common.js 通用配置
  • webpack.dev.js 开发环境配置
  • webpack.prod.js 生产环境配置

webpack-merge合并逻辑

  1. 安装依赖
npm i --save-dev webpack-merge
  1. 配置使用,比如在生产环境配置 webpack.prod.js中:
//通用配置
const common = require('./webpack.common');
//合并配置
const merge = require('webpack-merge');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = merge(common,{mode:'production',plugins: [new CleanWebpackPlugin(),new CopyWebpackPlugin(['public'])]
})

打包命令构建,配置在package.json中的script命令

webpack --config webpack.prod.js

definePlugin注入变量

通常用于不同环境的区别

  1. 在webpack.prod.js定义
const webpack = require('webpack');
module.exports={mode:'none',entry: "./src/main.js",output:{filename: 'bundle.js',},plugins:[new webpack.DefinePlugin({API_BASE_URL:`"https:''api/example.com"`})]
}
  1. 在main.js测试
//可以看到结果https:''api/example.com
console.log(API_BASE_URL);

验证:执行打包npm run build:prod可以看到bundle.js有https://api.example.com

Tree Shaking 摇树

tree shaking:将枯树叶摇落:即意思为自动检测出代码中未引用的代码,移除未用的多余代码。
生产环境默认开启!生产模式默认开启tree shaking功能!
运用

自动开启

这里我在editor.js定义了一个未被引用的console.log

  • 开发环境:运行npm run build:dev打包就打印出来了,未开启tree shaking功能
  • 生产环境:运行npm run build:prod打包就没有打印出来,生产模式默认开启tree shaking功能

1-指定生产环境

或打包时候指定为生产环境:

  • package.json script配置命令
webpack --mode prodution

或者配置中指定mode

module.exports = {mode:'prodution'
}

运行命令

npm run build:prod

2-手动开启tree shaking

主要通过配置 optimization 比如在开发环境下配置

 // 开发环境打包配置
const common = require('./webpack.common');
// v5之前
// const merge = require('webpack-merge');
//v5版本之后 webpack-merge v5 之后的导入方式有所不同,你需要从 webpack-merge 包中导入 merge 函数。
const {merge} = require('webpack-merge');
module.exports = merge(common, {mode: 'development',devtool: 'cheap-module-source-map',//相当于tree shaking:集中配置webpack当中优化功能,下面二者缺一不可optimization: {//只导出使用的模块   负责标记【枯树叶】usedExports: true,//压缩代码 负责【摇掉】他们minimize: true,}
})

运行命令

npm run build:dev

合并模块ConcatenateModules

主要通过在optimization配置concatenateModules:true,同时为了更好地看到效果,关闭掉minimizee:true
作用:尽可能将所有模块合并输出到一个函数中,就不是一个模块对应一个函数了。又名 Scope Hoisting 作用域提升

// 开发环境打包配置
const common = require('./webpack.common');
// v5之前
// const merge = require('webpack-merge');
//v5版本之后 webpack-merge v5 之后的导入方式有所不同,你需要从 webpack-merge 包中导入 merge 函数。
const {merge} = require('webpack-merge');
module.exports = merge(common, {mode: 'development',devtool: 'cheap-module-source-map',//相当于tree shaking:集中配置webpack当中优化功能optimization: {//只导出使用的模块   负责标记【枯树叶】usedExports: true,//压缩代码 负责【摇掉】他们// minimize: true,//这里:合并模块!!concatenateModules: true,}
})

运行查看

npm run buil

Tree Shaking & Babel

webpack发展非常快,有人提出使用了babel-loader,tree shaking就会失效。
原因是 tree shaking使用的前提是ES Modules.交给Webpack打包的代码必须使用ES Modules.而babel-loader是将ES Modules转换成CommonJS。

  • 情况一:babel-loader 会判断是否usedExport,如果有,就禁用ES Module的转换。tree shaking生效
  • 情况二:babel-loader在presets里面配置了modules为commonjs则为强制转换,tree shaking不生效
    具体如下代码:
const webpack = require('webpack');
const path = require("path");
module.exports = {mode: "none",entry: "./src/main.js",output: {filename: "bundle.js",path: path.join(__dirname, "/dist"),},//生产环境 选择none 为了不暴露源代码 隐患 调试是开发阶段的事情 或者nosources-source-mapdevtool: 'cheap-module-source-map',optimization: {//!!!只导出使用的模块  负责标记【枯树叶】 但是使用了babel-loader就看具体情况,强制转换commonjs这次就失效usedExports: true,//压缩代码 负责【摇掉】他们minimize: true,//这里:合并模块!!concatenateModules: true,},module: {rules: [{test: /\.m?js$/,exclude: /(node_modules|bower_components)/,use: {loader: "babel-loader",options: {//将ES Modules转换为CommonJSpresets: [['@babel/preset-env', {//默认是auto自动,写了commonjs是强制转换为commonjs,此时usedExports无法生效,即tree shaking无法生效modules: 'commonjs'}]],}}}]}
}

SideEffects副作用特性

适用前提:确保你的代码没有副作用!! sideEffects一般用于NPM包标记是否有副作用,生产环境下默认开启,开启了之后没有用到的模块就不再会打包。
在optimization中配置

标识是否有副作用

在 Webpack 中,“副作用”(side effects)这个术语也有特定的意义,通常用于描述模块是否对导入它的代码产生额外影响。具体来说,Webpack 会使用 sideEffects 字段来标记一个模块或包是否包含副作用,以便进行更好的优化,例如 Tree Shaking(移除未使用的代码)。

package.json中

{
"name": "my-library",
"version": "1.0.0",
"sideEffects": false
}

在这个例子中,设置 “sideEffects”: false 表示模块没有副作用,Webpack 可以安全地移除未使用的代码部分。如果某些文件确实有副作用,可以指定它们:

{
"name": "my-library",
"version": "1.0.0",
"sideEffects": ["*.css", "*.scss"]
}

这告诉 Webpack 除了 .css 和 .scss 文件外,其他文件都没有副作用。

开启副作用功能

在编程中,“副作用”(side effect)指的是函数或表达式在计算结果之外,还会对程序的其他部分产生影响的情况。通俗一点说,副作用是指除了返回一个值以外,函数或表达式还做了其他事情,这些事情会影响到外部的状态或环境。比如说有个方法Number.propotype.pad = function(){}通过原型改变Number方法这也是副作用,开启了副作用将不再打包这段函数

webpack.dev.js:

optimization:{//开启副作用,生产环境默认开启,没用到的模块不再打包sideEffects: true
}

多入门打包

entry定义为一个对象,一个属性就是打包的一个路径,多个入口打包出多个结果,filename不直接定义为一个固定名称。
但是若是希望打包出来也为多个index.html就需要独立配置,都则将会打包到一个index.html中引入。则需在HtmlWebpackPlugin中定义chunks:[‘index’]
实现步骤

  1. 定义entry多个入口 对象方式
  2. 修改输出多个文件名称区分
  3. 定义输出多个不用名称index.html,多个引入代替一个index.html引入多个
// 开发环境打包配置
const common = require('./webpack.common');
const HTMLWebpackPlugin = require('html-webpack-plugin');
// v5之前
// const merge = require('webpack-merge');
//v5版本之后 webpack-merge v5 之后的导入方式有所不同,你需要从 webpack-merge 包中导入 merge 函数。
const {merge} = require('webpack-merge');
const path = require("node:path");
module.exports = merge(common, {// 1-定义多个入口entry:{main: './src/main.js',hello:'./src/hello.js'},//2-修改输出名称output: {filename: '[name].bundle.js',path: path.join(__dirname, "/dist"),},//3-定义输出多个不同名称index.htmlplugins: [new HTMLWebpackPlugin({title: 'webpack main',filename: 'main.html',//定义chunks连接chunks:['main']}),new HTMLWebpackPlugin({title: 'webpack hello',filename: 'hello.html',//定义chunks连接chunks:['hello']})],mode: 'development',devtool: 'cheap-module-source-map',//相当于tree shaking:集中配置webpack当中优化功能optimization: {//只导出使用的模块   负责标记【枯树叶】usedExports: true,//压缩代码 负责【摇掉】他们  仅仅对于js文件生效 css不生效minimize: true,//这里:合并模块!!concatenateModules: true,//副作用,开启了之后没有用到的模块就不再会打包。sideEffects: true,}
})

不同打包之提取公共模块

代码中会有相同的共用部分造成代码复用率低。
利用optimization中配置splitChunks代码分割为chunks:'all’开启提取打包公用模块

    //相当于tree shaking:集中配置webpack当中优化功能optimization: {//提取公共部分splitChunks:{chunks: 'all'}}

打包查看

npm run build:dev

可看到dist文件中生成了多余的js文件名为二者合一公用模块

动态导入

动态导入按需加载极大节省我们的带宽以及流量,提高响应速度。
事例情况:就是比如点击菜单A显示A内容,菜单B显示B内容。而不是一开始就加载全部AB内容,而是做到按需加载。
具体原理:按照监听菜单瞄点,是什么瞄点加载什么内容

具体原理实现代码:

const render = ()=>{const hash = window.location.hash || "#posts";const mainElements = document.querySelector('.main');mainElements.innerHTML = '';if(hash==='#posts'){import('./posts/post').then(({default:posts})=>{mainElements.appendChild(posts());})}else if(hash==='#album'){import('./posts/album').then(({default:album})=>{mainElements.appendChild(album());})}
}
render();
//监听改变
window.addEventListener('hashchange', render);

魔法注释

通过分包时候添加注释,相同的chunks name就会打包到一起

实现如下:

const render = () => {const hash = window.location.hash || "#posts";const mainElements = document.querySelector('.main');mainElements.innerHTML = '';if (hash === '#posts') {//魔法注释import(/* webpackChunkname:'posts' */ './posts/post').then(({default: posts}) => {mainElements.appendChild(posts());})} else if (hash === '#album') {//魔法注释import(/* webpackChunkname:'album' */ './posts/album').then(({default: album}) => {mainElements.appendChild(album());})}
}
render();
//监听改变
window.addEventListener('hashchange', render);

输出文件名 Hash

启用静态资源,用户则不需要重复请求加载得到这些资源,整体响应速度就会有一个大幅度提升。
为了解决:

  • 缓存失效时间设置的过短,效果则不明显
  • 缓存失效时间设置的过长,没办法及时更新到客户端

生产模式下,文件名使用Hash哈希值,一旦资源文件发生改变,则会一起改变。
对于客户端而言,全新的文件名就是全新的请求,则没有缓存的问题。那缓存时间设置的非常长,也不担心更新问题。

hash模式

为了减少静态资源请求,设置缓存,则减少服务器的请求。

  • 缓存时间设置的短,请求频繁。
  • 缓存时间设置的长,用户不能及时更新

解决方式:设置hash哈希值

  • hash:项目中任何一个地方改动,打包都会造成全部文件变化
  • chunkhash推荐: 根据修改代码处,打包只变化修改的代码处文件名(推荐使用)
  • contenthash:根据代码修改代码处,修改只变化内容的文件名

通过:length指定哈希长度,默认是20位

//提取css文件
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports={output:{filename:'[name]-[chunkhash:8].bundle.js',path: path.join(__dirname, "/dist"),},plugins:[new MiniCssExtractPlugin({filename:'[name]-[chunkhash:8].bundle.css'})]
}

相关文章:

  • 掌握复选框(Checkbox)的奥秘:全选与反选功能实现
  • uniapp封装picker选择器组件,支持关键字查询
  • react快速开始(四)-之Vite 还是 (Create React App) CRA? 用Vite创建项目
  • Docker搭建ELKF日志分析系统
  • GPT-4o:免费且更快的模型
  • C语言 指针——函数指针的典型应用:计算定积分
  • CAD二次开发(8)-探索实现不重启CAD进行热部署代码
  • 算法-分治策略
  • 如何复制文件描述符
  • 解决nvidia驱动和CUDA升级问题
  • for of 和 for in 的区别
  • 二轴机器人大米装箱机:技术创新引领智能包装新潮流
  • 微波电路S参数测量实验方案
  • pyqt5 tablewidget实现excel拖曳填充
  • 关于科技的总结与思考
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • C++类的相互关联
  • CAP 一致性协议及应用解析
  • crontab执行失败的多种原因
  • express + mock 让前后台并行开发
  • Hexo+码云+git快速搭建免费的静态Blog
  • Javascript编码规范
  • js操作时间(持续更新)
  • Linux gpio口使用方法
  • Linux下的乱码问题
  • Promise面试题2实现异步串行执行
  • redis学习笔记(三):列表、集合、有序集合
  • SQLServer插入数据
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 番外篇1:在Windows环境下安装JDK
  • 工作手记之html2canvas使用概述
  • 基于axios的vue插件,让http请求更简单
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 新版博客前端前瞻
  • 转载:[译] 内容加速黑科技趣谈
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • ​你们这样子,耽误我的工作进度怎么办?
  • ​浅谈 Linux 中的 core dump 分析方法
  • ​虚拟化系列介绍(十)
  • ###C语言程序设计-----C语言学习(6)#
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • (0)Nginx 功能特性
  • (3)(3.5) 遥测无线电区域条例
  • (c语言)strcpy函数用法
  • (delphi11最新学习资料) Object Pascal 学习笔记---第7章第3节(封装和窗体)
  • (备忘)Java Map 遍历
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (十六)Flask之蓝图
  • (一)为什么要选择C++
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • ***监测系统的构建(chkrootkit )
  • **python多态