DataEase 一个隐藏了半年的 Bug:从 Less 内部导入引发的血案
一段从未执行过的代码,如何在半年后突然爆炸
一、问题现象
某个普通的下午,DataEase 前端构建突然报错:
Uncaught ReferenceError: Cannot access 'M' before initialization
at http://localhost:8100/assets/chunk/appearance-873c27cb.js:9:17155
更诡异的是:
✅ 代码运行了整整半年都没问题
✅ 最近没有改动过相关代码
✅ 依赖版本也没有变化
❌ 突然就崩了
二、排查过程
2.1 初期的徒劳尝试
遇到这种"莫名奇妙"的错误,第一反应就是:
# 回滚代码
git checkout HEAD~10
# 重新安装依赖
rm -rf node_modules package-lock.json
npm install
# 升级/降级 Less 版本
npm install less@4.1.3
npm install less@3.13.1
# 修改 Vite 配置
export default defineConfig({
build: { minify: false },
optimizeDeps: { exclude: ['less'] }
})
结果:全部无效。
2.2 定位到具体文件
在 index.html 中添加最早期错误捕获:
<script>
window.onerror = function(message, source, lineno, colno, error) {
console.log('错误文件:', source);
console.log('错误堆栈:', error?.stack);
}
</script>
终于定位到元凶:
错误文件: http://localhost:8100/assets/chunk/appearance-873c27cb.js
错误堆栈: ReferenceError: Cannot access 'M' before initialization
at appearance-873c27cb.js:9:17155
2.3 找到源头
这个 appearance-xxx.js 对应的是 DataEase 项目中的 appearance.ts 文件。打开一看,发现了问题代码:

// appearance.ts
import colorFunctions from 'less/lib/less/functions/color.js' // ⚠️ 危险!
import colorTree from 'less/lib/less/tree/color.js' // ⚠️ 危险!
// ... 省略其他代码
if (this.themeColor === 'custom' && this.customColor) {
document.documentElement.style.setProperty(
'--ed-color-primary-light-5',
colorFunctions
.mix(new colorTree('ffffff'), new colorTree(this.customColor.substr(1)), { value: 40 })
.toRGB()
)
// ... 其他颜色处理
}
三、根本原因分析
3.1 什么是 Less 内部模块?
less/lib/less/functions/color.js 和 less/lib/less/tree/color.js 是 Less 编译器源码的内部模块,它们:
❌ 不应该在业务代码中直接导入
❌ 包含复杂的内部依赖和循环引用
❌ 不是公开 API,随时可能变化
3.2 为什么之前没发现?
关键在这段代码:
if (this.themeColor === 'custom' && this.customColor) {
// 这段代码半年没被执行过!
// 直到今天用户第一次选择"自定义主题色"
}
3.3 为什么现在暴露了?
用户第一次在 DataEase 系统设置中选择了"自定义主题色":
✅ themeColor === ‘custom’ 成立
✅ customColor 有值
✅ 代码路径第一次被执行
✅ Less 内部模块被实际调用
❌ 循环依赖暴露!
这就是传说中的"休眠 Bug"——代码一直存在,只等特定条件触发。
四、解决方案
4.1 ❌ 错误示范
// 不要这样做!不要直接导入 Less 内部模块!
import colorFunctions from 'less/lib/less/functions/color.js'
import colorTree from 'less/lib/less/tree/color.js'
4.2 ✅ 正确的解决方案
使用专业的颜色处理库 color:
# 安装 color 库
npm install color
npm install @types/color --save-dev
修改后的代码:
// 删除危险导入
// import colorFunctions from 'less/lib/less/functions/color.js'
// import colorTree from 'less/lib/less/tree/color.js'
// 使用专业的 color 库
import Color from 'color'
if (this.themeColor === 'custom' && this.customColor) {
const baseColor = Color('#' + this.customColor.substr(1))
// 设置基础颜色
document.documentElement.style.setProperty('--ed-color-primary', this.customColor)
document.documentElement.style.setProperty('--van-blue', this.customColor)
// light-5: 混合白色 40%
document.documentElement.style.setProperty(
'--ed-color-primary-light-5',
baseColor.mix(Color('#ffffff'), 0.4).hex()
)
// light-3: 混合白色 15%
document.documentElement.style.setProperty(
'--ed-color-primary-light-3',
baseColor.mix(Color('#ffffff'), 0.15).hex()
)
// 半透明色
document.documentElement.style.setProperty('--ed-color-primary-1a', `${this.customColor}1a`)
document.documentElement.style.setProperty('--ed-color-primary-33', `${this.customColor}33`)
document.documentElement.style.setProperty('--ed-color-primary-99', `${this.customColor}99`)
// dark-2: 混合黑色 15%
document.documentElement.style.setProperty(
'--ed-color-primary-dark-2',
baseColor.mix(Color('#000000'), 0.15).hex()
)
}
五、经验总结
5.1 给开发者的建议
🚫 永远不要直接导入库的内部模块
内部模块不是公开 API,随时可能变化
可能包含复杂的循环依赖
应该使用库的官方 API 或专业工具库
📊 代码覆盖率很重要
未执行的代码也可能是定时炸弹
条件分支需要测试覆盖
用户配置路径都要考虑到
🔧 选择合适的工具
颜色处理 → color 库
日期处理 → dayjs 或 date-fns
数值计算 → mathjs
不要"借用"其他库的内部实现
5.2 排查技巧
从错误堆栈定位到具体文件
检查新触发的代码路径
关注条件分支的执行情况
添加早期错误捕获(window.onerror)
5.3 预防措施
// ESLint 规则:禁止导入 less 内部模块
{
"rules": {
"no-restricted-imports": ["error", {
"patterns": ["less/lib/less/*"]
}]
}
}
六、结语
这个 Bug 教会我们:
“代码写对了不代表没问题,没执行过的代码永远有风险。”
它不是一个"新" Bug,而是一直潜伏在 DataEase 的代码里,等待被触发的那一天。半年来它都安静地睡着,直到用户第一次选择了自定义主题色,它才终于醒来。
排查这类问题的过程很痛苦,但解决后的成就感也是无可替代的。希望这篇文章能帮到同样踩坑的你。
如需沟通:lita2lz
422

被折叠的 条评论
为什么被折叠?



