websocket会实时发送大量数据,导致页面卡顿?

作者站长头像
站长
· 阅读数 4

websocket会实时发送大量数据时,我目前写的这些代码,会导致页面卡顿,性能优化我不知道怎么下手了,求大佬指教一下, 有偿。谢谢大佬们

let socketInfo = 'xxxx'
let ws = new WebSocket(socketInfo)
let pingInterval = ref(null) // 心跳间隔ID
let viewer = null
let entity = null
let polylineInitialized = false; // 轨迹线是否已初始化的标志
let polylineEntity = null; // 用于存储轨迹线实体
let pointEntity = null;
const bufferLimit = 50; // 缓冲区大小限制,可以根据需求调整

// 从服务器获取用户角色名
const userRole = computed(() => {
  return window.localStorage.getItem('roles')
})
const dropdown1 = ref()
// 点击地图获取经纬度
let longitude = ref(null);
let latitude = ref(null);
onMounted(() => {
  init() //加载3d地图,
  bindUid()//判断是否绑定uid
  LocusModel()//3D地图模型
})

// 地图的基础配置
function init() {
  viewer = new Cesium.Viewer('cesiumContainer', {
    //cesium的查看器的基本属性
    baseLayerPicker: false,//配置图层底图的图标
    //加载谷歌影像地图,UrlTemplateImageryProvider该接口是加载谷歌地图服务的接口
    imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
      // url: "https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer",
      url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer' //卫星地图
    })
}
// 判断是否绑定uid
function bindUid(params) {
  if (window.localStorage.getItem('CardUID') === '') {
    return ElMessage({
      message: '车辆终端ID未绑定,请点击右上角的“设置”图标进行绑定。',
      type: 'warning',
      showClose: true
    });
  } else {
    webValue()
  }
}
// 加载websocket
function webValue() {
  let oldPositionParams = [];//初始化一个变量来存储旧数据
  let positionParams = []; // 用于存储所有的位置参数数据
  // 初始Websocket
  const onOpen = () => {
    const data = JSON.stringify({ command: 1, serializeAlgorithm: 1, token: window.localStorage.getItem('token') });
    ws.send(data); // 发送JSON字符串
    // 设置心跳间隔为5秒
    pingInterval.value = window.setInterval(() => {
      const pingMessage = JSON.stringify({
        command: 2,
        serializeAlgorithm: 1
      });
      ws.send(pingMessage);
    }, 5000);
  };
  // 接收websocket数据
  const onMessage = async (event) => {
    const data = JSON.parse(event.data);
    if (data.data == undefined) {
      console.log('未响应');
    } else {
      processPositionParams(data.data);
      updateCarModelPosition(positionParams);// 实时更新车辆移动位置
      if (!polylineInitialized) {
        initializePolyline(positionParams); // 初始化轨迹线
        polylineInitialized = true;// 标记轨迹线已初始化
      } else {
        updatePolylinePosition(positionParams); // 更新轨迹线位置
      }
      updateEntityPositions(positionParams); // 更新实体点的位置和标签文本。
    }
  };

  function processPositionParams(data) {
    const targetingTypes = {
      0x10: 'RTK',
      0x11: 'UWB',
      0x12: '融合'
    };
    const newPositionParam = {
      TargetingType: targetingTypes[data.type],
      UID: data.uid,
      Angle: data.angle,
      Height: data.height,
      longitude: data.longitude,
      latitude: data.latitude
    };
    if (oldPositionParams.length > 0) {
      const oldPosition = oldPositionParams[oldPositionParams.length - 1];
      if (isNewDataDifferent(newPositionParam, oldPosition)) {
        positionParams.push(newPositionParam);
        if (positionParams.length > bufferLimit) {
          positionParams.shift();
        }
      }
    } else {
      positionParams.push(newPositionParam);
    }
    oldPositionParams.push(newPositionParam);
    if (oldPositionParams.length > bufferLimit) {
      oldPositionParams.shift();
    }
  }

  function isNewDataDifferent(newData, oldData) {
    // 比较各个参数是否不同,如果有任何一个参数不同,就返回 true 表示数据不同
    if (
      newData.TargetingType !== oldData.TargetingType ||
      newData.UID !== oldData.UID ||
      newData.Angle !== oldData.Angle ||
      newData.Height !== oldData.Height ||
      newData.longitude !== oldData.longitude ||
      newData.latitude !== oldData.latitude
    ) {
      return true; // 数据不同
    }
    return false; // 数据相同
  }

  const onClose = () => {
    window.clearInterval(pingInterval.value); // 清除心跳间隔定时器
    console.log('Websocket连接关闭');
    webValue();
  };
  const onError = (error) => {
    console.log('Websocket连接错误');
    ws.close();
    window.clearInterval(pingInterval.value); // 清除心跳间隔定时器
    webValue();
  };
  ws.addEventListener('open', onOpen);
  ws.addEventListener('message', onMessage);
  ws.addEventListener('close', onClose);
  ws.addEventListener('error', onError);
}

