避免混淆文件之间的标识符冲突
当多个经过 VM 混淆的文件被打进同一个包时,它们可能声明相同的顶层标识符,导致页面在解析阶段崩溃。下面说明原因以及解决方法。
症状
构建运行正常,但浏览器在应用启动之前、解析阶段就抛出错误:
要查看有多少个 chunk 声明了错误信息中的名称,请统计包含该名称声明的文件数量:
-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_)。这样有效,但文件较多时容易出错,建议优先使用上面的随机前缀。将
identifierNamesGenerator切换为hexadecimal十六进制名称的取值空间大得多,因此两个文件生成相同标识符的可能性要小得多,但并不能保证唯一。代价是:标识符比
mangled-shuffled的输出更长,包体会略微增大。使用多文件批量界面,或检查打包工具插件
使用多文件批量混淆(付费套餐可用)时,控制台会为每个文件添加不同的前缀。如果使用打包工具插件,请核实它的行为,而不要想当然地认为它也会这样做。如果通过 npm API 手动接入混淆,则需要自己启用前缀。
要验证修复结果,请重新构建并重新加载合并后的应用,确认 SyntaxError 已经消失。旧错误信息中的名称不应再在多个 chunk 中被声明;前缀会改变每一个生成的名称,因此上面的 grep 只能告诉您那个特定名称的情况,而无法反映新的名称。
相关选项
randomIdentifiersPrefix- 每次构建使用随机前缀(推荐)identifiersPrefix- 显式指定的前缀字符串identifierNamesGenerator- 名称形式(mangled、hexadecimal、…)
