【躬行】webpack5 tree shaking 实际测试结果

190 阅读4分钟

webpack在打包的过程中,借鉴了rollup的tree shaking功能,降低了代码打包体积。之前一直是来自书本的理解,就是es module的静态检查功能使得 tree shaking 成为现实。但是在实际工作中,有一次关于 vconsole 的代码,我发现擦除失败,一直在思考为什么,本着实践出真知的想法,于是今天就写了一点简单的代码测试一下webpack5中tree shaking的一个实际情况。可能存在很多不足,希望大家可以在评论区里面指正。

先贴上webpack的基本配置信息:

// webpack ^5.72.0
// webpack-cli ^4.9.2
// webpack.config.js
const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js',
    },
    mode: 'production',
};

Es module 导入

Es module 导出

使用export default导出

// 入口文件 index.js

import HW from './helloworld';

document.write(HW.a());
// helloworld.js

function a() {
    return 'hello world a';
}

function b() {
    return 'hello world b';
}

export default {
    a,
    b,
};
// bundle.js
(() => {
    'use strict';
    document.write('hello world a');
})();

结论:function b 被 tree shaking 清除,虽然也可以擦除,但是不推荐这样的导出方式

使用export导出

// 入口文件 index.js

import { a } from './helloworld';

document.write(a());
export function a() {
    return 'hello world a';
}

export function b() {
    return 'hello world b';
}
(() => {
    'use strict';
    document.write('hello world a');
})();

结论:function b 被 tree shaking 清除

Commonjs 导出

使用 module.exports 导出

// 入口文件 index.js

import { a } from './helloworld';

document.write(a());
function a() {
 return 'hello world a';
 }

function b() {
 return 'hello world b';
 }

module.exports = {
 a,
 b,
 };
(() => {
    var r = {
            694: r => {
                r.exports = {
                    a: function () {
                        return 'hello world a';
                    },
                    b: function () {
                        return 'hello world b';
                    },
                };
            },
        },
        e = {};
    function t(o) {
        var n = e[o];
        if (void 0 !== n) return n.exports;
        var a = (e[o] = { exports: {} });
        return r[o](a, a.exports, t), a.exports;
    }
    (t.n = r => {
        var e = r && r.__esModule ? () => r.default : () => r;
        return t.d(e, { a: e }), e;
    }),
        (t.d = (r, e) => {
            for (var o in e) t.o(e, o) && !t.o(r, o) && Object.defineProperty(r, o, { enumerable: !0, get: e[o] });
        }),
        (t.o = (r, e) => Object.prototype.hasOwnProperty.call(r, e)),
        (() => {
            'use strict';
            var r = t(694);
            document.write((0, r.a)());
        })();

})();

结论:function b 没有被擦除,tree shaking无效

使用 exports 导出

// 入口文件 index.js

import { a } from './helloworld';

document.write(a());
exports.a = function a() {
 return 'hello world a';
 };

exports.b = function b() {
 return 'hello world b';
 };
(() => {
    var r = {
            694: (r, t) => {
                t.a = function () {
                    return 'hello world a';
                };
            },
        },
        t = {};
    function e(o) {
        var n = t[o];
        if (void 0 !== n) return n.exports;
        var a = (t[o] = { exports: {} });
        return r[o](a, a.exports, e), a.exports;
    }
    (() => {
        'use strict';
        var r = e(694);
        document.write((0, r.a)());
    })();
})();

结论:function b 被 tree shaking 清除,就是看起来打包后的代码啰里啰嗦的

Commonjs 导入

Es module 导出

使用export default导出

// 入口文件 index.js

const HW = require('./helloworld');

document.write(HW.default.a());
// helloworld.js
function a() {
    return 'hello world a';
}

function b() {
    return 'hello world b';
}



