plainheart / echarts-extension-amap

🚩 An AMap (https://lbs.amap.com) extension for Apache ECharts (https://github.com/apache/echarts)
https://github.com/plainheart/echarts-extension-amap
MIT License
251 stars 83 forks source link

使用定时器刷新数据后导致内存泄漏 #39

Closed hwk12 closed 2 years ago

hwk12 commented 2 years ago

使用echarts-extension-amap让echarts导入高德地图加geo覆盖物,设置了数据刷新会clear charts 重新setOption 下面为setOption的方法

setOptions(data, linesData, map, clear = true) {
      if (!this.chart) {
        return
      }
      const projectCoordList = data => {
        if (JSON.stringify(data) !== '{}') {
          return data.projectCoordList
            .map(({ center, name, value }) => {
              if (center) {
                center.push(value)
              }
              return { name, value: center }
            })
        }
      }
      const tenantCoordList = data => {
        if (JSON.stringify(data) !== '{}') {
          return data.tenantCoordList
            .filter(item => item.center)
            .map(({ center, name, value }) => {
              center.push(value)
              return { name, value: center }
            })
        }
      }
      const convertLinesData = data => {
        return data.linkCoordList
      }
      var maxValue
      var projectMaxValue
      if (JSON.stringify(data) !== '{}') {
        maxValue = Math.max(1, ...data.tenantCoordList.map(t => t.value))
        projectMaxValue = Math.max(1, ...data.projectCoordList.map(t => t.value))
      }
      const option = {
        backgroundColor: 'transparent',
        geo: {
          map,
          label: {
            emphasis: {
              show: false
            }
          },
          roam: true, // 是否允许缩放
          layoutCenter: ['50%', '50%'],
          layoutSize: '70%',
          itemStyle: {
            normal: {
              areaColor: 'rgba(51, 69, 89, .5)',
              borderColor: 'rgba(100, 149, 237, 1)'
            },
            emphasis: {
              areaColor: 'rgba(37, 43, 61, .5)'
            }
          }
        },
        amap: {
          center: [120.637376, 30.422934],
          resizeEnable: true,
          mapStyle: 'amap://styles/darkblue', // 地图样式白色
          // rotation: 10,
          renderOnMoving: true,
          echartsLayerInteractive: true,
          largeMode: false,
          returnMapCameraState: true,
          roam: true,
          zoom: 11, // 缩放
          viewMode: '3D' // 是否启用3d地图
          // pitch: 35, // 视角高度
          // skyColor: '#33216a'
        },
        animation: false,
        tooltip: {
          trigger: 'item',
          backgroundColor: 'rgba(12, 204, 104, 0.92)',
          borderColor: '#FFFFCC',
          showDelay: 0,
          hideDelay: 0,
          enterable: true,
          transitionDuration: 0,
          extraCssText: 'z-index:100',
          formatter: (params, ticket, callback) => {
            const name = params.name
            let value = params.value
            if (!name) {
              return ''
            }
            if (typeof value === 'object') {
              value = value[2]
            }
            if (isNaN(value)) {
              value = 0
            }
            if (value > 10000) {
              value = (value / 10000).toFixed(2)
              const res =
              "<span style='color:#fff;'>" + name + '</span><br/>数据:' + value + ' 万方'
              return res
            } else {
              value = this.formatNumber(value)
              const res =
              "<span style='color:#fff;'>" + name + '</span><br/>数据:' + value + ' 方'
              return res
            }
          }
        },
        series: [
          {
            name: 'lines1',
            type: 'lines',
            coordinateSystem: 'geo',
            zlevel: 1,
            effect: {
              show: true,
              period: 6,
              trailLength: 0.7,
              color: '#fff',
              symbolSize: 4
            },
            lineStyle: {
              normal: {
                color: '#eac736',
                width: 0,
                curveness: 0.2
              }
            },
            data: convertLinesData(data)
          },
          {
            name: 'lines2',
            type: 'lines',
            coordinateSystem: 'geo',
            zlevel: 2,
            effect: {
              show: true,
              period: 6,
              trailLength: 0,
              symbol:
                'path://M.6,1318.313v-89.254l-319.9-221.799l0.073-208.063c0.521-84.662-26.629-121.796-63.961-121.491c-37.332-0.305-64.482,36.829-63.961,121.491l0.073,208.063l-319.9,221.799v89.254l330.343-157.288l12.238,241.308l-134.449,92.931l0.531,42.034l175.125-42.917l175.125,42.917l0.531-42.034l-134.449-92.931l12.238-241.308L1705',
              symbolSize: 15
            },
            lineStyle: {
              normal: {
                color: '#eac736',
                width: 1,
                opacity: 0.4,
                curveness: 0.2
              }
            },
            data: convertLinesData(data)
          },
          {
            name: 'blueDot',
            type: 'scatter',
            coordinateSystem: 'geo',
            data: tenantCoordList(data),
            // data: [
            // ],
            // data: convertScatterData(data.filter(t => t.type !== 'project')),
            // 非工程显示原型(搅拌站、公司),即蓝点
            symbolSize: (val) => {
              if (maxValue > 0) {
                return Math.max(10, (val[2] * 20) / maxValue)
              }
              return 10
              // return 20
            },
            // label: {
            //   normal: {
            //     formatter: function(params) {
            //       let name = params.name
            //       let value = params.value[2]
            //       console.log('con', typeof value)
            //       let res = name + '\n' + value + ' 方'
            //       return res
            //     },
            //     position: 'right',
            //     align: 'center',
            //     show: true
            //   },
            //   emphasis: {
            //     show: true
            //   }
            // },
            itemStyle: {
              normal: {
                color: '#05C3F9'
              }
            }
          },
          {
            name: 'projectGeo',
            type: 'scatter',
            coordinateSystem: 'geo',
            data: projectCoordList(data),
            // data: convertScatterData(data.filter(t => t.type === 'project')),
            // 工程显示原型,即绿点
            symbolSize: val => {
              if (projectMaxValue > 0) {
                // return Math.max(15, (val[2] * 15) / projectMaxValue)
                return Math.max(10, (val[2] * 20) / projectMaxValue)
              }
              return 10
              // return 20
            },
            // label: {
            //   normal: {
            //     formatter: '{b}',
            //     position: 'right',
            //     show: true
            //   },
            //   emphasis: {
            //     show: true
            //   }
            // },
            itemStyle: {
              normal: {
                color: '#90EE90'
              }
            }
          }
        ]
      }
      axios
        .get('/geo-json/100000/330000/330400/330481.geoJson', { timeout: 60000 })
        .then(({ data }) => {
          const geojson = new AMap.GeoJSON({
            geoJSON: data,
            // 还可以自定义getMarker和getPolyline
            getPolygon: function(geojson, lnglats) {
            // 计算面积
              // var area = AMap.GeometryUtil.ringArea(lnglats[0])

              return new AMap.Polygon({
                path: lnglats,
                fillOpacity: 0.4,
                // fillOpacity: 1 - Math.sqrt(area / 8000000000), // 面积越大透明度越高
                strokeColor: '#008B8B',
                fillColor: '#4169FF'
              })
            }
          })
          const amap = this.chart.getModel().getComponent('amap').getAMap()
          amap.add(geojson)
          console.log('GeoJSON 数据加载完成')
        })
        .catch(error => {
          const message = parseRequestError(error)
          console.log('error', message)
        })
      console.log('clear', clear)
      if (clear) {
        this.chart.clear()
      }
      this.chart.setOption(option)
    },

坐标系现在是geo,用来和amap进行对比的,发现geo为坐标系并不会出现内存泄漏现象,使用amap才会,请问怎么解决这个问题,是否有相关api

plainheart commented 2 years ago

这个我怎么能快速地复现一下?当前给出的代码无法直接执行,也显得有些复杂。能用 Codepen 提供个简化之后的在线例子吗?

hwk12 commented 2 years ago

我已经通过邮件将相关文件发给你,不知道有没有收到。只要将vue文件作为组件引入就行,在public里面放入一个包含geo的文件夹即可重现。

plainheart commented 2 years ago

似乎没有收到,你发送的是邮箱地址是?

hwk12 commented 2 years ago

466958177@qq.com

plainheart commented 2 years ago

可能是你填错邮箱地址了,邮箱地址在我的 GitHub 主页,稍后麻烦你再发一遍吧。

hwk12 commented 2 years ago

可能是你填错邮箱地址了,邮箱地址在我的 GitHub 主页,稍后麻烦你再发一遍吧。

已发送到yhen@all-my-life.cn

plainheart commented 2 years ago

代码里为什么在每次刷新前要 clear 一下?这样会导致整个地图完全重新加载。不过根据你这个用法也发现了在 clear 的时候,现在的逻辑会出现地图实例可能无法被正常销毁的 bug。

hwk12 commented 2 years ago

代码里为什么在每次刷新前要 clear 一下?

我是在公司老前端的一些代码基础上改的,他的代码思路比较完善,所以我保留了clear。主要感觉clear总比不clear更完善一点,可能可以避免一些其他问题,是否会带来什么问题,暂时没遇到

hwk12 commented 2 years ago

代码里为什么在每次刷新前要 clear 一下?这样会导致整个地图完全重新加载。不过根据你这个用法也发现了在 clear 的时候,现在的逻辑会出现地图实例可能无法被正常销毁的 bug。

我找到了要用clear的原因,我下面还注释掉了一部分代码,用axios的,我还给高德的amap加了一层geo的覆盖层,如果不用clear,覆盖层会越叠越厚,这个同样占内存,但是2边大部分相似的代码,AMap没有定义,简化代码的不能使用,我原始的那个可以

hwk12 commented 2 years ago

我找到简化出问题的原因了,index.html里面的被我注释了。这样去掉clear,我怎么清除我添加的这个覆盖层

plainheart commented 2 years ago

这个要调用高德地图本身的API,添加是 amap.add 移除是 amap.remove

hwk12 commented 2 years ago

这个要调用高德地图本身的API,添加是 amap.add 移除是 amap.remove

remove使用后没有效果,不知道是不是我无法指定的问题。 现在我把remove换成clearMap const amap = this.chart.getModel().getComponent('amap').getAMap() amap.clearMap() amap.add(geojson) 然后去掉了echarts的clear,效果层面比原来更好,内存还是会在每次数据刷新时小幅度上涨,但是不会下降,总体上应该泄漏的更少,这个amap是不是和高德的map还是不同

plainheart commented 2 years ago

map 实例和高德地图是一样的,amap.remove(geojson) 应该是可以的,如果不可以可能是你的用法有问题。

hwk12 commented 2 years ago

内存泄漏问题终于解决了,十分感谢您的帮助。虽然昨天本地项目就可以正常运行,但因为我们公司把项目并入了微前端,在微前端上无法初始化界面,用了高德相关的api提前初始化就会导致内存泄漏的问题,这也是clearMap以后内存还在泄漏的原因。今天终于找到无法初始化的原因,在微前端的index.html上还有一份旧的高德引入的script,应该和我引入的形成冲突,注释掉之后问题解决

hwk12 commented 1 year ago

Codepen我还没有试过,不知道外部文件怎么导入,怎么import进来 我专门把相关代码提取到一个新的vue的组件中,echarts的版本是4.2.1(关键) 额外多的是geo文件,连着geo-json文件夹放在public下面就行 1.初始选择的是amap为坐标系,js虚拟机的内存会因为数据刷新增加(采用一组定时器似乎会因为数据不变导致不刷新,所以采用了2组定时器刷新数据),下图红色的内存部分虽然会一直出现新的,但如果提前引入高德,可以防止增加(当初尝试的时候用的是公司引入高德的方法,不增加的原理不太清楚)。 所以目前困扰我最关键的是本地localhost的内存泄漏问题,每次刷新内存会大增,然后小幅度回退,但总体是增加的趋势 2.如果把amap换成geo坐标系,即把option里面4个坐标系的amap换成geo,数据刷新并不会引起内存泄漏

------------------ 原始邮件 ------------------ 发件人: "plainheart/echarts-extension-amap" @.>; 发送时间: 2022年7月18日(星期一) 上午9:42 @.>; @.**@.>; 主题: Re: [plainheart/echarts-extension-amap] 使用定时器刷新数据后导致内存泄漏 (Issue #39)

这个我怎么能快速地复现一下?当前给出的代码无法直接执行,也显得有些复杂。能用 Codepen 提供个简化之后的在线例子吗?

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>

plainheart commented 1 year ago

没有看到你所说的“下图红色的内存部分”。没有可复现的例子的话,就很难办了。如果需要用到编译环境,可以考虑用 codesandbox 来做 Demo,注意简化代码逻辑,只留下必要的部分。