Data内一个变量也会造成这么卡顿

570 阅读4分钟

最近在进行GIS开发时,遇到了一些与vue2搭配上的问题。特别是在使用Cesium进行3D地理信息系统的开发时,因为之前没有太多相关经验,所以主要是通过在网上查找资料和代码示例来学习(主要是Ctrl+C和Ctrl+V)。有一天,领导要求我们在水库的模型中加入动态水位的控制。

问题背景

领导的要求看似简单:在空的水库中加入水,并且水位能够动态变化。看上去只是一个普通的动画效果,但在实现过程中却遇到了意想不到的性能问题。

解决方案的初步设想

     CreateReservoir(positions, waterHeight) {
       this.Reservoir= viewer.entities.add({
          polygon: {
            hierarchy: new Cesium.PolygonHierarchy(positions),
            extrudedHeight: waterHeight,
            material: new Cesium.Color.fromBytes(64, 157, 253, 150),
            perPositionHeight: true,
          },
        });
      }

然后使用requestAnimationFrame变化到目标水位

性能问题

效果确实可以实现,也没什么问题,但是发现页面有点卡卡的感觉,因为刚开始这个水库周围都是比较平坦的,且暂时没有高程数据,就是围着这个水库画了一个多边形,想着可能标点有点多的问题。后来就想着使用还是感觉卡卡的,就使用了Primitive来创建水库水位,在进行逐步优化内部的一些方法。

//物体的创建
function useCreatePolygonGeometry(boundary, extrudedHeight) {
  return new Cesium.PolygonGeometry({
    polygonHierarchy: new Cesium.PolygonHierarchy(
      Cesium.Cartesian3.fromDegreesArray(boundary)
    ),
    extrudedHeight,
    vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
  });
}
function useDrawRiver(boundary, extrudedHeight, waterColor) {
//初始高度
  let riverHeight = extrudedHeight;
  //创建集合体传入初始高度和边界点
  const polygon = useCreatePolygonGeometry(boundary, riverHeight);

  riverInstance = new Cesium.Primitive({
    geometryInstances: new Cesium.GeometryInstance({
      geometry: polygon,
    }),
    appearance: new Cesium.EllipsoidSurfaceAppearance({
      aboveGround: true,
    }),
    show: true,
    asynchronous: false,
    releaseGeometryInstances: false,
  });

  const riverMaterial = new Cesium.Material({
    fabric: {
      type: "Water",
      uniforms: {
        baseWaterColor: new Cesium.Color(64 / 255.0, 157 / 255.0, 253 / 255.0, 0.6), // 水的基本颜色
        normalMap: require("@/assets/images/water.webp"), // 水的法线贴图
        frequency:2000.0, // 水波纹的数量
        animationSpeed: 0.05, // 水的流速
        amplitude: 5, // 水波纹振幅
        specularIntensity: 2, // 镜面反射强度
      },
    },
  });
    //设置集合体材质
  riverInstance.appearance.material = riverMaterial;
  const scene = viewer.scene;
  scene.primitives.add(riverInstance);
    //监听物体高度属性的修改
  Object.defineProperty(riverInstance, "extrudedHeight", {
    get() {
      return riverHeight;
    },
    set(newVal) {
      if (typeof newVal !== "number") {
        return;
      }
      riverHeight = newVal;
      riverInstance._state = 3; // 重置primitive状态触发cesium update方法
      riverInstance._appearance = undefined;
      riverInstance.geometryInstances.geometry = useCreatePolygonGeometry(
        boundary,
        riverHeight
      );
    },
  });

  return riverInstance;
}
//组件销毁前移除物体
function removeWaterRiver() {
  if (riverInstance) {
    viewer.scene.primitives.remove(riverInstance);
    riverInstance = null;
  } else {
    console.warn("没有水面实例需要移除.");
  }
}

这样写完以后发现效果还是差不多,内存消耗没有什么太大的变化。

问题发现

深入探究后,通过性能分析工具(如Chrome DevTools)查看了调用栈,发现频繁出现与数据劫持相关的方法调用,如 defineReactive、getter 和 setter。这说明Vue2的响应式系统在处理大量数据更新时引入了额外的性能开销。

性能分析结果

通过性能分析工具可以看到以下几点:

  • 高CPU使用率:在性能面板中,JavaScript线程使用率很高,说明大量的getter和setter调用在消耗资源。

  • 调用栈中频繁出现getter和setter:在调用栈中,可以看到 defineReactive、getter 和 setter 频繁出现,表明Vue的响应式系统在频繁工作。

  • 垃圾回收频率高:高频率的数据更新和深度监听会导致内存占用增加,触发更频繁的垃圾回收操作。

这些迹象表明,Vue的 Object.defineProperty 机制在处理复杂数据结构和频繁更新时,确实带来了性能瓶颈。

解决思路

  1. 使用 Object.freeze

通过 Object.freeze 冻结对象,阻止Vue对其进行响应式处理,但是你也无法对对象进行修改了,所以这里有点不太适用

data() {
  return {
    frozenObject: Object.freeze({
      property1: 'value1',
      property2: 'value2'
    })
  };
}

2.将非响应式数据存储在 this.$data 之外

在Vue实例的 data 选项之外存储数据,Vue不会对其进行响应式处理。

 this.nonReactiveObject = null
export default {
  data() {
    return {
      reactiveProperty: 'value'
    };
  },
  created() {
   
  }
};

3.直接在组件实例上添加非响应式属性

在Vue实例的 data 选项之外存储数据,Vue不会对其进行响应式处理。

export default {
  data() {
    return {
      reactiveProperty: 'value'
    };
  },
  created() {
    this.nonReactiveProperty = 'value';
  }
};

总结

通过以上优化措施,可以显著降低Vue2响应式系统的性能开销,改善Cesium应用中的性能问题。虽然最终效果可能还需要根据具体应用场景进行调整和测试,但这些方法提供了明确的优化方向。


注意:在性能优化过程中,应结合实际应用场景和需求,逐步进行测试和调整,以确保最终的解决方案既满足功能需求,又能够显著提升性能