条件compile/require使用browserify(死代码消除)
Conditional compile/require using browserify (dead code elimination)
我知道您不能有条件地 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;
}
效果很好,编辑器包已经更小了。因为我知道另一个 bundle 永远不会使用 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
。
我想我已经找到了一个足够有效的解决方案。作为奖励,它不需要修改现有代码(例如添加 process.env.
检查)。我写了一个 browserify 转换,给定 modules/files 列表,用 {}
替换他们的 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"
]
},
我想你想要的是像uglifyify
这样的转换。
Uglifyify gives you the benefit of applying Uglify's "squeeze" transform on each file before it's included in the bundle, meaning you can remove dead code paths for conditional requires.
请注意,您仍然希望对结果输出进行 运行 uglifyjs
。
我知道您不能有条件地 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;
}
效果很好,编辑器包已经更小了。因为我知道另一个 bundle 永远不会使用 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
。
我想我已经找到了一个足够有效的解决方案。作为奖励,它不需要修改现有代码(例如添加 process.env.
检查)。我写了一个 browserify 转换,给定 modules/files 列表,用 {}
替换他们的 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"
]
},
我想你想要的是像uglifyify
这样的转换。
Uglifyify gives you the benefit of applying Uglify's "squeeze" transform on each file before it's included in the bundle, meaning you can remove dead code paths for conditional requires.
请注意,您仍然希望对结果输出进行 运行 uglifyjs
。