【问题标题】:Conditional compile/require using browserify (dead code elimination)使用 browserify 进行条件编译/要求(死代码消除)
【发布时间】:2017-03-06 00:58:07
【问题描述】:

我知道你不能有条件地 require 一个带有 browserify 的模块,因为它们是在编译时捆绑的,而不是在运行时捆绑的。有条件地剥离模块怎么样?

假设我有一个允许您创建图片库的应用。还可以编辑画廊(重新排序图像等)。但是画廊的渲染和剪辑是耦合的,不能完全分开。但是对于部署画廊,我不需要编辑功能,而且我知道使用了哪些模块。我想创建两个不同的包,一个具有编辑功能,一个不消除大部分编辑代码。我所做的是使用 envify 和 uglify 的死代码消除将我自己的代码从较小的包中排除。

之前(thing.js)

//...some code that only the editor needs...
module.exports = thing;

之后(thing.js)

if(process.env.INCLUDE_EDITOR === 'yes') {
    //...some code that only the editor needs...
    module.exports = thing;
}

这很好用,并且编辑器包已经更小了。而且因为我知道另一个包永远不会使用thing 的功能,所以什么都不导出并有一个空模块是可以的。

现在问题来了。如果thing.js 需要一个模块,比如pica,它仍然会被捆绑即使在消除死代码后没有人使用它

之前(thing.js)

var pica = require('pica');
//...some code that uses pica...
module.exports = thing;

之后(thing.js)

if(process.env.INCLUDE_EDITOR === 'yes') {
    var pica = require('pica');
    //...some code that uses pica...
    module.exports = thing;
}

总结一下:我的包现在包含pica 库,但没有人需要它。需要它的代码是死代码,但uglify显然无法理解它可以完全删除pica

【问题讨论】:

  • 也许factor-bundle 对你有用?
  • @casr 谢谢!我不认为它可以应用在这里。鉴于文档中的示例,我想要从捆绑 x 中删除 w 和 z,因为我知道 console.log(z(5) * w(2)); 永远不会被执行。不过它仍然会在捆绑包中。我在这里做一些愚蠢疯狂的事情。但是,嘿,我从非编辑器包中删除了 20%!

标签: javascript browserify commonjs uglifyjs envify


【解决方案1】:

我想我已经找到了一个行之有效的解决方案。作为奖励,它不需要触及现有代码(例如添加process.env. 检查)。我写了一个 browserify 转换,给定一个模块/文件列表,用{} 替换他们的require 调用。这样它们就完全从生成的包中删除了。无论如何,这只是一个黑客行为。我知道。

之前:

var thing = require('./thing.js');
var pica = require('pica');

之后:

var thing = {};
var pica = {};

使用https://www.npmjs.com/package/browserify-transform-tools 这只是几行代码,因为它已经提供了makeRequireTransform 帮助程序。我将在这里转储代码并建议任何人都不要使用它,除非您确切知道自己在做什么。

derequire.js

var path = require('path');
var resolve = require('resolve');
var transformTools = require('browserify-transform-tools');

var options = {
    evaluateArguments: true,
    jsFilesOnly: true
};

var cache = {};
var resolveDerequire = function(moduleName) {
    var fromCache = cache[moduleName];

    if(fromCache) {
        return fromCache;
    }

    return require.resolve(moduleName);
};

var transform = transformTools.makeRequireTransform('derequire', options, function(args, transformOptions, done) {
    var requiredModule = args[0];
    var basedir = path.dirname(transformOptions.file);

    var shouldDerequire = transformOptions.config.modules.some(function(moduleToRequire) {
        try {
            //The normal require which respects NODE_PATH.
            return require.resolve(requiredModule) === resolveDerequire(moduleToRequire);
        } catch(ex1) {
            try {
                //A local require relative to the current file.
                return resolve.sync(requiredModule, {basedir: basedir}) === resolveDerequire(moduleToRequire);
            } catch(ex2) {
                console.error(ex1, ex2);
                return false;
            }
        }
    });

    if(shouldDerequire) {
        done(null, '{}');
    } else {
        done();
    }
});


module.exports = transform;

用于转换的 package.json 配置

"browserify": {
    "transform": [
        "babelify",
        [
            "./derequire.js",
            {
                "modules": [
                    "pica",
                    "exif-js",
                    "./src/thing.js",
                    "components/TextEditor.jsx",
                    "lib/FileUploader.js"
                ]
            }
        ],
        "browserify-css",
        "imgurify",
        "glslify"
    ]
},

【讨论】:

    【解决方案2】:

    我认为你所追求的是像uglifyify 这样的转换。

    Uglifyify 为您提供了在每个文件包含在包中之前对其应用 Uglify 的“挤压”转换的好处,这意味着您可以删除条件要求的死代码路径。

    请注意,您仍希望在结果输出上运行 uglifyjs

    【讨论】:

    • 你说的完全正确!这正是我最初想要的。我将坚持我现在拥有的解决方案,因为它会产生更小的捆绑包。但 uglifyify 是不那么老套的版本。
    • 这应该会产生与您的自定义转换步骤相同大小的包,并具有自动处理项目更改的额外好处。如果你得到比这更小的包,那么很可能你的包已经删除了它实际上依赖的代码
    猜你喜欢
    • 2016-06-12
    • 2017-07-11
    • 1970-01-01
    • 1970-01-01
    • 2012-02-17
    • 1970-01-01
    • 2019-01-30
    • 1970-01-01
    • 2015-06-02
    相关资源
    最近更新 更多