// 点击按钮是否显示轨迹点和轨迹线
let checkedPoint = ref(true);
let checkedLines = ref(false);
// 保存checkedPoint和checkedLines的值到LocalStorage
function saveCheckedValues() {
  localStorage.setItem('checkedPoint', JSON.stringify(checkedPoint.value));
  localStorage.setItem('checkedLines', JSON.stringify(checkedLines.value));
}
// 从LocalStorage中获取checkedPoint和checkedLines的值
function loadCheckedValues() {
  const storedCheckedPoint = localStorage.getItem('checkedPoint');
  const storedCheckedLines = localStorage.getItem('checkedLines');
  if (storedCheckedPoint) {
    checkedPoint.value = JSON.parse(storedCheckedPoint);
  }
  if (storedCheckedLines) {
    checkedLines.value = JSON.parse(storedCheckedLines);
  }
}
// 监听checkedPoint和checkedLines的变化,并保存到LocalStorage
watch([checkedPoint, checkedLines], saveCheckedValues, { deep: true });
// 在页面加载时加载checkedPoint和checkedLines的值
window.addEventListener('load', loadCheckedValues);


// 车辆模型
let carModelData = null;
let shouldFollowCar = false; // 标志位
function updateCarModelPosition(positions) {
  if (positions.length === 0) {
    return;
  }
  const latestPosition = positions[positions.length - 1];
  const cartesian3 = Cesium.Cartesian3.fromDegrees(latestPosition.longitude, latestPosition.latitude, 0.4);
  if (!carModelData) {
    createCarModel(cartesian3, latestPosition.Angle);
  } else {
    carModelData.position = cartesian3;
    const headingPitchRoll = new Cesium.HeadingPitchRoll(
      Cesium.Math.toRadians(latestPosition.Angle + 90),
      Cesium.Math.toRadians(0),
      Cesium.Math.toRadians(0)
    );
    carModelData.orientation = Cesium.Transforms.headingPitchRollQuaternion(cartesian3, headingPitchRoll);
  }
}
function createCarModel(cartesian3, angle) {
  const headingPitchRoll = new Cesium.HeadingPitchRoll(
    Cesium.Math.toRadians(angle + 90),
    Cesium.Math.toRadians(0),
    Cesium.Math.toRadians(0)
  );
  const scaleByDistance = new Cesium.NearFarScalar(200, 120, 1200, 0.6);
  const distanceDisplayCondition = new Cesium.DistanceDisplayCondition(0, 10000);
  carModelData = viewer.entities.add({
    position: cartesian3,
    orientation: Cesium.Transforms.headingPitchRollQuaternion(cartesian3, headingPitchRoll),
    model: {
      uri: "http://127.0.0.1:5501/car/scene.gltf",
      scale: 75,
      scaleByDistance,
      distanceDisplayCondition,
    },
    id: 'carModel' // 添加id属性
  });

}
// 轨迹点 and 标签文字
let positionsArray = [];
let entityList = [];
function updateEntityPositions(positions) {
  for (let i = 0; i < positions.length; i++) {
    let entityIndex = entityList.length;
    const position = positions[i];
    // 创建每个实体点的文本标签
    const labelText = `终端ID:${position.UID},定位类型:${position.TargetingType}\n经度:${position.longitude},纬度:${position.latitude}\n角度:${position.Angle},高度:${position.Height}`;
    if (i < entityList.length) {
      // 如果实体点已经存在,只需更新位置和文本
      const cartesian3 = Cesium.Cartesian3.fromDegrees(
        position.longitude,
        position.latitude,
        0.02
      );
      entityList[i].position = cartesian3;
      // 更新标签文本
      entityList[i].label.text = labelText;
    } else {
      // 如果实体点不存在,创建一个新的实体点
      const cartesian3 = Cesium.Cartesian3.fromDegrees(
        position.longitude,
        position.latitude,
        0.02
      );
      const pointLabel = new Cesium.Entity({
        position: cartesian3,
        point: {
          pixelSize: 10,
          color: Cesium.Color.BLUE,
          show: checkedPoint.value,
          outlineColor: Cesium.Color.WHITE,
          distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 500),
          scaleByDistance: new Cesium.NearFarScalar(100, 1, 300, 0.5),
          outlineWidth: 1,
        },
        label: {
          text: labelText, // 设置不同的文本
          font: '14pt monospace',
          show: new Cesium.CallbackProperty(() => {
            return entityIndex === entityList.length - 1;
          }, false),
          style: Cesium.LabelStyle.FILL_AND_OUTLINE,
          outlineWidth: 2,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          pixelOffset: new Cesium.Cartesian2(0, -9),
          scaleByDistance: new Cesium.NearFarScalar(300, 1, 1200, 0.4),
          scale: 1,
          fillColor: Cesium.Color.WHITE,
          outlineColor: Cesium.Color.WHITE,
        },
      });
      entityList.push(pointLabel);
      viewer.entities.add(pointLabel);
    }
  }
  // 移除多余的实体点(如果有的话)
  if (entityList.length > positions.length) {
    for (let i = positions.length; i < entityList.length; i++) {
      viewer.entities.remove(entityList[i]);
    }
    entityList.length = positions.length; // 调整数组的长度
  }
}

