摘要:升級(jí)之后,項(xiàng)目的壓縮包并沒(méi)有什么明顯變化。這里可以參考下阮老師介紹的基本語(yǔ)法的循環(huán)是通過(guò)遍歷器迭代的,循環(huán)數(shù)組時(shí)并非是,然后通過(guò)下標(biāo)尋值。樓主好奇為什么不能消除未引用的類(lèi)。樓主我的代碼沒(méi)什么副作用啊。
本文將探討tree-shaking在當(dāng)下(webpack@3, babel@6 以下)的現(xiàn)狀,以及研究為什么tree-shaking依舊舉步維艱的原因,最終總結(jié)當(dāng)下能提高tree-shaking效果的一些手段。
Tree-Shaking這個(gè)名詞,很多前端coder已經(jīng)耳熟能詳了,它代表的大意就是刪除沒(méi)用到的代碼。這樣的功能對(duì)于構(gòu)建大型應(yīng)用時(shí)是非常好的,因?yàn)槿粘i_(kāi)發(fā)經(jīng)常需要引用各種庫(kù)。但大多時(shí)候僅僅使用了這些庫(kù)的某些部分,并非需要全部,此時(shí)Tree-Shaking如果能幫助我們刪除掉沒(méi)有使用的代碼,將會(huì)大大縮減打包后的代碼量。
Tree-Shaking在前端界由rollup首先提出并實(shí)現(xiàn),后續(xù)webpack在2.x版本也借助于UglifyJS實(shí)現(xiàn)了。自那以后,在各類(lèi)討論優(yōu)化打包的文章中,都能看到Tree-Shaking的身影。
許多開(kāi)發(fā)者看到就很開(kāi)心,以為自己引用的elementUI、antd 等庫(kù)終于可以刪掉一大半了。然而理想是豐滿的,現(xiàn)實(shí)是骨干的。升級(jí)之后,項(xiàng)目的壓縮包并沒(méi)有什么明顯變化。
我也遇到了這樣的問(wèn)題,前段時(shí)間,需要開(kāi)發(fā)個(gè)組件庫(kù)。我非常納悶我開(kāi)發(fā)的組件庫(kù)在打包后,為什么引用者通過(guò)ES6引用,最終依舊會(huì)把組件庫(kù)中沒(méi)有使用過(guò)的組件引入進(jìn)來(lái)。
下面跟大家分享下,我在Tree-Shaking上的摸索歷程。
Tree-Shaking的原理這里我不多冗余闡述,直接貼百度外賣(mài)前端的一篇文章:Tree-Shaking性能優(yōu)化實(shí)踐 - 原理篇。
如果懶得看文章,可以看下如下總結(jié):
ES6的模塊引入是靜態(tài)分析的,故而可以在編譯時(shí)正確判斷到底加載了什么代碼。
分析程序流,判斷哪些變量未被使用、引用,進(jìn)而刪除此代碼。
很好,原理非常完美,那為什么我們的代碼又刪不掉呢?
先說(shuō)原因:都是副作用的鍋!
副作用了解過(guò)函數(shù)式編程的同學(xué)對(duì)副作用這詞肯定不陌生。它大致可以理解成:一個(gè)函數(shù)會(huì)、或者可能會(huì)對(duì)函數(shù)外部變量產(chǎn)生影響的行為。
舉個(gè)例子,比如這個(gè)函數(shù):
function go (url) { window.location.href = url }
這個(gè)函數(shù)修改了全局變量location,甚至還讓瀏覽器發(fā)生了跳轉(zhuǎn),這就是一個(gè)有副作用的函數(shù)。
現(xiàn)在我們了解了副作用了,但是細(xì)想來(lái),我寫(xiě)的組件庫(kù)也沒(méi)有什么副作用啊,我每一個(gè)組件都是一個(gè)類(lèi),簡(jiǎn)化一下,如下所示:
// componetns.js export class Person { constructor ({ name, age, sex }) { this.className = "Person" this.name = name this.age = age this.sex = sex } getName () { return this.name } } export class Apple { constructor ({ model }) { this.className = "Apple" this.model = model } getModel () { return this.model } }
// main.js import { Apple } from "./components" const appleModel = new Apple({ model: "IphoneX" }).getModel() console.log(appleModel)
用rollup在線repl嘗試了下tree-shaking,也確實(shí)刪掉了Person,傳送門(mén)
可是為什么當(dāng)我通過(guò)webpack打包組件庫(kù),再被他人引入時(shí),卻沒(méi)辦法消除未使用代碼呢?
因?yàn)槲液雎粤藘杉虑椋篵abel編譯 + webpack打包
成也Babel,敗也BabelBabel不用我多解釋了,它能把ES6/ES7的代碼轉(zhuǎn)化成指定瀏覽器能支持的代碼。正是由于它,我們前端開(kāi)發(fā)者才能有今天這樣美好的開(kāi)發(fā)環(huán)境,能夠不用考慮瀏覽器兼容性地、暢快淋漓地使用最新的JavaScript語(yǔ)言特性。
然而也是由于它的編譯,一些我們?cè)究此茮](méi)有副作用的代碼,便轉(zhuǎn)化為了(可能)有副作用的。
如果懶得點(diǎn)開(kāi)鏈接,可以看下Person類(lèi)被babel編譯后的結(jié)果:
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var _createClass = function() { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || !1, descriptor.configurable = !0, "value" in descriptor && (descriptor.writable = !0), Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { return protoProps && defineProperties(Constructor.prototype, protoProps), staticProps && defineProperties(Constructor, staticProps), Constructor; }; }() var Person = function () { function Person(_ref) { var name = _ref.name, age = _ref.age, sex = _ref.sex; _classCallCheck(this, Person); this.className = "Person"; this.name = name; this.age = age; this.sex = sex; } _createClass(Person, [{ key: "getName", value: function getName() { return this.name; } }]); return Person; }();
我們的Person類(lèi)被封裝成了一個(gè)IIFE(立即執(zhí)行函數(shù)),然后返回一個(gè)構(gòu)造函數(shù)。那它怎么就產(chǎn)生副作用了呢?問(wèn)題就出現(xiàn)在_createClass這個(gè)方法上,你只要在上一個(gè)rollup的repl鏈接中,將Person的IIFE中的_createClass調(diào)用刪了,Person類(lèi)就會(huì)被移除了。至于_createClass為什么會(huì)產(chǎn)生副作用,我們先放一邊。因?yàn)榇蠹铱赡軙?huì)產(chǎn)生另外一個(gè)疑問(wèn):Babel為什么要這樣去聲明構(gòu)造函數(shù)的?
假如是我的話,我可能會(huì)這樣去編譯:
var Person = function () { function Person() { } Person.prototype.getName = function () { return this.name }; return Person; }();
因?yàn)槲覀円郧熬褪沁@么寫(xiě)“類(lèi)”的,那babel為什么要采用Object.defineProperty這樣的形式呢,用原型鏈有什么不妥呢?自然是非常的不妥的,因?yàn)镋S6的一些語(yǔ)法是有其特定的語(yǔ)義的。比如:
類(lèi)內(nèi)部聲明的方法,是不可枚舉的,而通過(guò)原型鏈聲明的方法是可以枚舉的。這里可以參考下阮老師介紹Class 的基本語(yǔ)法
for...of的循環(huán)是通過(guò)遍歷器(Iterator)迭代的,循環(huán)數(shù)組時(shí)并非是i++,然后通過(guò)下標(biāo)尋值。這里依舊可以看下阮老師關(guān)于遍歷器與for...of的介紹,以及一篇babel關(guān)于for...of編譯的說(shuō)明transform-es2015-for-of
所以,babel為了符合ES6真正的語(yǔ)義,編譯類(lèi)時(shí)采取了Object.defineProperty來(lái)定義原型方法,于是導(dǎo)致了后續(xù)這些一系列問(wèn)題。
眼尖的同學(xué)可能在我上述第二點(diǎn)中發(fā)的鏈接transform-es2015-for-of中看到,babel其實(shí)是有一個(gè)loose模式的,直譯的話叫做寬松模式。它是做什么用的呢?它會(huì)不嚴(yán)格遵循ES6的語(yǔ)義,而采取更符合我們平常編寫(xiě)代碼時(shí)的習(xí)慣去編譯代碼。比如上述的Person類(lèi)的屬性方法將會(huì)編譯成直接在原型鏈上聲明方法。
這個(gè)模式具體的babel配置如下:
// .babelrc { "presets": [["env", { "loose": false }]] }
同樣的,我放個(gè)在線repl示例方便大家直接查看效果:loose-mode
咦,如果我們真的不關(guān)心類(lèi)方法能否被枚舉,開(kāi)啟了loose模式,這樣是不是就沒(méi)有副作用產(chǎn)生,就能完美tree-shaking類(lèi)了呢?
我們開(kāi)啟了loose模式,使用rollup打包,發(fā)現(xiàn)還真是如此!傳送門(mén)
不夠?qū)诺腢glifyJS然而不要開(kāi)心的太早,當(dāng)我們用Webpack配合UglifyJS打包文件時(shí),這個(gè)Person類(lèi)的IIFE又被打包進(jìn)去了? What???
為了徹底搞明白這個(gè)問(wèn)題,我搜到一條UglifyJS的issue:Class declaration in IIFE considered as side effect,仔細(xì)看了好久。對(duì)此有興趣、并且英語(yǔ)還ok的同學(xué),可以快速去了解這條issue,還是挺有意思的。我大致闡述下這條issue下都說(shuō)了些啥。
issue樓主-blacksonic 好奇為什么UglifyJS不能消除未引用的類(lèi)。UglifyJS貢獻(xiàn)者-kzc說(shuō),uglify不進(jìn)行程序流分析,所以不能排除有可能有副作用的代碼。
樓主:我的代碼沒(méi)什么副作用啊。要不你們來(lái)個(gè)配置項(xiàng),設(shè)置后,可以認(rèn)為它是沒(méi)有副作用的,然后放心的刪了它們吧。
貢獻(xiàn)者:我們沒(méi)有程序流分析,我們干不了這事兒,實(shí)在想刪除他們,出門(mén)左轉(zhuǎn) rollup 吼吧,他們屌,做了程序流分析,能判斷到底有沒(méi)有副作用。
樓主:遷移rollup成本有點(diǎn)高啊。我覺(jué)得加個(gè)配置不難啊,比如這樣這樣,巴拉巴拉。
貢獻(xiàn)者:歡迎提PR。
樓主:別嘛,你們項(xiàng)目上千行代碼,我咋提PR啊。我的代碼也沒(méi)啥副作用啊,您能詳細(xì)的說(shuō)明下么?
貢獻(xiàn)者:變量賦值就是有可能產(chǎn)生副作用的!我舉個(gè)例子:
var V8Engine = (function () { function V8Engine () {} V8Engine.prototype.toString = function () { return "V8" } return V8Engine }()) var V6Engine = (function () { function V6Engine () {} V6Engine.prototype = V8Engine.prototype // <---- side effect V6Engine.prototype.toString = function () { return "V6" } return V6Engine }()) console.log(new V8Engine().toString())
貢獻(xiàn)者:V6Engine雖然沒(méi)有被使用,但是它修改了V8Engine原型鏈上的屬性,這就產(chǎn)生副作用了。你看rollup(樓主特意注明截至當(dāng)時(shí))目前就是這樣的策略,直接把V6Engine 給刪了,其實(shí)是不對(duì)的。樓主以及一些路人甲乙丙丁,紛紛提出自己的建議與方案。最終定下,可以在代碼上通過(guò)/*@__PURE__*/這樣的注釋聲明此函數(shù)無(wú)副作用。
這個(gè)issue信息量比較大,也挺有意思,其中那位uglify貢獻(xiàn)者kzc,當(dāng)時(shí)提出rollup存在的問(wèn)題后還給rollup提了issue,rollup認(rèn)為問(wèn)題不大不緊急,這位貢獻(xiàn)者還順手給rollup提了個(gè)PR,解決了問(wèn)題。。。
我再?gòu)倪@個(gè)issue中總結(jié)下幾點(diǎn)關(guān)鍵信息:
函數(shù)的參數(shù)若是引用類(lèi)型,對(duì)于它屬性的操作,都是有可能會(huì)產(chǎn)生副作用的。因?yàn)槭紫人且妙?lèi)型,對(duì)它屬性的任何修改其實(shí)都是改變了函數(shù)外部的數(shù)據(jù)。其次獲取或修改它的屬性,會(huì)觸發(fā)getter或者setter,而getter、setter是不透明的,有可能會(huì)產(chǎn)生副作用。
uglify沒(méi)有完善的程序流分析。它可以簡(jiǎn)單的判斷變量后續(xù)是否被引用、修改,但是不能判斷一個(gè)變量完整的修改過(guò)程,不知道它是否已經(jīng)指向了外部變量,所以很多有可能會(huì)產(chǎn)生副作用的代碼,都只能保守的不刪除。
rollup有程序流分析的功能,可以更好的判斷代碼是否真正會(huì)產(chǎn)生副作用。
有的同學(xué)可能會(huì)想,連獲取對(duì)象的屬性也會(huì)產(chǎn)生副作用導(dǎo)致不能刪除代碼,這也太過(guò)分了吧!事實(shí)還真是如此,我再貼個(gè)示例演示一下:傳送門(mén)
代碼如下:
// maths.js export function square ( x ) { return x.a } square({ a: 123 }) export function cube ( x ) { return x * x * x; }
//main.js import { cube } from "./maths.js"; console.log( cube( 5 ) ); // 125
打包結(jié)果如下:
function square ( x ) { return x.a } square({ a: 123 }); function cube ( x ) { return x * x * x; } console.log( cube( 5 ) ); // 125
而如果將square方法中的return x.a 改為 return x,則最終打包的結(jié)果則不會(huì)出現(xiàn)square方法。當(dāng)然啦,如果不在maths.js文件中執(zhí)行這個(gè)square方法,自然也是不會(huì)在打包文件中出現(xiàn)它的。
所以我們現(xiàn)在理解了,當(dāng)時(shí)babel編譯成的_createClass方法為什么會(huì)有副作用?,F(xiàn)在再回頭一看,它簡(jiǎn)直渾身上下都是副作用。
查看uglify的具體配置,我們可以知道,目前uglify可以配置pure_getters: true來(lái)強(qiáng)制認(rèn)為獲取對(duì)象屬性,是沒(méi)有副作用的。這樣可以通過(guò)它刪除上述示例中的square方法。不過(guò)由于沒(méi)有pure_setters這樣的配置,_createClass方法依舊被認(rèn)為是有副作用的,無(wú)法刪除。
那到底該怎么辦?聰明的同學(xué)肯定會(huì)想,既然babel編譯導(dǎo)致我們產(chǎn)生了副作用代碼,那我們先進(jìn)行tree-shaking打包,最后再編譯bundle文件不就好了嘛。這確實(shí)是一個(gè)方案,然而可惜的是:這在處理項(xiàng)目自身資源代碼時(shí)是可行的,處理外部依賴(lài)npm包就不行了。因?yàn)槿思覟榱俗尮ぞ甙哂型ㄓ眯?、兼容性,大多是?jīng)過(guò)babel編譯的。而最占容量的地方往往就是這些外部依賴(lài)包。
那先從根源上討論,假如我們現(xiàn)在要開(kāi)發(fā)一個(gè)組件庫(kù)提供給別人用,該怎么做?
如果是使用webpack打包JavaScript庫(kù)先貼下webpack將項(xiàng)目打包為JS庫(kù)的文檔??梢钥吹絯ebpack有多種導(dǎo)出模式,一般大家都會(huì)選擇最具通用性的umd方式,但是webpack卻沒(méi)支持導(dǎo)出ES模塊的模式。
所以,假如你把所有的資源文件通過(guò)webpack打包到一個(gè)bundle文件里的話,那這個(gè)庫(kù)文件從此與Tree-shaking無(wú)緣。
那怎么辦呢?也不是沒(méi)有辦法。目前業(yè)界流行的組件庫(kù)多是將每一個(gè)組件或者功能函數(shù),都打包成多帶帶的文件或目錄。然后可以像如下的方式引入:
import clone from "lodash/clone" import Button from "antd/lib/button";
但是這樣呢也比較麻煩,而且不能同時(shí)引入多個(gè)組件。所以這些比較流行的組件庫(kù)大哥如antd,element專(zhuān)門(mén)開(kāi)發(fā)了babel插件,使得用戶(hù)能以import { Button, Message } form "antd"這樣的方式去按需加載。本質(zhì)上就是通過(guò)插件將上一句的代碼又轉(zhuǎn)化成如下:
import Button from "antd/lib/button"; import Message from "antd/lib/button";
這樣似乎是最完美的變相tree-shaking方案。唯一不足的是,對(duì)于組件庫(kù)開(kāi)發(fā)者來(lái)說(shuō),需要專(zhuān)門(mén)開(kāi)發(fā)一個(gè)babel插件;對(duì)于使用者來(lái)說(shuō),需要引入一個(gè)babel插件,稍微略增加了開(kāi)發(fā)成本與使用成本。
除此之外,其實(shí)還有一個(gè)比較前沿的方法。是rollup的一個(gè)提案,在package.json中增加一個(gè)key:module,如下所示:
{ "name": "my-package", "main": "dist/my-package.umd.js", "module": "dist/my-package.esm.js" }
這樣,當(dāng)開(kāi)發(fā)者以es6模塊的方式去加載npm包時(shí),會(huì)以module的值為入口文件,這樣就能夠同時(shí)兼容多種引入方式,(rollup以及webpack2+都已支持)。但是webpack不支持導(dǎo)出為es6模塊,所以webpack還是要拜拜。我們得上rollup!
(有人會(huì)好奇,那干脆把未打包前的資源入口文件暴露到module,讓使用者自己去編譯打包好了,那它就能用未編譯版的npm包進(jìn)行tree-shaking了。這樣確實(shí)也不是不可以。但是,很多工程化項(xiàng)目的babel編譯配置,為了提高編譯速度,其實(shí)是會(huì)忽略掉node_modules內(nèi)的文件的。所以為了保證這些同學(xué)的使用,我們還是應(yīng)該要暴露出一份編譯過(guò)的ES6 Module。)
使用rollup打包JavaScript庫(kù)吃了那么多虧后,我們終于明白,打包工具庫(kù)、組件庫(kù),還是rollup好用,為什么呢?
它支持導(dǎo)出ES模塊的包。
它支持程序流分析,能更加正確的判斷項(xiàng)目本身的代碼是否有副作用。
我們只要通過(guò)rollup打出兩份文件,一份umd版,一份ES模塊版,它們的路徑分別設(shè)為main,module的值。這樣就能方便使用者進(jìn)行tree-shaking。
那么問(wèn)題又來(lái)了,使用者并不是用rollup打包自己的工程化項(xiàng)目的,由于生態(tài)不足以及代碼拆分等功能限制,一般還是用webpack做工程化打包。
使用webpack打包工程化項(xiàng)目之前也提到了,我們可以先進(jìn)行tree-shaking,再進(jìn)行編譯,減少編譯帶來(lái)的副作用,從而增加tree-shaking的效果。那么具體應(yīng)該怎么做呢?
首先我們需要去掉babel-loader,然后webpack打包結(jié)束后,再執(zhí)行babel編譯文件。但是由于webpack項(xiàng)目常有多入口文件或者代碼拆分等需求,我們又需要寫(xiě)一個(gè)配置文件,對(duì)應(yīng)執(zhí)行babel,這又略顯麻煩。所以我們可以使用webpack的plugin,讓這個(gè)環(huán)節(jié)依舊跑在webpack的打包流程中,就像uglifyjs-webpack-plugin一樣,不再是以loader的形式對(duì)單個(gè)資源文件進(jìn)行操作,而是在打包最后的環(huán)節(jié)進(jìn)行編譯。這里可能需要大家了解下webpack的plugin機(jī)制。
關(guān)于uglifyjs-webpack-plugin,這里有一個(gè)小細(xì)節(jié),webpack默認(rèn)會(huì)帶一個(gè)低版本的,可以直接用webpack.optimize.UglifyJsPlugin別名去使用。具體可以看webpack的相關(guān)說(shuō)明
webpack =< v3.0.0 currently contains v0.4.6 of this plugin under webpack.optimize.UglifyJsPlugin as an alias. For usage of the latest version (v1.0.0), please follow the instructions below. Aliasing v1.0.0 as webpack.optimize.UglifyJsPlugin is scheduled for webpack v4.0.0
而這個(gè)低版本的uglifyjs-webpack-plugin使用的依賴(lài)uglifyjs也是低版本的,它沒(méi)有uglifyES6代碼的能力,故而如果我們有這樣的需求,需要在工程中重新npm install uglifyjs-webpack-plugin -D,安裝最新版本的uglifyjs-webpack-plugin,重新引入它并使用。
這樣之后,我們?cè)偈褂脀ebpack的babel插件進(jìn)行編譯代碼。
問(wèn)題又來(lái)了,這樣的需求比較少,因此webpack和babel官方都沒(méi)有這樣的插件,只有一個(gè)第三方開(kāi)發(fā)者開(kāi)發(fā)了一個(gè)插件babel-webpack-plugin。可惜的是這位作者已經(jīng)近一年沒(méi)有維護(hù)這個(gè)插件了,并且存在著一個(gè)問(wèn)題,此插件不會(huì)用項(xiàng)目根目錄下的.babelrc文件進(jìn)行babel編譯。有人對(duì)此提了issue,卻也沒(méi)有任何回應(yīng)。
那么又沒(méi)有辦法,就我來(lái)寫(xiě)一個(gè)新的插件吧----webpack-babel-plugin,有了它之后我們就能讓webpack在最后打包文件之前進(jìn)行babel編譯代碼了,具體如何安裝使用可以點(diǎn)開(kāi)項(xiàng)目查看。注意這個(gè)配置需要在uglifyjs-webpack-plugin之后,像這樣:
plugins: [ new UglifyJsPlugin(), new BabelPlugin() ]
但是這樣呢,有一個(gè)毛病,由于babel在最后階段去編譯比較大的文件,耗時(shí)比較長(zhǎng),所以建議區(qū)分下開(kāi)發(fā)模式與生產(chǎn)模式。另外還有個(gè)更大的問(wèn)題,webpack本身采用的編譯器acorn不支持對(duì)象的擴(kuò)展運(yùn)算符(...)以及某些還未正式成為ES標(biāo)準(zhǔn)的特性,所以。。。。。
所以如果特性用的非常超前,還是需要babel-loader,但是babel-loader要做專(zhuān)門(mén)的配置,把還在es stage階段的代碼編譯成ES2017的代碼,以便于webpack本身做處理。
感謝掘金熱心網(wǎng)友的提示,還有一個(gè)插件BabelMinifyWebpackPlugin,它所依賴(lài)的babel/minify也集成了uglifyjs。使用此插件便等同于上述使用UglifyJsPlugin + BabelPlugin的效果,如若有此方面需求,建議使用此插件。
總結(jié)上面講了這么多,我最后再總結(jié)下,在當(dāng)下階段,在tree-shaking上能夠盡力的事。
盡量不寫(xiě)帶有副作用的代碼。諸如編寫(xiě)了立即執(zhí)行函數(shù),在函數(shù)里又使用了外部變量等。
如果對(duì)ES6語(yǔ)義特性要求不是特別嚴(yán)格,可以開(kāi)啟babel的loose模式,這個(gè)要根據(jù)自身項(xiàng)目判斷,如:是否真的要不可枚舉class的屬性。
如果是開(kāi)發(fā)JavaScript庫(kù),請(qǐng)使用rollup。并且提供ES6 module的版本,入口文件地址設(shè)置到package.json的module字段。
如果JavaScript庫(kù)開(kāi)發(fā)中,難以避免的產(chǎn)生各種副作用代碼,可以將功能函數(shù)或者組件,打包成多帶帶的文件或目錄,以便于用戶(hù)可以通過(guò)目錄去加載。如有條件,也可為自己的庫(kù)開(kāi)發(fā)多帶帶的webpack-loader,便于用戶(hù)按需加載。
如果是工程項(xiàng)目開(kāi)發(fā),對(duì)于依賴(lài)的組件,只能看組件提供者是否有對(duì)應(yīng)上述3、4點(diǎn)的優(yōu)化。對(duì)于自身的代碼,除1、2兩點(diǎn)外,對(duì)于項(xiàng)目有極致要求的話,可以先進(jìn)行打包,最終再進(jìn)行編譯。
如果對(duì)項(xiàng)目非常有把握,可以通過(guò)uglify的一些編譯配置,如:pure_getters: true,刪除一些強(qiáng)制認(rèn)為不會(huì)產(chǎn)生副作用的代碼。
故而,在當(dāng)下階段,依舊沒(méi)有比較簡(jiǎn)單好用的方法,便于我們完整的進(jìn)行tree-shaking。所以說(shuō),想做好一件事真難啊。不僅需要靠個(gè)人的努力,還需要考慮到歷史的進(jìn)程。
PS: 此文中涉及到的代碼,我也傳到了github,可以點(diǎn)擊閱讀原文下載查看。
--閱讀原文
@丁香園F2E @相學(xué)長(zhǎng)
--轉(zhuǎn)載請(qǐng)先經(jīng)過(guò)本人授權(quán)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90805.html
摘要:為何有查閱了的文檔,并沒(méi)有找到字段的定義,直到才知道它是中最早就提出的概念。況且目前大部分仍是采用,所以便使用了另一個(gè)字段。所以目前主流的打包工具都是支持的,鑒于其優(yōu)點(diǎn),字段很有可能加入的規(guī)范之中。 引入 最近團(tuán)隊(duì)的一個(gè)同學(xué)在搞 npm library 源碼的調(diào)試插件,因?yàn)閮?nèi)部的一個(gè)組件庫(kù)含有大量的邏輯,在某個(gè)項(xiàng)目中不經(jīng)意就出現(xiàn)一個(gè)磨人的 bug,但是組件庫(kù)發(fā)布都是打包編譯后的代碼,而...
摘要:概念由來(lái)已久,今天再來(lái)談一談,是因?yàn)橹杏辛诵碌膬?yōu)化。簡(jiǎn)單的介紹下什么是。它已經(jīng)為我們消除了副作用。而且我并沒(méi)有引入。即便根據(jù)文件大小,可能還有朋友持懷疑態(tài)度。因?yàn)樽罱沤佑|。所以沒(méi)有在低版本的時(shí)候打包過(guò)。 Tree-Shaking概念由來(lái)已久,今天再來(lái)談一談,是因?yàn)閣ebpack4中有了新的優(yōu)化。簡(jiǎn)單的介紹下什么是Tree-Shaking。 代碼不會(huì)被執(zhí)行 if(false) { ...
摘要:原文鏈接開(kāi)始新增了一個(gè)特性,通過(guò)給加入聲明該包模塊是否包含副作用,從而可以為提供更大的優(yōu)化空間。而通常我們期望的是模塊既然不被使用了,其中所有的代碼應(yīng)該不被引入才對(duì)。但當(dāng)我們加上之后,就能安全的把它從里完整的移除掉了。 原文鏈接 webpack v4 開(kāi)始新增了一個(gè) sideEffects 特性,通過(guò)給 package.json 加入 sideEffects: false 聲明該包模塊...
摘要:因此,你還是需要各種各樣雜七雜八的工具來(lái)轉(zhuǎn)換你的代碼噢,我可去你媽的吧,這些東西都是干嘛的我就是想用個(gè)模塊化,我到底該用啥子本文正旨在列出幾種可用的在生產(chǎn)環(huán)境中放心使用模塊化的方法,希望能幫到諸位后來(lái)者這方面的中文資源實(shí)在是忒少了。 原文發(fā)表在我的博客上。最近搗鼓了一下 ES6 的模塊化,分享一些經(jīng)驗(yàn) :) Python3 已經(jīng)發(fā)布了九年了,Python 社區(qū)卻還在用 Python 2...
摘要:教程如何使用打包通過(guò)這個(gè)系列教程一步一步學(xué)習(xí)如何使用更小更快的取代和打包文件。安裝并且創(chuàng)建配置文件。提示是告訴我們實(shí)際需要哪些插件的集合。通過(guò)下面的命令安裝兩個(gè)插件更新然后,引入插件并添加進(jìn)配置注意屬性是為了幫助模塊遷移到的一部分。 教程:如何使用Rollup打包JavaScript 通過(guò)這個(gè)系列教程一步一步學(xué)習(xí)如何使用更小更快的Rollup取代webpack和Browserify打包...
閱讀 570·2023-04-26 02:59
閱讀 700·2023-04-25 16:02
閱讀 2174·2021-08-05 09:55
閱讀 3590·2019-08-30 15:55
閱讀 4682·2019-08-30 15:44
閱讀 1811·2019-08-30 13:02
閱讀 2209·2019-08-29 16:57
閱讀 2297·2019-08-26 13:35