我们相信:世界是美好的,你是我也是。 来玩一下解压小游戏吧!

Tree Shaking是一种通过消除未使用代码来优化JavaScript包大小的技术。它依赖于ESM模块的静态结构特性,可以在编译时确定哪些模块和函数是未使用的,并将其从最终的打包文件中移除。

苏南大叔:如何理解webpack打包中有关js的TreeShaking技术? - treeshaking
如何理解webpack打包中有关js的TreeShaking技术?(图3-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。测试环境:win10node@20.18.0webpack@5.98.0TreeShaking可以想象成用力摇树,把树叶摇下来。就是说尽量减少没有用到的代码。很多打包工具都有TreeShaking功能,不局限于webpack

仅 ESM 模块支持

两大模块加载方式:

  • commonjsrequire,不支持TreeShaking的,它只支持整体加载,不支持剪枝。
  • ESM(ES6)的import,支持TreeShaking,所以目前来看,应用更广泛。

参考文章:

基础代码

首先,苏南大叔给出一个commonjs的文件js-commonjs.js

module.exports.usedFunction = function () {
    console.log("苏南大叔说:这是个被使用的函数");
}
module.exports.unusedFunction = function () {
    console.log("苏南大叔说:这是一个不被使用的函数");
}

然后,作为对照。创建了一个包含两个导出函数的ESM文件 js-esm.mjs

export function usedFunction2() {
    console.log("苏南大叔说:这是个被使用的函数");
}
export function unusedFunction2() {
    console.log("苏南大叔说:这是一个不被使用的函数");
}

苏南大叔:如何理解webpack打包中有关js的TreeShaking技术? - 代码模块对比
如何理解webpack打包中有关js的TreeShaking技术?(图3-2)

入口文件 main.mjs,只导入并使用 usedFunction()usedFunction2()

var js1 = require('./js-commonjs.js');
console.log(js1.usedFunction());
import {usedFunction2} from './js-esm.mjs'; 
console.log(usedFunction2());

创建一个 webpack.config.js 文件,并启用生产模式:

const path = require('path');
module.exports = {
    mode: 'production',
    entry: './main.mjs',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    }
};

webpack 打包结果

执行命令:

npm install --save-dev webpack webpack-cli
npx webpack

执行webpack命令,在生产模式下,Webpack会自动启用Tree Shaking,并移除未使用的代码。得到的dist/bundle.js代码如下:

(() => {
    "use strict";
    var o = require("./js-commonjs.js");
    console.log(o.usedFunction());
    console.log(void console.log("苏南大叔说:这是个被使用的函数"));
})();

结果非常明显。

  • commonjs模块文件中的代码,依然独立存在。没有被打包进来,也没有被TreeShaking
  • ESM模块文件中的代码,被打包进来了。没有被使用到的函数,被TreeShaking掉了。

苏南大叔:如何理解webpack打包中有关js的TreeShaking技术? - 打包结果对比
如何理解webpack打包中有关js的TreeShaking技术?(图3-3)

browserify打包结果对比

根据这两篇文章:

执行命令:

browserify main.mjs -o dist/bundle2.js -t [ babelify --presets [ @babel/preset-env ] ]

得到的dist/bundle2.js,代码如下:

(function () {
    function r(e, n, t) {
        function o(i, f) {
            if (!n[i]) {
                if (!e[i]) {
                    var c = "function" == typeof require && require;
                    if (!f && c) return c(i, !0);
                    if (u) return u(i, !0);
                    var a = new Error("Cannot find module '" + i + "'");
                    throw a.code = "MODULE_NOT_FOUND", a
                }
                var p = n[i] = { exports: {} };
                e[i][0].call(p.exports, function (r) {
                    var n = e[i][4][r];
                    return o(n || r)
                }, p, p.exports, r, e, n, t)
            }
            return n[i].exports
        }
        for (var u = "function" == typeof require && require, i = 0; i < t.length; i++) o(t[i]);
        return o
    }
    return r
})()({
    1: [function (require, module, exports) {
        "use strict";
        module.exports.usedFunction = function () {
            console.log("苏南大叔说:这是个被使用的函数");
        };
        module.exports.unusedFunction = function () {
            console.log("苏南大叔说:这是一个不被使用的函数");
        };
    }, {}], 2: [function (require, module, exports) {
        "use strict";
        Object.defineProperty(exports, "__esModule", {
            value: true
        });
        exports.unusedFunction2 = unusedFunction2;
        exports.usedFunction2 = usedFunction2;
        function usedFunction2() {
            console.log("苏南大叔说:这是个被使用的函数");
        }
        function unusedFunction2() {
            console.log("苏南大叔说:这是一个不被使用的函数");
        }
    }, {}], 3: [function (require, module, exports) {
        "use strict";
        var _jsEsm = require("./js-esm.mjs");
        var js1 = require('./js-commonjs.js');
        console.log(js1.usedFunction());
        console.log((0, _jsEsm.usedFunction2)());
    }, { "./js-commonjs.js": 1, "./js-esm.mjs": 2 }]
}, {}, [3]);

所以,上述默认情况下。browserify的结论是:

  • browerify不但不支持treeshaking
  • 而且会无视commonjs还是esm,统统打包到一起,所以代码量确实会增大很多。

结论

Tree Shaking就是个概念,其实它早就在代码编写打包中使用了。它可以显著减少JavaScript包的大小。通过使用支持Tree Shaking的打包工具,就可以确保最终的打包文件只包含实际使用的代码,从而提高应用的性能。

更多苏南大叔的webpack相关经验文章,请点击:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   webpack