可通过静态分析+运行时探测+构建期拦截组合策略在打包阶段发现并隔离异步模块中的死逻辑:一、识别条件导入恒假、路径无效、导出未引用、promise无副作用四类形态;二、在transform阶段检查路径可解性与条件折叠,generatebundle阶段追踪导出引用;三、用空桩替换而非删除,并生成报告;四、结合ts配置与define常量提升检测精度。

自定义构建脚本无法直接“识别死逻辑”,但可以通过静态分析 + 运行时探测 + 构建期拦截的组合策略,在打包阶段发现并隔离异步模块中实际不会执行的代码分支(即所谓“死逻辑”)。关键不在于运行时判断,而在于构建期对模块依赖图、条件表达式、导出引用关系的精准建模。
一、明确“死逻辑”在异步模块中的典型形态
异步加载场景(如 import()、require.ensure)下的死逻辑,往往不是语法错误,而是语义失效:
-
条件导入永远不满足:例如
if (false) await import('./feature-a.js')或基于编译期已知常量的判断 -
动态路径拼接结果为无效/不存在模块:如
await import(`./features/${env}/not-used.js`),其中env在构建时固定为'prod',但not-used.js在prod目录下根本不存在或未被构建 -
模块导出未被任何后续调用引用:模块被加载,但其导出的函数/类从未在任何
then、await或回调中被使用(即“加载即丢弃”) -
Promise 链中途被忽略且无副作用:如
import('./utils.js').then(() => {}),回调为空,且该import不参与任何状态变更或副作用触发
二、在构建脚本中注入三阶段检测逻辑
以 Webpack 或 Vite 插件为例,可在 buildStart → transform → generateBundle 阶段分步介入:
-
静态路径可解性检查(transform 阶段):对所有
import(...)表达式做 AST 解析,提取字符串字面量或可推断的模板字符串。若路径能静态确定(如不含变量或变量值在构建环境里已知),则尝试在resolve前模拟查找——调用this.resolve()并捕获失败。失败即标记为潜在死加载 -
条件表达式常量折叠分析(transform 阶段):利用
@babel/plugin-transform-constant-folding或自定义 visitor,识别if (DEBUG === false)类条件。若条件恒假且包裹import(),直接标记该import节点为 dead -
导出引用追踪(generateBundle 阶段):遍历最终 chunk 的 AST,对每个异步引入的模块,检查其命名导出是否出现在任何
CallExpression、MemberExpression或NewExpression中。若全部导出均未被访问,且无import * as或默认导出调用,则判定为“加载无用”
三、隔离方式:重写而非删除,保留调试线索
直接删除可能破坏模块结构或 sourcemap 映射。更稳妥的做法是构建期重写:
- 将疑似死逻辑的
import()替换为返回Promise.resolve({})的空桩,同时插入注释说明:/* DEAD IMPORT: ./legacy/unused.js (reason: path unresolved) */ - 对恒假条件块,保留外层
if,但将内部import()替换为Promise.resolve(null),并添加__DEV__ && console.warn(...)提示 - 生成一份
dead-import-report.json,记录文件位置、原因、原始代码片段,供人工复核
四、配合 TypeScript 和构建配置提效
死逻辑检测效果高度依赖类型与配置信息:
- 启用
isolatedModules: true和verbatimModuleSyntax: true,让 TS 编译器提前暴露非法动态导入 - 在
tsconfig.json中通过paths和baseUrl明确模块解析规则,避免构建脚本误判路径有效性 - 使用
define注入构建常量(如__ENV__ = 'prod'),使条件判断可在 transform 阶段被折叠,而非留到运行时
不复杂但容易忽略:死逻辑的本质是“构建期可证伪的冗余执行”。它不在运行时爆发,却悄悄拖慢首屏、增大包体积、干扰 tree-shaking。自定义脚本的价值,是把模糊的“可能不用”变成可审计、可隔离、可追溯的具体节点。











