灵异事件之薛定谔的白屏
场景复现
1 | <html> |
main.js:
1 | require.config({ |
控制台会出现报错Error: Mismatched anonymous define() module: function(){return y}, 但是有时候白屏有时候正常。
分析原因
可知白屏是因为标记为main logic的代码没有正常工作导致的。
分析下来可得如下结论
当出现错误时代码执行顺序为:
1 | -> require.min.js |
当出现正确时代码执行顺序为:
1 | -> require.min.js |
易得是代码执行时序的问题。那么问题来了, 为什么会出现这种情况?
源码分析
我们可以看一下requirejs的源码: https://github.com/requirejs/requirejs/blob/HEAD/require.js
当我们执行sha1.min.js时, sha1.min.js检测到当前有amd环境,调用define将自己注入到requirejs的运行时中, 因为没有调用require相关方法, 因此requirejs将其定义推入自身的globalDefQueue中。(相关代码: L2061)。
当我们执行main.js时, 调用require.config时, requirejs会尝试消费所有的globalDefQueue, 此时在queue中的参数为[null, [], function(){…}], 因为第一个参数(name)为null, 则会抛出异常Mismatched anonymous define() module...(相关代码: L1244)
requirejs 会通过调用自身的onError方法抛出异常, 如果没有手动覆盖onError的话会调用内置的defaultOnError方法(相关代码: L1870), 而defaultOnError的实现很简单:
1 | function defaultOnError(err) { |
直接向顶层抛出异常, 导致整个script的运行时中断,后续的代码当然无法执行。
解决方案
可以看见原来的实现是有onError的覆写的, 只不过因为在require.config之后执行导致没有执行。
最佳的解决方案是尽可能早的覆写requirejs.onError方法。
吐槽
如果看源码的话。可以看到requirejs写明了允许匿名模块
Allow for anonymous modules
但是实际使用中却会报错。而且默认的报错是直接向顶层抛出。
这种情况就是A做错了,但是却导致B无法正常执行。这种场景非常难debug