webpack scope hositing 和tree shaking

Scope Hoisting(作用域提升)Tree Shaking(摇树优化) 是现代前端构建中至关重要的概念。它们是构建工具(如 Webpack、Rollup、Vite)用来优化最终打包产物的核心技术。

核心概念快速理解

  • Tree Shaking“消除死代码”。像一个园丁摇动果树,把已经枯萎、不再结果实的树枝(未使用的代码)摇掉。它是一个静态分析过程,在打包时移除 JavaScript 上下文中未引用的代码(export 但未被 import 的部分)。
  • Scope Hoisting“优化模块结构”。它尽可能地将分散的模块合并到一个函数作用域内,然后重命名变量以防止冲突。它的主要目的是减少打包后的函数声明数量、减小文件体积、提升运行时执行效率

1. Tree Shaking(摇树优化)

是什么?

Tree Shaking 是一个术语,通常用于描述在 JavaScript 打包过程中移除未被使用的代码(俗称“死代码”,dead code)的行为。它依赖于 ES2015 模块语法(importexport)的静态结构特性。

为什么需要?

在编写项目时,我们经常会引入整个库(例如 import _ from 'lodash'),但可能只使用了其中一两个函数。如果没有 Tree Shaking,整个 lodash 库都会被完整地打包到最终产物中,导致体积巨大。

工作原理:
  1. 标记:构建工具(如 Webpack)从入口文件开始,分析所有 importexport 语句,构建一个依赖图。
  2. 分析:工具会标记出哪些 export 的代码被其他模块 import 并使用了。
  3. 清除:在最终生成打包文件时,所有未被标记为“已使用”的 export 代码将被安全地剔除。
生效的前提条件:
  1. 必须使用 ES Module(ESM)语法:即使用 importexportCommonJS(requiremodule.exports)无法被可靠地 Tree Shaken,因为它的依赖关系是动态的,无法在构建时静态分析。
    • 有效import { debounce } from 'lodash-es';
    • 无效const debounce = require('lodash/debounce'); (虽然这样写更好,但整个 require 语法树本身不支持摇树)
  2. 编译器不能将 ESM 转换为其他模块规范:例如,Babel 默认配置可能会将 import/export 转译成 CommonJS。你需要确保 Babel 保留 ESM 语法(通常通过设置 @babel/preset-envmodules: false)。
  3. package.json 的 sideEffects 属性
    • 有些模块本身没有导出任何内容,而是会执行一些操作(如 polyfills、CSS 文件)。这些被称为“有副作用”的模块。
    • 如果你在 package.json 中设置 "sideEffects": false,是在告诉打包工具:“我这个包里的所有文件都是纯的,没有副作用,你可以安全地对它们进行 Tree Shaking”。
    • 如果你的包有副作用文件,需要列出它们:"sideEffects": ["./src/some-side-effectful-file.js", "*.css"],以防止它们被意外移除。
示例:

假设我们有一个 math.js 库:

// math.js
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b; // 假设这个函数未被使用
export const pi = 3.14159; // 假设这个常量未被使用

在我们的主文件中:

// main.js
import { add } from './math.js';
console.log(add(1, 2));

经过 Tree Shaking 后,打包产物将不包含 multiplypi 的代码,最终体积更小。


2. Scope Hoisting(作用域提升)

是什么?

在 Webpack 等工具中,每个模块通常会被包裹在一个函数中(Webpack 称之为“模块包装函数”)。这是为了实现模块化,但会带来一些性能开销。Scope Hoisting 会尽可能地将所有模块合并到一个作用域中,而不是将它们放在单独的模块函数里。

为什么需要?
  1. 减少体积:消除大量模块包装函数的代码本身就能减小文件体积。
  2. 提升运行速度
    • 减少函数声明:JavaScript 引擎执行代码时,调用一个函数的开销比执行内联代码要大。
    • 改善压缩效果:变量被合并到一个作用域后,压缩工具(如 Terser)可以更好地重命名变量,实现更高效的压缩。
工作原理:

构建工具会分析模块之间的依赖关系,并将它们尽可能地“内联”到同一个作用域中。它会智能地重命名变量以避免冲突。

示例(简化概念):

没有 Scope Hoisting 的打包产物可能看起来像:

// 很多这样的模块包装函数
(function(module, __webpack_exports__, __webpack_require__) {"use strict";__webpack_require__.r(__webpack_exports__);/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);console.log(Object(_math__WEBPACK_IMPORTED_MODULE_0__["add"])(1, 2));
}),
(function(module, __webpack_exports__, __webpack_require__) {"use strict";__webpack_require__.r(__webpack_exports__);/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });const add = (a, b) => a + b;const multiply = (a, b) => a * b;
})

启用 Scope Hoisting 后,产物可能被优化为:

// 模块被合并到一个作用域,变量被重命名
const $add$ = (a, b) => a + b;
// ... multiply 可能被 Tree Shaken 掉 ...
console.log($add$(1, 2));

可以看到,后者没有了函数包装,代码更紧凑,执行效率更高。


两者的关系与区别

