文档
/

故障排查

/

避免标识符冲突

避免混淆文件之间的标识符冲突

当多个经过 VM 混淆的文件被打进同一个包时,它们可能声明相同的顶层标识符,导致页面在解析阶段崩溃。下面说明原因以及解决方法。

症状

构建运行正常,但浏览器在应用启动之前、解析阶段就抛出错误:

Text

要查看有多少个 chunk 声明了错误信息中的名称,请统计包含该名称声明的文件数量:

Text

-l 让每个匹配的文件只列出一次,-w 只匹配完整的单词,因此不会把 vmdX 计算在内。这里没有包含 var,因为重复的 var 声明本身并不是错误。结果大于 1 就说明该名称在多个 chunk 中被声明。请把 vmd 替换为您的错误信息中的名称。

只有当这些声明处于同一作用域时,标识符冲突才会造成问题。同一个名称在不同模块或闭包中重复出现,并不能证明存在冲突;上面的解析错误才是证据。

原因

VM 预设把 Identifier Names Generator 设为 mangled-shuffled。它以打乱后的顺序遍历一个小字母表,并且在 VM 混淆下,每个被重命名的全局变量都会加上 vm 前缀(即默认的 identifiersPrefix)。打乱后的顺序按进程缓存,因此在同一进程中混淆的文件,或使用相同固定 seed 混淆的文件,都会从同一个序列中取名:每个文件的第一个全局变量名称相同,第二个也相同,依此类推。

每个文件都是独立混淆的,生成器对每个文件都会从序列的开头重新开始。当两个文件最终处于同一作用域时,名称就会冲突。两个顶层的 const vmd = … 声明落在同一作用域中,解析器会拒绝第二个。

会引入更多顶层标识符的选项,会增加两个文件生成相同名称的几率。vmWrapTopLevelInitializers 就是其中之一,而所有 VM 预设都已启用它;vmDynamicOpcodes 或 vmBytecodeEncoding 等选项还会引入更多。

解决方法

优先先打包,再对整个包进行一次混淆。如果确实要分别混淆,并且这些脚本共享同一个全局作用域,请从以下方法中任选其一:

  • 启用 randomIdentifiersPrefix(推荐)

    每次混淆都会得到一个随机前缀,添加到每个全局标识符之前(在 VM 混淆下它会取代默认的 vm 前缀)。不同文件的名称不再共享同一个命名空间,因此冲突会消失,而无需手动协调前缀。

  • 为每个文件设置唯一的 identifiersPrefix

    在混淆每个文件时手动传入不同的前缀(例如一个用 identifiersPrefix: 'auth_',另一个用 checkout_)。这样有效,但文件较多时容易出错,建议优先使用上面的随机前缀。

    JavaScript

  • 将 identifierNamesGenerator 切换为 hexadecimal

    十六进制名称的取值空间大得多,因此两个文件生成相同标识符的可能性要小得多,但并不能保证唯一。代价是:标识符比 mangled-shuffled 的输出更长,包体会略微增大。

  • 使用多文件批量界面,或检查打包工具插件

    使用多文件批量混淆(付费套餐可用)时,控制台会为每个文件添加不同的前缀。如果使用打包工具插件,请核实它的行为,而不要想当然地认为它也会这样做。如果通过 npm API 手动接入混淆,则需要自己启用前缀。

要验证修复结果,请重新构建并重新加载合并后的应用,确认 SyntaxError 已经消失。旧错误信息中的名称不应再在多个 chunk 中被声明;前缀会改变每一个生成的名称,因此上面的 grep 只能告诉您那个特定名称的情况,而无法反映新的名称。

相关选项