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.pswp.cn/news/274443.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/274443.shtml
英文地址,請注明出處:http://en.pswp.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;也許您只是停留在如何創建簡單的…

C#除法運算

C#中進行算術運算容易牽扯到類型的自動轉換&#xff0c;這種自動轉換稱之為隱式轉換&#xff0c;當然還可以人為的強制轉換 隱式轉換要求&#xff1a;不丟失精度&#xff0c;而且轉換前后都為數值 強制轉換&#xff1a;容易丟失可能會丟失精度 1 namespace 除法運算2 {3 cl…

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 有哪些更新吧…

代碼復審

我們CodingCook復審的是WWW的代碼&#xff0c;他們的項目是時間管理助手&#xff08;TimeLine&#xff09;。只是跟根據自己的經驗來看&#xff0c;不一定準 先說一下整體的感覺。WWW的代碼用了應該是比較符合面向對象的思想&#xff0c;借口&#xff0c;封裝隨處可見&#xff…

圖像標注技巧_保護互聯網上圖像的一個簡單技巧

圖像標注技巧補習 (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 …

內存泄露檢測 vld

VLD是一款開源檢測內存泄露軟件的簡稱&#xff1a;Visual Leak Detector 網站&#xff1a;http://vld.codeplex.com/ 使用&#xff1a; 1. 安裝vld 或者 下載相關 .h&#xff0c;lib&#xff0c;dll 文件 2. 方法很簡單&#xff0c;只要在包含入口函數的.cpp文件中包含vld.h就可…

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.我 被自己的網站困在家里。…

重構與臭豆腐4

重構要繼續&#xff0c;臭豆腐要做。   這個重構中各種提取類&#xff0c;方法&#xff0c;字段&#xff0c;可以方便的理解&#xff0c;如果使用了設置模式就更加邏輯清晰了。切東西也要講究刀法的。 重構可以方便的使用設計模式。設計模式為重構提供了目標。 比如多個if 可…

你構建的代碼為什么這么大?如何優化~

大家好&#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;…