特性Tree ShakingScope Hoisting
主要目标移除未使用的代码,减小体积优化模块结构,减小体积并提升运行性能
工作阶段主要在代码压缩(Minification)阶段主要在模块连接(Module Concatenation)阶段
关系它们是互补的优化技术。Scope Hoisting 将模块合并到一个作用域,这为 Tree Shaking 提供了更好的基础来识别未使用的变量和函数。

协同工作流程:

  1. Scope Hoisting 首先将许多模块内联到同一个作用域中。
  2. 然后,Tree Shaking 和代码压缩工具(如 Terser)在这个扁平化的作用域中进行静态分析,能更轻松地发现和移除那些未被引用的变量和函数。

如何在 Webpack 中启用?

  • Tree Shaking
    • production 模式下(mode: 'production')是默认启用的。Webpack 会自动使用 TerserPlugin 进行压缩和 Tree Shaking。
    • 确保你的代码和依赖使用 ES Module 语法。
  • Scope Hoisting
    • production 模式下也是默认启用的。Webpack 内部使用 ModuleConcatenationPlugin 来实现这一功能。
    • 在某些情况下(如动态导入),Webpack 无法进行作用域提升,它会安全地回退到传统的模块包装函数。

总结

优化解决了什么问题?带来的好处
Tree Shaking引入了整个库但只使用一小部分减小打包体积
Scope Hoisting模块包装函数带来的体积和性能开销减小打包体积提升运行时性能

它们是现代前端构建流程的基石,通过协同工作,共同打造出体积更小、性能更优的应用程序 bundle。要最大化利用它们,关键在于编写“可摇树”的代码(使用 ESM 语法)和正确配置库的 sideEffects 属性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/98325.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/98325.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手写React状态hook

在日常开发中,我们经常用到 React 的状态管理 Hook:useState 和 useReducer。 但你有没有想过:这些 Hook 内部是怎么实现的?为什么调用 setState 之后组件会重新渲染? 今天我们就来从零手写 useState 和 useReducer&am…

力扣hot100:相交链表与反转链表详细思路讲解(160,206)

问题描述核心思路:双指针交替遍历算法思想: 使用两个指针 pa 和 pb 分别从链表A和链表B的头节点出发,同步向后遍历。当任一指针走到链表末尾时,将其重定位到另一链表的头节点继续遍历。若两链表相交,pa 和 pb 最终会在…

跨平台游戏引擎 Axmol-2.8.1 发布

所有使用 axmol-2.8.0 的开发者都应更新至此版本 Axmol 2.8.1 版本是一个以错误修复和功能改进为主的次要 LTS 长期支持版本,发布时间: 2025 年 9 月 5 日 🙏感谢所有对 axmol 项目的贡献者,包括财务赞助者:scorewarrior、peter…

通过PXE的方式实现Ubuntu 24.04 自动安装

PXE自动化安装Ubuntu 24.04的配置文件之前都是通过PXE来自动化安装Redhat系列的,例如:Rocky9、Rocky10、CentOS7、银河麒麟 Kylin-V10、Kylin-V11、OpenEuler 24.03等。现在安装Ubuntu系列的跟红帽的不太一样,所以在这里介绍下。创建三个文件…

AOSP Framework开发的一些超方便的快捷命令

在系统源码中发现的一些命令和快捷方式。我们在编译源码之前执行的source build/envsetup.sh,通过cat build/envsetup.sh发现如下命令 - lunch: lunch <product_name>-<build_variant>Selects <product_name> as the product to build, and <build_…

【Protues仿真】基于AT89C52单片机的数码管驱动事例

目录 0案例视频效果展示 1 AT89C52单片机驱动单个数码管 1.1 数码管基础知识 1.1.1外观与引脚 1.1.2 共阴(CC) vs 共阳(CA) 1.1.3段码表(以数字1为例) 1.1.4驱动方式A. 直连IO(最简单,占用IO多)一个段一根线,共阴或共阳公共端固定接GND/VCC。适合单个数码管、…

01-Redis 发展简史与核心定位解析:从诞生到三大产品矩阵

目录引言一、Redis 的起源与发展&#xff1a;从定制工具到开源生态二、Redis 的核心定位&#xff1a;不止是缓存的多面手三、Redis 三大产品矩阵&#xff1a;按需选择的完整解决方案3.1 Redis Open Source&#xff08;社区版&#xff09;&#xff1a;入门与轻量场景首选3.2 Red…

记录jilu~

centos1、安装最小版Linux 安装必要工具yum -install -y epel-releaseyum -install -y net-toolsyum -install -y vim2、修改hostname hostnamectl net-hostname newhostname3、网络配置文件&#xff0c;网关 &#xff0c; 使用ip &#xff0c;dns。。/etc/sysconfig/network-s…

【Linux基础】fdisk命令详解:从入门到精通的磁盘分区管理完全指南

