Rollup 与 Webpack 的 Tree-shaking

大家好,我是若川。我持续组织了近一年的源码共读活动,感兴趣的可以 点此扫码加我微信 lxchuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。另外:目前建有江西|湖南|湖北籍前端群,可加我微信进群。

d98c91f0cd1449ef7ecb16bae45738a0.png

Rollup 与 Webpack 的 Tree-shaking

http://zoo.zhengcaiyun.cn/blog/article/tree-shaking

Rollup 和 Webpack 是目前项目中使用较为广泛的两种打包工具,去年发布的 Vite 中打包所依赖的也是 Rollup;在对界面加载效率要求越来越高的今天,打包工具最终产出的包体积也影响着开发人员对工具的选择,所以对 Tree-shaking 的支持程度和配置的便捷性、有效性就尤为重要了。本文就来简单分析下两者 Tree-shaking 的流程和效果差异。

Tree-shaking 的目的

Tree-shaking 的目标只有一个,去除无用代码,缩小最终的包体积,至于什么算是无用代码呢?主要分为三类:

  1. 代码不会被执行,不可到达

  2. 代码执行的结果不会被用到

  3. 代码只会影响死变量(只写不读) Tree-shaking 的目的就是将这三类代码在最终包中剔除,做到按需引入。

为什么 Tree-shaking 需要依赖 ES6 module

ES6 module 特点:

  • 只能作为模块顶层的语句出现

  • import 的模块名只能是字符串常量

  • import 之后是不可修改的 例如,在使用 CommonJS 时,必须导入完整的工具 (tool) 或库 (library) 对象,且可带有条件判断来决定是否导入。

// 使用 CommonJS 导入完整的 utils 对象
if (hasRequest) {const utils = require( 'utils' );
}

但是在使用 ES6 模块时,无需导入整个 utils 对象,我们可以只导入我们所需使用的 request 函数,但此处的 import 是不能在任何条件语句下进行的,否则就会报错。

// 使用 ES6 import 语句导入 request 函数
import { request } from 'utils';

ES6 模块依赖关系是确定的,和运行时的状态无关,因此可以进行可靠的静态分析,这就是 Tree-shaking 的基础。

静态分析就是不执行代码,直接对代码进行分析;在 ES6 之前的模块化,比如上面提到的 CommonJS ,我们可以动态 require 一个模块,只有执行后才知道引用的什么模块,这就使得我们不能直接静态的进行分析。

Wepack5.x Tree-shaking 机制

Webpack 2 正式版本内置支持 ES2015 模块(也叫做 harmony modules)和未使用模块检测能力。Webpack 4 正式版本扩展了此检测能力,通过 package.json 的  "sideEffects"  属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 "pure (纯正 ES2015 模块)",由此可以安全地删除文件中未使用的部分。Webpack 5 中内置了 terser-webpack-plugin 插件用于 JS 代码压缩,相较于 Webpack 4 来说,无需再额外下载安装,但如果开发者需要增加自定义配置项,那还是需要安装。

Wepack 自身在编译过程中,会根据模块的 importexport 依赖分析对代码块进行打标。

/*** @param {Context} context context* @returns {string|Source} the source code that will be included as initialization code*/getContent({ runtimeTemplate, runtimeRequirements }) {runtimeRequirements.add(RuntimeGlobals.exports);runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);// 未使用的模块, 在代码块前增加 unused harmony exports 注释标记const unusedPart =this.unusedExports.size > 1? `/* unused harmony exports ${joinIterableWithComma(this.unusedExports)} */\n`: this.unusedExports.size > 0? `/* unused harmony export ${first(this.unusedExports)} */\n`: "";const definitions = [];const orderedExportMap = Array.from(this.exportMap).sort(([a], [b]) =>a < b ? -1 : 1);// 对 harmony export 进行打标for (const [key, value] of orderedExportMap) {definitions.push(`\n/* harmony export */   ${JSON.stringify(key)}: ${runtimeTemplate.returningFunction(value)}`);}// 对 harmony export 进行打标const definePart =this.exportMap.size > 0? `/* harmony export */ ${RuntimeGlobals.definePropertyGetters}(${this.exportsArgument}, {${definitions.join(",")}\n/* harmony export */ });\n`: "";return `${definePart}${unusedPart}`;}

上面是从 Webpack 中截取的打标代码,可以看到主要会有两类标记,harmony exportunused harmony export 分别代表了有用与无用。标记完成后打包时 Teser 会将无用的模块去除。

