记一前端缺陷

昨天遇到了一个前端问题,作为这个方面的新人,基本上无力解决。在美丽的前端小姐姐的指点下,直到今天才弄明白这个问题。

背景

某个 Web App,可以从本地上传一张图片(但只留在浏览器端),图片缓存在会话中。刷新或者跳跃回本页的时候,从会话缓存里取出缓存绘制出来。而在上传图像之前,绘图板保持空白。图像输入,不论是用户上传,还是从缓存里加载,都是自由模糊类的输入,为此注册了画板的 onerror 事件,显示出错误消息框。错误消息框上的提示信息,类似是「无法加载图片」,但本 App 有国际化要求,视某个选项的可能变成其他语言。所以信息的文本实际上是从 i18n 的配置中提取出来的。

问题

测试出现了 BUG。在不加载任何图片,刷新页面时,有一定的概率会弹出无法加载图片的错误消息框。而且错误消息并不是正确国际化的内容,而是到此配置项目的键路径,类似是:webapp.cannot_load_image

有经验的前端小姐姐很轻松的找出了弹出错误框的原因,在画框的 标签里,错写了 src="#" 属性。这会让浏览器加载到此时请求一张跟当前 URL 路径一致的资源,并当作图片解析。也就是得到了本页的 HTML,却要认为它是图片,这是产生错误的根本原因。

这只是一个不大不小的错误,在编码过程中出现这种情况是很正常的现象。只要将 src 属性整个删去即可。但这个现象距离完全解释清楚还有很长的路要走。

  • 为什么这个错误不是稳定产生的,而是随机产生的?
  • 为什么国际化出了错误?

实际上,每次因为 src 的错误加载了错误的图像,HTML 一定会被请求过来,但是这个请求会不会产生错误,要看这时的 onerror 事件有没有注册。惯例来说,页面的 Js 会控制在 DOM 加载完成之后在注册这类事件,而这个时机和错误的图片返回回来的时机谁先谁后完全不可知。

所以说,在初始加载的过程中,我们不能期望 onerror 发挥作用。

国际化错误的原因肯定跟国际化方案联系在一起,此页加载了国际化工具 Js:some_i18n_prepare.js,但此准备工作的进行却是异步的。类似:

window.i18n.prepare(foo, bar).call(() => {
   $(document).trigger('i18n-ok');
   // something else
});

所以,在 src 加载触发了错误,并触发了 onerror 时(一定在 DOM 加载完之后),i18n 库的准备还没有完成,此时提出的错误消息,是一个没替换的键就不足为奇了。实际上,这个 some_i18n_prepare.js 已经为此准备了接口,在加载完成之后会触发 i18n-ok 事件。

上传图片此事不需要过度担心;空图片的情况,去掉 src 即可解决;而从缓存加载图片时,为了保证 onerror 在错误时有效,也应该将加载过程放在 i18n-ok 事件的回调里。

此外,这还解决了另一个此 App 上,以此同样的原因产生的问题,这也就是题外话了。

番外

在定位问题的过程中,小姐姐还教导我,Js 中注册的任务有微任务和宏任务之分,并且利用 setTimeout 强行让注册错误发生在 i18n 准备之后。也是一番神仙操作。

参见

  • 有关微任务。

  • 图片渲染时机。

你可能感兴趣的:(记一前端缺陷)