本文已参加「新人创作礼」活动,一起开启掘金创作之路。
背景
前端时间做了vue2内嵌vue3的项目,具体可以看之前的一篇文章vue2接入vue3,微前端初探手记(一)。
当时也总结了通过iframe嵌套的方式,不可避免的几个问题:慢、内存堆积、焦点丢失等问题。
原本计划是通过调研和选取其他的微服务方案来替代iframe这种简单粗暴的方式,然鹅,计划是美好的,项目却十万火急。
随着源源不断的需求和bug修复,微前端的调研方案被搁置了。而目前来看,首要解决的便是iframe加载数据慢和焦点丢失的问题。
问题原因解析
加载慢
由于做的是一个医生写报告的系统,每次打开iframe都是医生对一个患者的诊断,医生每天要处理几十上百个患者,而从技术的角度来看,经历了以下耗时的过程:
- 需要等待iframe渲染完成(2s左右)
- 再通过postMessage发送大量渲染报告模板的数据到iframe内部(由于无法精准把控iframe渲染过程,用了setTimeout 2s才能保证95%的情况保证iframe渲染成功,且listener创建成功)
- 主页面向iframe内部发送数据,iframe进行渲染(1s左右)
从上面一段时间可以看出,iframe接收到数据的渲染时长是不可避免的,但是iframe渲染完成,以及主页面的等待时间上,是有明显优化空间的。
焦点问题
其实大部分情况下,是不太需要考虑到焦点问题,但是当你的主页面需要进行一些拖拽操作的时候,你会发现,当拖拽区域不小心经过了iframe,你的mouseout是会提前触发的。
这样带来的后果就是:
- 拖拽经过iframe的时候,感受到明显的卡顿
- 鼠标在iframe区域抬起,但是从主页面来看,并没有感知到mouseup事件,窗口还是跟着你的鼠标逃串。
解决方案
监听iframe的监听
无论你的子项目是vue2还是vue3,总有一个时机做了监听的注册,而主页面向子页面派发数据并不依赖iframe组件的渲染,只需要iframe内部的监听建立完成。
就像你让父母给你打生活费的时候,并不需要守在银行提款机旁边,并且插卡,你父母来预算你做这些事情需要的时间,再精准的大款给你。
因此,目前的方案是,在iframe内部的addEventListener后面直接粗暴的派发一个事件,告诉子组件:我的监听已经建立好了,你不用管我的页面啥状态,给我发数据吧。
window.parent.postMessage(
{
type: 'listenersReady',
pageId: pageId.value
},
'*'
);
二父组件的监听必然是先于iframe完成的,父组件监听到listenersReady,便直接把钱打过去,哦不,是把数据发过去。
适时的把iframe盖起来
拖拽问题其实解决方案也是同样的粗暴但是有效。
因为我只需要解决在主页面中的拖拽不会因为路过iframe受影响。
实现思路是:
- 建立一个vuex数据,标志位isDragging,默认为false。
- 每次涉及到iframe上层的拖拽(放大缩小、位置移动等),在mousedown时,便将标志位isDragging设置为true。
- mousemove过程中,检测到isDragging为true,在iframe上面放置一个同样大小的不可见蒙版,这样在整个拖拽过程中,iframe便被巧妙的盖起来了。
- mouseup也就是一次拖拽完成,将标志位isDragging设置为false。
总结
以上思路,既将报告(iframe)渲染过程从5s左右提速到1s多,又解决了焦点混乱拖拽卡顿的问题,保障产品目前是可用状态。