// 创建新的轨迹线
let polylineGeometry = null;
let geometryInstance = null;
function initializePolyline(positions) {
  const positionsArray = positions.flatMap(pos => [pos.longitude, pos.latitude]);
  const positionsInCartesian = Cesium.Cartesian3.fromDegreesArray(positionsArray);
  polylineGeometry = new Cesium.PolylineGeometry({
    positions: positionsInCartesian,
    width: 5,
    distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 500),
    scaleByDistance: new Cesium.NearFarScalar(100, 10, 500, 1),
  });
  geometryInstance = new Cesium.GeometryInstance({
    geometry: polylineGeometry,
    attributes: {
      color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
    }
  });
  const primitive = new Cesium.Primitive({
    geometryInstances: geometryInstance,
    appearance: new Cesium.PolylineMaterialAppearance({
      material: Cesium.Material.fromType(Cesium.Material.ColorType)
    })
  });
  viewer.scene.primitives.add(primitive);
}
// 更新轨迹线位置
function updatePolylinePosition(positions) {
  if (geometryInstance) {
    const positionsArray = positions.flatMap(pos => [pos.longitude, pos.latitude]);
    const positionsInCartesian = Cesium.Cartesian3.fromDegreesArray(positionsArray);
    polylineGeometry.positions = positionsInCartesian;
    geometryInstance.geometry = polylineGeometry;
    geometryInstance.show = checkedLines.value;
  }
}
// 获取父组件传递的停车场信息的经纬度
let props = defineProps({
  cesiumData: Object,
})

// 3D地图模型
function LocusModel() {
  let modelMatrix = new Cesium.Transforms.eastNorthUpToFixedFrame(new Cesium.Cartesian3.fromDegrees(104.2255077, 30.57514052)); // gltf数据加载位置(自定义)
  let heading = Cesium.Math.toRadians(66); // 设置旋转角度
  let orientation = Cesium.Matrix4.fromRotationTranslation(
    Cesium.Matrix3.fromRotationZ(heading),
    Cesium.Cartesian3.ZERO
  );
  modelMatrix = Cesium.Matrix4.multiply(modelMatrix, orientation, new Cesium.Matrix4());
  viewer.scene.primitives.add(
    new Cesium.Model.fromGltf({
      url: 'http://127.0.0.1:5501/parking/lq_parking_space.gltf', //gltf文件的URL
      modelMatrix: modelMatrix,
      scale: 1,
      maximumSize: 1,
      minimumPixelSize: 0.1,
      scaleByDistance: new Cesium.NearFarScalar(300, 1, 1200, 0.5), //设置随图缩放距离和比例
      distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 10000), //设置可见距离 10000米可见
    }));
}
回复
1个回答
avatar
test
2024-06-27

下面的修改:1.用Map来存储位置参数,可以更快查到和更新数据。2.在onMessage事件里,用setTimeout来设置下一次心跳,而不用setInterval这个处理。加了一个重连机制,在onClose和onError事件里,如果WebSocket连接关闭或发生错误,会在5秒后重连。用requestAnimationFrame来更新实体位置,这样可以较少页面卡的情况。

整体的结构可以参考一下:

let positionParams = new Map(); // 用Map来存储位置

function webValue() {
  let reconnectInterval = null;

  const onOpen = () => {
    // ... (其他代码)
    clearTimeout(reconnectInterval);
  };

  const onMessage = async (event) => {
    // ... (其他代码)
    clearTimeout(pingInterval.value);
    pingInterval.value = setTimeout(sendHeartbeat, 5000);
  };

  const onClose = () => {
    console.log('Websocket连接关闭');
    reconnectInterval = setTimeout(webValue, 5000);
  };

  const onError = (error) => {
    console.log('Websocket连接错误', error);
    reconnectInterval = setTimeout(webValue, 5000);
  };

  // ... (省略其他代码)
}

function processPositionParams(data) {
  // ... (其他代码)
  const newPositionParam = {
    // ... (其他代码)
  };

  const oldData = positionParams.get(data.uid);
  if (!oldData || isNewDataDifferent(newPositionParam, oldData)) {
    positionParams.set(data.uid, newPositionParam);
  }
}

function isNewDataDifferent(newData, oldData) {
  // ... (其他代码)
}

function updateEntityPositions() {
  requestAnimationFrame(() => {
    positionParams.forEach((position, uid) => {
      // 更新实体位置
    });
  });
}

// ... (其他代码)
回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容