博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【译】Tree-shaking - webpack 2 和 Babel 6
阅读量:6296 次
发布时间:2019-06-22

本文共 5091 字,大约阅读时间需要 16 分钟。

注:原文发表于 2015年 12月20日,彼时一些相关的技术方案还处于公测或者论证阶段。

Rich Harris’ module bundler Rollup popularized an important feature in the JavaScript world: tree-shaking, excluding unused exports from bundles. Rollup depends on the static structure of ES6 modules (imports and exports can’t be changed at runtime) to detect which exports are unused.

在 javascript 的圈子里引爆了一个新的概念 : tree-shaking。 tree shaking 在模块打包时排除没有使用到的模块,从而减小代码体积,提高加载和执行效率。 Rollup 依赖 ES6 的(不像ES6中可以在runtime中动态的决定导入导出的内容) 分析辨别无用的代码。

Tree-shaking for webpack is currently in beta. This blog post explains how it works. The project we are going to examine is on GitHub: tree-shaking-demo

webpack 的 tree-shaking 方案目前处于公测阶段。本篇博文解释它是如何工作的。文中使用到的代码来自于

webpack2 如何消除无用源代码

webpack 2, a new version that is in beta, eliminates unused exports in two steps:

webpack2 通过下面的两个步骤来消除无用的代码。

First, all ES6 module files are combined into a single bundle file. In that file, exports that were not imported anywhere are not exported, anymore.

首先,所有的 ES6 文件整合到一个 bundle 中,在这个文件里,没有被导入过的模块将不会再被导出。

我的理解是:

m1.js

export const foo = () => {}export const bar = () => {}

m2.js

import { foo } from './m1'foo()

整合过程中,发现 bar 没有被其他模块导入过,所以最终结果是

bundle.js

export const foo = () => {}const bar = () => {} // bar 还在,只是没有被 exportfoo()

Second, the bundle is minified, while eliminating dead code. Therefore, entities that are neither exported nor used inside their modules do not appear in the minified bundle. Without the first step, dead code elimination would never remove exports (registering an export keeps it alive).

移除既不导出也不使用的模块。如果没有第一步的支持,我们无法辨别从未被导入过的模块。

Unused exports can only be reliably detected at build time if the module system has a static structure. Therefore, webpack 2 can parse and understand all of ES6 and only tree-shakes if it detects an ES6 module. However, only imports and exports are transpiled to ES5. If you want all of the bundle to be in ES5, you need a transpiler for the remaining parts of ES6. In this blog post, we’ll use Babel 6.

只有在静态的模块结构下,才能够准确的移除无用的模块。 webpack 2 仅仅能够理解和分析 ES6 的模块并且执行 tree-shaking,并不能帮你将代码编译成 ES6,如果你需要那么做,可以使用 Babel 或者其他的编译工具。

输入:ES6 代码

示例包含两个 ES6 代码文件。

helpers.js

// helpers.jsexport function foo() {    return 'foo';}export function bar() {    return 'bar';}

main.js, 入口文件

// main.jsimport {foo} from './helpers';let elem = document.getElementById('output');elem.innerHTML = `Output: ${foo()}`;

注意 bar 模块没有被任何其他模块使用。

输出:没有 tree-shaking

Babel6 编译 ES6 代码最常见办法的是使用一个预设的 preset :

{    presets: ['es2015'],}

However, that preset includes the plugin transform-es2015-modules-commonjs, which means that Babel will output CommonJS modules and webpack won’t be able to tree-shake:

然而,这个 preset 包含 ,它会使得 Babel 输出 commonjs 风格的模块,这导致 webpack 无法执行 tree-shake。

function(module, exports) {    'use strict';    Object.defineProperty(exports, "__esModule", {        value: true    });    exports.foo = foo;    exports.bar = bar;    function foo() {        return 'foo';    }    function bar() {        return 'bar';    }}

You can see that bar is part of the exports, which prevents it being recognized as dead code by minification.

你可以看到 bar 现在是 exports 的一部分了, webpack 无法辨别 bar 是否是 dead code - 从来没有被使用的代码。

输出: tree-shaking

What we want is Babel’s es2015, but without the plugin transform-es2015-modules-commonjs. At the moment, the only way to get that is by mentioning all of the preset’s plugins in our configuration data, except for the one we want to exclude. The preset’s source is on GitHub, so it’s basically a case of copying and pasting:

我们想要不包含 transform-es2015-modules-commonjs 的 ,目前来说只能显示的声明一堆 plugin 来代替 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 }],    ],}

If we build the project now, module helpers looks like this inside the bundle:

如果我们再去编译这个项目, 会发现一些变化。

function(module, exports, __webpack_require__) {    /* harmony export */ exports["foo"] = foo;    /* unused harmony export bar */;    function foo() {        return 'foo';    }    function bar() {        return 'bar';    }}

Only foo is an export now, but bar is still there. After minification, helpers looks like this (I’ve added line breaks and whitespace to make the code easier to read):

简单来说, 只有 foo 被导出。接下来做移除的时候,打包工具发现 bar 可以移除,从而得到下面的结果。

function (t, n, r) {    function e() {        return "foo"    }    n.foo = e}

Et voilà – no more function bar!

再也没有 bar 了。

拓展阅读

转载地址:http://jvvta.baihongyu.com/

你可能感兴趣的文章
内核源码树
查看>>
AppScan使用
查看>>
Java NIO框架Netty教程(三) 字符串消息收发(转)
查看>>
Ucenter 会员同步登录通讯原理
查看>>
php--------获取当前时间、时间戳
查看>>
Spring MVC中文文档翻译发布
查看>>
docker centos环境部署tomcat
查看>>
JavaScript 基础(九): 条件 语句
查看>>
Linux系统固定IP配置
查看>>
配置Quartz
查看>>
Linux 线程实现机制分析
查看>>
继承自ActionBarActivity的activity的activity theme问题
查看>>
设计模式01:简单工厂模式
查看>>
项目经理笔记一
查看>>
Hibernate一对一外键双向关联
查看>>
mac pro 入手,php环境配置总结
查看>>
MyBatis-Plus | 最简单的查询操作教程(Lambda)
查看>>
rpmfusion 的国内大学 NEU 源配置
查看>>
spring jpa 配置详解
查看>>
IOE,为什么去IOE?
查看>>