Rollup Tree-shaking 机制

以下是 rollup 2.77.2 版本的 package.json 文件,我们可以看下它的主要依赖;

{"name": "rollup","version": "2.77.2","description": "Next-generation ES module bundler","main": "dist/rollup.js","module": "dist/es/rollup.js","typings": "dist/rollup.d.ts","bin": {"rollup": "dist/bin/rollup"},"devDependencies": {"@rollup/plugin-alias": "^3.1.9","@rollup/plugin-buble": "^0.21.3","@rollup/plugin-commonjs": "^22.0.1","@rollup/plugin-json": "^4.1.0","@rollup/plugin-node-resolve": "^13.3.0","@rollup/plugin-replace": "^4.0.0","@rollup/plugin-typescript": "^8.3.3","@rollup/pluginutils": "^4.2.1","acorn": "^8.7.1", // 生成 AST 语法树"acorn-jsx": "^5.3.2", // 针对 jsx 语法分析"acorn-walk": "^8.2.0", // 递归生成对象"magic-string": "^0.26.2", // 语句的替换......,},
......
}

想要详细了解Acorn:A tiny, fast JavaScript parser, written completely in JavaScript.可查看(https://github.com/acornjs/acorn),Magic-string,可查看(https://github.com/rich-harris/magic-string#readme) 。rollup源码中各个模块的执行顺序大致如下图,这也基本表明了它的分析流程。

9115cd6e4737bc427cde276149acecd4.png

与 Webpack 不同的是,Rollup 不仅仅针对模块进行依赖分析,它的分析流程如下:

  1. 从入口文件开始,组织依赖关系,并按文件生成 Module

  2. 生成抽象语法树(Acorn),建立语句间的关联关系

  3. 为每个节点打标,标记是否被使用

  4. 生成代码(MagicString+ position)去除无用代码

Rollup 的优势

  1. 它支持导出 ES 模块的包。

  2. 它支持程序流分析,能更加正确的判断项目本身的代码是否有副作用。

两个 Case

  1. 案例1:Import  但未调用,不可消除

import pkgjson from '../package.json';export function getMeta (version: string) {return {lver: version || pkgjson.version,}
}

编译后整个 package.json 都被打了进来,代码块如下:

var name = "@zcy/xxxxx-sdk";
var version$1 = "0.0.1-beta";
var description = "";
var main = "lib/index.es.js";
var module$1 = "lib/index.cjs.js";
var browser = "lib/index.umd.js";
var types = "lib/index.d.ts";
var scripts = {test: "jest --color  --coverage=true",doc: "rm -rf doc && typedoc --out doc ./src",.....
};
var repository = {type: "git",url: "......"
};
var author = "";
var license = "ISC";
var devDependencies = {"@babel/core": "^7.15.5","@babel/preset-env": "^7.15.4","@babel/runtime-corejs3": "^7.11.2","@types/jest": "^24.9.1","@typescript-eslint/eslint-plugin": "^2.34.0","@typescript-eslint/parser": "^2.34.0","babel-loader": "^8.2.2",eslint: "^6.8.0","eslint-config-alloy": "^3.7.2",jest: "^24.9.0","lodash.camelcase": "^4.3.0",path: "^0.12.7",prettier: "^1.19.1",rollup: "^1.32.1",...
.  
};
var dependencies = {"@babel/plugin-transform-runtime": "^7.10.5","@rollup/plugin-json": "^4.1.0","core-js": "^3.6.5"
};
var sideEffects = false;
var pkgjson = {name: name,version: version$1,description: description,main: main,module: module$1,browser: browser,types: types,scripts: scripts,repository: repository,author: author,license: license,devDependencies: devDependencies,dependencies: dependencies,sideEffects: sideEffects,
};

未 import 的部分可消除

import { version } from '../package.json';export function getMeta (ver: string) {return {lver: ver || version,}
}

编译后可以发现,version 作为一个常量被单独打包进来;代码块如下:

var version$1 = "0.0.1-beta";
  1. 案例2: 变量影响了全局变量

window.utm = 'a.b.c';

即使 utm 没有任何地方被使用到,在编译打包的过程中,上述代码也不能被去除。因此我们可以得出结论:

  1. 在 import 三方工具库、组件库时不要全量 import。

  2. 设置或改动全局变量需谨慎。

Vue3 针对 Tree-shaking 所做的优化

在 Vue2.x 中,你一定见过以下引入方式:

import Vue from 'vue'Vue.nextTick(() => {// 一些和 DOM 有关的东西
})

很可惜的是,像 Vue.nextTick() 这样的全局 API 是不支持 Tree-shaking 的,因为它并没有被单独 export;无论 nextTick 方法是否被实际调用,都会被包含在最终的打包产物中。但在 Vue3,针对全局和内部 API 进行了改造。如果你想更详细的了解 Vue3.x 全局 API Tree-shaking 带来的改动,可以查看这里,里面详细列出了不再兼容的 API,以及在内部帮助器及插件中的使用变化。

有了这些能力之后,我们可以不再过于关注框架总体的体积了,因为按需打包使得我们只需要关注那些我们已经使用到的功能和代码。

最终效果对比

先分别来看下两种打包工具的配置;

webpack.config.js :

const webpack = require('webpack');
const path = require('path');
// 删除 const UglifyJsPlugin = require('uglifyjs-webpack-plugin');module.exports = {entry: path.join(__dirname, 'src/index.ts'),output: {filename: 'webpack.bundle.js'},module: {rules: [{test: /\.(js|ts|tsx)$/,exclude: /(node_modules|bower_components|lib)/,use: {loader: 'babel-loader',options: {presets: ['@babel/preset-env']}}},{test: /\.tsx?$/,use: 'ts-loader',exclude: /(node_modules|lib)/,},]},resolve: {extensions: ['.tsx', '.ts', '.js'],},optimization: { // tree-shaking 优化配置usedExports: true,},plugins: [new webpack.optimize.ModuleConcatenationPlugin()]
}

rollup.config.js :

import resolve from "rollup-plugin-node-resolve";
import commonjs from "rollup-plugin-commonjs";
import typescript from "rollup-plugin-typescript2";
import babel from "rollup-plugin-babel";
import json from "rollup-plugin-json";
import { uglify } from 'rollup-plugin-uglify'export default {input: "src/index.ts",output: [{ file: "lib/index.cjs.js", format: "cjs" },],treeshake: true, // treeshake 开关plugins: [json(),typescript(),resolve(),commonjs(),babel({exclude: "node_modules/**",runtimeHelpers: true,sourceMap: true,extensions: [".js", ".jsx", ".es6", ".es", ".mjs", ".ts", ".json"],}),uglify(),],
};

最后来看下打包结果的对比。结果发现,本项目在配置  sideEffects:false  前后时长和体积没有明显变化。

对比Tree-Shaking 前体积Tree-Shaking 后体积打包时长
webpack(5.52.0)46kb44kb4.8s
rollup(1.32.1)24kb18kb3.7s

另,上述打包效果中的项目是 sdk 工具包。

结束语

你如果想了解 Rollup 会打包更快的原因,可以查看我之前发布的文章《Vite 特性和部分源码解析 》(https://www.zoo.team/article/about-vite)。关于 Tree-shaking 的问题也欢迎你在下面留言讨论。

推荐阅读

《Rollup源码解析》(https://juejin.cn/post/7021115814870810660)

Rollup Tree-shaking 机制 (https://www.rollupjs.com/guide/introduction#tree-shaking)

e7cc89037956d82ad3102f7ae5064e2d.gif

················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇,在知乎、掘金收获超百万阅读。从2014年起,每年都会写一篇年度总结,已经坚持写了8年,点击查看年度总结。同时,最近组织了源码共读活动,帮助5000+前端人学会看源码。公众号愿景:帮助5年内前端人走向前列。

bbbc2090c3391d4f8d255dbe831f22b4.jpeg

扫码加我微信 lxchuan12、拉你进源码共读

今日话题

目前建有江西|湖南|湖北 籍 前端群,想进群的可以加我微信 lxchuan12 进群。分享、收藏、点赞、在看我的文章就是对我最大的支持~

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

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

相关文章

聚类与分类的主要区别在于:_经验在于细节:分析流服务的用户体验

聚类与分类的主要区别在于&#xff1a;看不见的差异 (The Invisible Difference) When app markets mature the overlap in features and designs grows closer as they catch up and copy each other. The more similar the apps are to one another, the more important the …

asp.net 动态创建TextBox控件 如何加载状态信息

接着上文Asp.net TextBox的TextChanged事件你真的清楚吗&#xff1f; 这里我们来说说状态数据时如何加载的。虽然在Control中有调用状态转存的方法&#xff0c;但是这里有一个判断条件 if (_controlState > ControlState.ViewStateLoaded) 一般的get请求这里的条件是不满足…

从零实现一个迷你 Webpack

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

ios 刷新遮罩遮罩_在Adobe XD中进行遮罩的3种方法

ios 刷新遮罩遮罩Are you new to Adobe XD? Or maybe you’re just stuck on how to create a simple mask? Here are 3 quick tips for how to mask your photos and designs in Adobe XD.您是Adobe XD的新手吗&#xff1f; 或者&#xff0c;也许您只是停留在如何创建简单的…

Vite 4.0 正式发布!

源码共读我新出了&#xff1a;第40期 | vite 是如何解析用户配置的 .env 的链接&#xff1a;https://www.yuque.com/ruochuan12/notice/p40也可以点击文末阅读原文查看&#xff0c;欢迎学习记笔记~12 月 9 日&#xff0c;Vite 4.0 正式发布。下面就来看看 Vite 4.0 有哪些更新吧…

图像标注技巧_保护互联网上图像的一个简单技巧

图像标注技巧补习 (TUTORIAL) Have you ever worried about sharing your images on the Internet? Anytime you upload something to the web you risk the chance of your work being used (without permission) by another.您是否曾经担心过要在Internet上共享图像&#xf…

【VueConf 2022】尤雨溪:Vue的进化历程

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

WCF netTcpBinding寄宿到IIS7

config配置文件不多说 <?xml version"1.0" encoding"utf-8" ?> <configuration><system.serviceModel><behaviors><serviceBehaviors><behavior name"myBehavior"><serviceMetadata/></behavior…

ar软件测试工具_如何为用户测试制作快速的AR原型

ar软件测试工具We had a project recently with an element of AR-based interaction, which it turned out was impossible to create as a prototype in either Invision or Framer (our current stack). This had a massive impact on our ability to test with users in a …

未来ui设计的发展趋势_2025年的未来UI趋势?

未来ui设计的发展趋势Humans are restless.人类是不安的。 Once we find something that works, we get used to it and we crave the next big thing. The next innovation. When will the future finally arrive? And when it does, how long will it take us to get used …

Monorepo 在网易的工程改造实践

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

这一年,Vue.js 生态开源之旅带给我很大收获~

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

CSSyphus:烦躁不安的烦恼设计指南。

I’m trapped at home with my website. Or maybe it’s trapped at home with me. While some are using the weird lump of time provided by lockdown to indulge in baking, dancing, painting, singing, I’m using it to play around with code.我 被自己的网站困在家里。…

你构建的代码为什么这么大?如何优化~

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

用户体验需求层次_需求和用户体验

用户体验需求层次Shortly after the start of 2020 I led the development of a new website, and it went live in August. A week before the deployment, I paused development and took a step back in order to write about the project. Taking that pause, that step ba…

VMwareWorkstation设置U盘启动(或U盘使用)

最近在工作中&#xff0c;经常要和LINUX部署打交道&#xff0c;一般在生产环境部署之前需要在自己的机器上进行测试。比如使用U盘安装操作系统等。 在机器上安装了VMware Workstation9.0&#xff0c;运行多个测试虚拟机。理由所当然的要使用此做一些操作系统部署&#xff0c;…

类从未使用_如果您从未依赖在线销售,如何优化您的网站

类从未使用初学者指南 (A beginner’s guide) If you own a small business with a store front, you might have never had to rely on online sales. Maybe you’re a small clothing store or a coffee shop. You just made that website so people could find you online, …

狼书三卷终大成,狼叔亲传Node神功【留言送书】

大家好&#xff0c;我是若川。之前送过N次书&#xff0c;可以点此查看回馈粉丝&#xff0c;现在又和博文视点合作再次争取了几本书&#xff0c;具体送书规则看文末。众所周知&#xff0c;我在参加掘金人气作者打榜活动&#xff08;可点击跳转&#xff09;&#xff0c;需要大家投…

程序详细设计之代码编写规范_我在不编写任何代码的情况下建立了一个设计策划网站

程序详细设计之代码编写规范It’s been just over a month since MakeStuffUp.Info — my first solo project as an independent Creator; was released to the world. It was not a big project or complicated in any way, it’s not even unique, but I’m thrilled where …

偷偷告诉你们一个 git 神器 tig,一般人我不告诉TA~

大家好&#xff0c;我是若川。众所周知&#xff0c;我参加了掘金创作者人气作者投票活动&#xff0c;最后3天投票。今天可投28票&#xff0c;明天32票&#xff0c;后天36票&#xff08;结束&#xff09;。投票操作流程看这里&#xff1a;一个普通小前端&#xff0c;将如何再战掘…