当前位置: 首页 > 后端技术 > Node.js

[翻译]Tree-shaking-webpack2andBabel6

时间:2023-04-03 18:06:20 Node.js

注:原文发表于2015年12月20日,当时部分相关技术方案尚处于公测或演示阶段。RichHarris的模块打包器Rollup推广了JavaScript世界的一个重要特性:tree-shaking,从包中排除未使用的导出。Rollup依赖于ES6模块的静态结构(imports和exports不能在运行时改变)来检测哪些exports没有被使用。RichHarris的模块打包器Rollup在javascript圈子里引爆了一个新概念:tree-shaking。Treeshaking在模块打包时排除了不用的模块,从而减少代码大小,提高加载和执行效率。Rollup依靠ES6的静态模块结构(不像ES6,可以在运行时动态确定导入和导出内容)来分析和识别无用代码。用于webpack的tree-shaking目前处于测试阶段。这篇博文解释了它是如何工作的。我们要检查的项目在GitHub上:tree-shaking-demowebpack的tree-shaking解决方案目前处于公开测试阶段。这篇博文解释了它是如何工作的。本文使用的代码来自howtree-shaking-demowebpack2eliminationuselesssourcecodewebpack2,一个处于测试阶段的新版本,分两步消除无用的exports:webpack2通过以下两步消除无用代码。首先,所有ES6模块文件都合并到一个包文件中。在该文件中,不会再导出未在任何地方导入的导出。过期模块将不再导出。我的理解是:m1.jsexportconstfoo=()=>{}exportconstbar=()=>{}m2.jsimport{foo}from'./m1'foo()集成过程中发现,bar没有导入其他模块,所以最后的结果是bundle.jsexportconstfoo=()=>{}constbar=()=>{}//bar还在,只是没有被foo()其次,bundle被缩小,同时消除了死代码。因此,既未导出也未在其模块内使用的实体不会出现在缩小包中。如果没有第一步,死代码消除将永远不会删除导出(注册导出使其保持活动状态)。也不使用的模块。没有第一步的支持,我们无法识别从未导入过的模块。如果模块系统具有静态结构,则只有在构建时才能可靠地检测到未使用的导出。因此,webpack2可以解析和理解所有ES6,并且只有在检测到ES6模块时才进行tree-shakes。但是,只有导入和导出被转换为ES5。如果你希望所有的包都在ES5中,你需要一个用于ES6其余部分的转译器。在这篇博文中,我们将使用Babel6。只有在静态模块结构下,才能准确移除无用的模块。webpack2只能理解和分析ES6模块并执行tree-shaking,并不能帮助你将代码编译成ES6。如果需要这样做,可以使用Babel或其他编译工具。输入:ES6代码示例包含两个ES6代码文件。helpers.js//helpers.jsexportfunctionfoo(){return'foo';}exportfunctionbar(){return'bar';}main.js,entryfile//main.jsimport{foo}from'./helpers';letelem=document.getElementById('output');elem.innerHTML=`输出:${foo()}`;请注意,bar模块不被任何其他模块使用。输出:最常见??的不用tree-shakingBabel6编译ES6代码的方法是使用presetpresetbabel-preset-es2015:{presets:['es2015'],}然而,那个preset包括插件transform-es2015-modules-commonjs,这意味着Babel将输出CommonJS模块,webpack将无法进行tree-shake:但是,这个preset包含transform-es2015-modules-commonjs,这将使Babel输出commonjs风格的模块,这使得webpack无法执行摇树。功能(模块,出口){'使用严格';Object.defineProperty(exports,"__esModule",{value:true});exports.foo=foo;exports.bar=bar;函数foo(){返回'foo';}functionbar(){return'bar';您可以看到bar是导出的一部分,这可以防止它被缩小识别为死代码。你可以看到bar现在是导出的一部分,webpack无法识别bar是否是死代码——从未使用过的代码。Output:tree-shaking我们要的是Babel的es2015,但是没有插件transform-es2015-modules-commonjs。目前,唯一的方法是在我们的配置数据中提及所有预设的插件,除了我们想要排除的插件。preset的源码在GitHub上,所以基本上就是复制粘贴:我们要的是不包含transform-es2015-modules-commonjs的babel-preset-es2015。目前只能显示一种说法Heapplugin来替换preset。如下。{plugins:['transform-es2015-template-literals','transform-es2015-literals','transform-es2015-function-name','transform-es2015-arrow-functions','transform-es2015-block-scoped-functions','transform-es2015-classes','transform-es2015-object-super','transform-es2015-shorthand-properties','transform-es2015-computed-properties','transform-es2015-for-of','transform-es2015-sticky-regex','transform-es2015-unicode-regex','check-es2015-constants','transform-es2015-spread','transform-es2015-parameters','transform-es2015-destructuring','transform-es2015-block-scoping','transform-es2015-typeof-symbol',['transform-regenerator',{async:false,asyncGenerators:false}],],}如果我们构建项目现在,模块助手在包中看起来像这样:如果我们再去编写这个项目,会发现一些变化。function(module,exports,__webpack_require__){/*harmonyexport*/exports["foo"]=foo;/*未使用的和声导出栏*/;函数foo(){返回'foo';}functionbar(){return'bar';}}现在只有foo是导出的,但bar仍然存在。缩小后,助手看起来像这样(我添加了换行符和空格以使代码更易于阅读):简而言之,仅导出foo。在进行下一次移除时,打包工具发现bar可以移除,结果如下。function(t,n,r){functione(){return"foo"}n.foo=e}Etvoilà–没有更多的函数吧!没有更多的酒吧。进一步阅读webpack示例:harmony-unusedConfiguringBabel6