export default {
    a,
    b,
};
// bundle.js
(() => {
    var e = {
            694: (e, r, t) => {
                'use strict';
                t.r(r), t.d(r, { default: () => o });

                const o = {
                    a: function () {
                        return 'hello world a';
                    },
                    b: function () {
                        return 'hello world b';
                    },
                };
            },
        },
        r = {};
    function t(o) {
        var n = r[o];
        if (void 0 !== n) return n.exports;
        var l = (r[o] = { exports: {} });
        return e[o](l, l.exports, t), l.exports;
    }
    (t.d = (e, r) => {
        for (var o in r) t.o(r, o) && !t.o(e, o) && Object.defineProperty(e, o, { enumerable: !0, get: r[o] });
    }),
        (t.o = (e, r) => Object.prototype.hasOwnProperty.call(e, r)),
        (t.r = e => {
            'undefined' != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: 'Module' }),
                Object.defineProperty(e, '__esModule', { value: !0 });
        }),
        (() => {
            const e = t(694);
            document.write(e.default.a());
        })();
})();

结论:function b 没有被擦除,tree shaking无效

使用export导出

// 入口文件 index.js

const HW = require('./helloworld');

document.write(HW.a());
export function a() {
    return 'hello world a';
}

export function b() {
    return 'hello world b';
}
(() => {
    var e = {
            694: (e, r, o) => {
                'use strict';
                function t() {
                    return 'hello world a';
                }
                function n() {
                    return 'hello world b';
                }
                o.r(r), o.d(r, { a: () => t, b: () => n });
            },
        },
        r = {};
    function o(t) {
        var n = r[t];
        if (void 0 !== n) return n.exports;
        var l = (r[t] = { exports: {} });
        return e[t](l, l.exports, o), l.exports;
    }
    (o.d = (e, r) => {
        for (var t in r) o.o(r, t) && !o.o(e, t) && Object.defineProperty(e, t, { enumerable: !0, get: r[t] });
    }),
        (o.o = (e, r) => Object.prototype.hasOwnProperty.call(e, r)),
        (o.r = e => {
            'undefined' != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: 'Module' }),
                Object.defineProperty(e, '__esModule', { value: !0 });
        }),
        (() => {
            const e = o(694);
            document.write(e.a());
        })();
})();

结论:function b 没有被擦除,tree shaking无效

Commonjs 导入

使用 module.exports 导出

// 入口文件 index.js

const HW = require('./helloworld');

document.write(HW.a());
function a() {
 return 'hello world a';
 }

function b() {
 return 'hello world b';
 }

module.exports = {
 a,
 b,
 };
(() => {
    var r = {
            694: r => {
                r.exports = {
                    a: function () {
                        return 'hello world a';
                    },
                    b: function () {
                        return 'hello world b';
                    },
                };
            },
        },
        o = {};
    function t(e) {
        var n = o[e];
        if (void 0 !== n) return n.exports;
        var u = (o[e] = { exports: {} });
        return r[e](u, u.exports, t), u.exports;
    }
    (() => {
        const r = t(694);
        document.write(r.a());
    })();
})();

结论:function b 没有被擦除,tree shaking无效

使用 exports 导出

// 入口文件 index.js
const HW = require('./helloworld');

document.write(HW.a());
exports.a = function a() {
 return 'hello world a';
 }

exports.b = function b() {
 return 'hello world b';
 }
(() => {
    var r = {
            694: (r, o) => {
                (o.a = function () {
                    return 'hello world a';
                }),
                    (o.b = function () {
                        return 'hello world b';
                    });
            },
        },
        o = {};
    function t(e) {
        var n = o[e];
        if (void 0 !== n) return n.exports;
        var u = (o[e] = { exports: {} });
        return r[e](u, u.exports, t), u.exports;
    }
    (() => {
        const r = t(694);
        document.write(r.a());
    })();
})();

结论:function b 没有被擦除,tree shaking无效

结论:

  1. 如果导入方式采用的是commonjs的形式,那么tree shaking无法生效
  2. 如果导入方式采用的是es module,除了 module 采用了 module.exports 这样的形式导出接口外,其余情况都可以生效
  3. 当然这个测试是比较简单的纯函数,真实的项目里面情况比这个复杂,推荐阅读 你的Tree-Shaking并没什么卵用

最后: 本人在4月初被百度公司按违纪解除劳动合同,公司拒绝沟通,不出具具体原因。在疫情情况下,蒙受损失,还有许许多多人和我一样的遭遇,拿不到股票,那不多签字费。在金3银4的末尾,友情提醒大家一旦公司开始作恶,那么它就是会一直作恶,毕竟“简单可以赖”,希望大家可以找到心仪的工作。