webpack,vite为JS、HTML、CSS开启条件编译,宏剔除,代码剔除
需求背景/用户故事
- 不使用mock的那部分假数据,总不能放在代码里发布生产环境吧?
- debug工具代码,如移动端比较流行的vconsole插件,我们不希望测试前引入这份代码,发布生产的时候手动关闭这部分代码吧,我们需要一个功能去自动化引入和移除。
- 使用JS做游戏,开发时引用了大量debug代码,然后打包后因为代码中还有引用没有成为dead code被剔除,从而导致了包体较大。
- 希望开发时用的测试API接口不会出现在打包后的代码中。
- 网页在桌面端和移动端希望使用不同的CSS,互相不影响包体。
在以上这些情况中都很适合做条件编译,让不希望在生产环境出现的相关代码彻底消失。
那么在目前主流的vite,webpack打包工具中如何实现这种效果呢。
// main.js
if (DEBUG) {
console.log("debug stuff");
const donotLikeDropConsole = 'donotLikeDropConsole';
console.log(donotLikeDropConsole);
}else{
console.log("noting");
}
// dist.js
console.log("noting");
方式1 宏注释
这种方式非常简单,但不太灵活,原理是利用注释声明起点和终点,再利用正则删除注释和注释中的代码。
这里给出Unix下使用sed的示例
# 去除文件夹echatim 下所有IFTRUE_WXAPP的平台相关代码
for f in `find . echatim -name '*.ts'`; do echo $f && sed -e ':a' -e 'N' -e '$!ba' -e 's/[ ]*\/\*IFTRUE_WXAPP\*\/.*\n.*\/\*FITRUE_WXAPP\*\// /g' -i '' $f; done
也可以使用webpack下的js-conditional-compile-loader插件来实现,但它目前只支持JS
module: {rules: [{
test: /\.tsx?$/i,
use: [{
loader: 'ts-loader',
options: {configFile: path.resolve(__dirname, '../tslint.json')}
},
// 引入js-conditional-compile-loader插件
{
loader: 'js-conditional-compile-loader',
options: {
isDebug: process.env.NODE_ENV === 'development', // optional, this is default
WEBAPP: process.env.platform === 'web', // any name, used for /* IFTRUE_WEBAPP ...js code... FITRUE_WEBAPP */
WXAPP: process.env.platform === 'wx', // any name, used for /* IFTRUE_WXAPP ...js code... FITRUE_WXAPP */
// RNAPP: process.env.platform === 'rn', // any name, used for /* IFTRUE_RNAPP ...js code... FITRUE_RNAPP */
}
}
],
exclude: /node_modules/
}]}
具体使用方式如下
// 以下的代码仅会在设置WEBAPP:true时才会条件编译
/*IFTRUE_WEBAPP*/
let webfetch = Fetch.getFetchToolkit();
return webfetch(url as string, request).then(response =>{
return response.json();
}).then(res =>{
console.log(`==> [${request.method}] ${url} back:` + Beans.json(res));
const resp = this.response2ApiResponse(res);
if(resp.isFailed()){
// return Promise.reject(new Error(Beans.json(resp)));
return Promise.reject(resp);
}
return Promise.resolve(this.response2ApiResponse(res));
});
/*FITRUE_WEBAPP*/
方式2 UglifyJsPlugin
利用UglifyJsPlugin插件去除死代码(不执行的代码块),使用方式非常简单,但该插件只能在webpack中使用
compress: {
warnings: false, // 去除warning警告
dead_code: true, // 去除不可达代码
}

方式3 conditional-webpack-plugin
如果希望条件编译能够覆盖项目中的其他文本文件,可以参考这个插件和其实现。
https://www.npmjs.com/package/conditional-webpack-plugin
方式4 Terser
上面的一些插件的底层就是Terser,这是一个非常著名的代码混淆、压缩库,条件编译的实现也是依赖他,相关中文文档看这里https://github.com/LiPinghai/UglifyJSDocCN/tree/UglifyJs2
使用Terser的Global definitions加dead code剔除实现,Global definitions支持命令行输入和compress参数中的global_defs 对象作为输入,点击这里查看官方文档https://github.com/terser/terser#conditional-compilation
📝 terser Vite配置
利用vite内置的terser,调整选项即可实现条件编译
export default defineConfig({
build: {
minify:false,
terserOptions:{
compress: {
global_defs:{
'DEBUG': false
}
}
}
}
})
📝 terser Webpack
webpack可以 使用TerserWebpackPlugin,并配置terser参数来实现Global definitions
module.exports = {
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
global_defs:{
'DEBUG': false
}
}
}
}),
],
},
};
📎 参考文章
- TerserWebpackPlugin | webpack 中文文档 (docschina.org)
- terser/terser: 🗜 JavaScript parser, mangler and compressor toolkit for ES6+ (github.com)
- javascript - webpack选择性编译DefinePlugin(打包自动剔除测试数据) - 原创小文章 - SegmentFault 思否
- 【webpack系列】----- 条件编译_webpack 条件编译-CSDN博客
- 实现webpack对某段代码不编译与原理浅析 - 掘金 (juejin.cn)
- E聊SDK在TypeScript下的条件编译(使用js-conditional-compile-loader插件)-CSDN博客
💡 欢迎您在底部评论区留言,一起交流~