目录 前言 1 fdisk命令概述 1.1 什么是fdisk 1.2 fdisk的应用场景 1.3 fdisk与其他分区工具的比较 2 fdisk命令的安装与基本语法 2.1 在不同Linux发行版中安装fdisk 2.2 fdisk的基本语法 3 fdisk命令参数详解 3.1 主要参数说明 3.2 交互式命令 4 fdisk操作流程详解…

Flowable 工作流引擎

1、核心类 Flowable 引擎通过 ProcessEngine 作为总入口点&#xff0c;提供了多个核心服务接口&#xff0c;每个服务都负责特定的功能领域&#xff1a;服务名称 (Service Name)主要功能 (Main Functionality)关键操作 (Key Operations)RepositoryService管理流程定义和部署&…

(RDFS)随机深度特征选择方法解释:简而言之,RDFS主要针对的是恶意的服务器,它建立在客户端是诚实的前提下。

1. 随机深度特征选择是怎么实现的&#xff1f;随机深度特征选择 是一种在分布式机器学习&#xff08;特别是联邦学习&#xff09;中用于保护客户端数据隐私的技术。它的核心思想是&#xff1a;在每一轮训练中&#xff0c;每个客户端随机选择模型的一个子集&#xff08;即“深度…

C++20格式化字符串:std::format的使用与实践

在C编程中&#xff0c;字符串格式化是一项常见的任务。在C20引入std::format之前&#xff0c;开发者通常依赖于一些传统的解决方案&#xff0c;如printf系列函数、sstream&#xff0c;或者第三方库如boost.format。然而&#xff0c;这些方法在代码可读性、类型安全性和灵活性方…

【漏洞复现】CVE-2025-8088|WinRAR 路径穿越漏洞:从原理到蓝屏攻击全流程

【漏洞复现】CVE-2025-8088&#xff5c;WinRAR 路径穿越漏洞&#xff1a;从原理到蓝屏攻击全流程 前言 WinRAR 作为 Windows 平台最常用的压缩管理工具之一&#xff0c;几乎是每台电脑的 “标配软件”。但在 2025 年 8 月&#xff0c;一款影响范围覆盖 WinRAR 0 至 7.12 全版本…

uniapp中使用echarts并且支持pc端的拖动、拖拽和其他交互事件

npm install echarts -D ​ // "echarts": "^5.3.2", [推荐版本] // "zrender": "^5.3.2" [如果报错的话就安装这个]<template><view class"container"><view id"myChart" class"chart"…

Qt中QProxyStyledrawControl函数4个参数的意义

Qt中QProxyStyle::drawControl函数4个参数的意义 我们来详细解释一下 Qt 中 QProxyStyle::drawControl 函数的四个参数。 这个函数是 Qt 样式系统中的一个核心方法&#xff0c;用于绘制标准 UI 元素&#xff08;如按钮、复选框、菜单栏等&#xff09;。当你继承 QProxyStyle 并…

idf-esp32 PWM呼吸灯(LEDC头文件)

相关宏和变量#define LED_PIN GPIO_NUM_3 #define LEDC_CHANNEL LEDC_CHANNEL_0 #define LEDC_TIMER LEDC_TIMER_0 #define LEDC_MODE LEDC_LOW_SPEED_MODE #define LEDC_DUTY_RES LEDC_TIMER_13_BIT // 2^13 8192级亮度 #define LEDC_FREQUENCY 50…

PLC_博图系列☞基本指令”S_ODTS:分配保持型接通延时定时器参数并启动“

PLC_博图系列☞基本指令”S_ODTS&#xff1a;分配保持型接通延时定时器参数并启动“ 文章目录PLC_博图系列☞基本指令”S_ODTS&#xff1a;分配保持型接通延时定时器参数并启动“背景介绍S_ODTS&#xff1a; 分配保持型接通延时定时器参数并启动说明参数脉冲时序图示例关键字&a…

OneCode 可视化揭秘系列(三):AI MCP驱动的智能工作流逻辑编排

OneCode 可视化揭秘系列&#xff08;三&#xff09;&#xff1a;AI MCP驱动的智能工作流逻辑编排 引言 在前两篇系列博文中&#xff0c;我们详细探讨了OneCode可视化动作的基础配置与界面设计&#xff0c;以及组件交互与数据流管理。在本篇文章中&#xff0c;我们将深入剖析逻辑…

TypeORM、Sequelize、Hibernate 的优缺点对比:新手常见 SQL 与 ORM 踩坑总结

1. ORM 与关系型数据库&#xff08;MySQL、PostgreSQL&#xff09; 的使用 SQL 语句编写&#xff08;JOIN、GROUP BY、索引使用、事务控制&#xff09;与 ORM 映射&#xff08;如 Sequelize、TypeORM、Hibernate&#xff09;之间的差异会让新手非常纠结&#xff1b;尤其是理解…

JavaScript 创建型设计模式详解

1. 单例模式1.1. 使用场景在前端开发中&#xff0c;全局状态管理、配置信息、数据库连接等往往需要在应用中只存在一个实例&#xff0c;避免多次实例化带来的数据不一致性。例如&#xff0c;在一个前端应用中&#xff0c;全局的 loading 状态通常需要一个单例模式来确保其唯一性…