程沛权
程沛权

百度地图API实例销毁方案 可用于Vue单页面项目

作者:程沛权2020/12/24

最近项目用到了百度地图的 API ,用倒是没啥毛病,就是发现用完之后留下来的垃圾太多 = =

项目是基于 Vue 做的,当时发现在需要百度地图的那个路由用完之后,切去其他页面,积累了很多 DOM,并且再次进入会再次创建,在项目里体验越久,DOM 树越累赘,并且由百度地图创建的定时器越攒越多。

这当然不是什么好事……

在接入的时候就发现了这个问题,查了好久都没有找到销毁的 API ,包括官方贴吧和各种博客都没有找到,打了个 TODO 直到今天才有空优化这个问题。

一开始尝试直接移除创建的 DOM ,但发现会带来另外的问题,控制台一片血海……(很明显是定时器在执行时找不到 DOM 而产生的报错…

根据打印出来的报错日志,可以看到这个报错是百度地图 API 的 JS 文件产生出来的。

通常会产生这种情况的无非就是 setTimeoutsetInterval 了,在百度地图 API 的源码里找到了相关的代码 setInterval(e._watchSize, 16) ,确实是一直在监听 DOM 的尺寸变化。

那么解决思路就有了,在移除 DOM 的同时,顺带清除这些定时器。

平时我们清除定时器都是通过 const XXX = setInterval( .... ) 的形式定义定时器,然后再 clearInterval(XXX)去清除。

但百度地图的定时器因为封装并且混淆的太深,暂时得不到具体的变量,所以采用了一个全局清除的 Hack 方案来解决该问题。

最终相关的销毁代码如下:

/**
 * 销毁百度地图
 */
try {
  // 注销地图(换成你初始化时定义的地图变量)
  map = null

  // 获取interval的最高ID然后遍历清除
  const HIGHEST_INTERVAL_ID = setInterval(';')
  for (let i = 0; i < HIGHEST_INTERVAL_ID; i++) {
    clearInterval(i)
  }

  // 获取百度地图创建的DOM并销毁
  const BAIDU_MAPS = document.querySelectorAll('.tangram-suggestion-main')
  BAIDU_MAPS.forEach((item) => {
    document.body.removeChild(item)
  })
} catch (e) {
  // console.log(e);
}

Vue 项目我是放到了组件卸载后的生命周期里执行(Vue 2.x 是 destroyed,3.x 是 onUnmounted)。

在上线这个方案之前有考虑到会不会误伤到其他定时器,但和组里的同学讨论过后,基本上都是有良好的使用习惯,就是创建 setInterval 之后都有习惯在达成目的后执行销毁,所以按照这个习惯来说,理论上不会带来很大的影响。

如果有更好的解决方案欢迎分享,销毁这一块实在是搜不到什么解决方案,自己也是排查了半天,不知道为什么官方就不能出一个销毁的 API 直接调。