VUE实践优化:轮询机制与代码结构升级

2023-12-19

前言

我们之前探讨过,对于包含处理状态的表格数据,我们可以通过轮询的方式进行处理( 轮询更新进度条:JavaScript中的定时器和异步编程技巧 )。然而,当我们离开页面时,定时器仍会继续触发请求,这会造成资源的浪费,因为返回的数据并没有被渲染出来。

为了解决这个问题,我们需要实现以下要求:

  1. 最多只能同时存在一个轮询的定时器。
  2. 当用户离开页面时,定时器应被关闭。
  3. 当用户重新进入页面时,如果仍有数据正在处理中,应重新启动轮询。
  4. 轮询应能跨页面、跨显示数量。

实现

为了实现上述要求,我们可以采取以下步骤:

  1. 在全局范围内定义一个唯一的定时器变量,例如 running_task_timer
  2. 在开始轮询之前,检查 running_task_timer 是否已存在。如果存在,则清除该定时器并停止轮询。
  3. 如果需要启动轮询,首先检查页面是否处于激活状态。如果不在激活状态,则不启动轮询。
  4. 启动轮询时,设置 running_task_timer 为定时器的ID,并开始轮询处理。
  5. 当用户离开页面时,清除 running_task_timer 并停止轮询。
  6. 当用户重新进入页面时,检查是否有正在处理的数据。如果有,则重新设置 running_task_timer 并启动轮询。
  7. 在轮询处理中,检查页面是否处于激活状态。如果不在激活状态,则暂停轮询处理。
  8. 在轮询处理完成时,检查是否有新的数据需要处理。如果有,则重新设置 running_task_timer 并启动轮询。
  9. 通过上述步骤,我们可以确保同时只有一个轮询的定时器存在,并在用户离开页面时关闭定时器。当用户重新进入页面时,如果仍有数据正在处理中,可以重新启动轮询。同时,轮询的处理可以跨页面、跨显示数量进行。
<template>
  <div>
    <el-table :data="tableData">
      <el-table-column prop="id" label="任务ID" />
      <el-table-column prop="name" label="任务名称" />
      <el-table-column prop="status" label="任务状态" />
      <el-table-column label="操作">
        <template slot-scope="scope">
          <el-button @click="reloadTask(scope.row)">重新执行</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
export default {
  data() {
    return {
      tableData: [],
      running_task_timer: null,
      running_task_list: [],
    };
  },
  created() {
    this.getData();
  },
  beforeDestroy() {
    this.clearMyInterval();
  },
  methods: {
    getData() {
      axios
        .get(url, query)
        .then(res => {
          if (res.status === 200) {
            tableData = res.data.data;
            this.addRunningTask();
          }
        })
        .catch(error => alert(error));
    },

    addRunningTask() {
      this.running_task_list = [];
      this.tableData.forEach(item => {
        if (item.status === 'RUNNING') this.running_task_list.push(item);
      });
      this.running_task_list.length ? this.startInterval() : this.clearMyInterval();
    },

    startInterval() {
      const _this = this;
      this.clearMyInterval();
      this.running_task_timer = setInterval(() => _this.getData, 1000);
    },

    clearMyInterval() {
      if (this.running_task_timer !== null) clearInterval(this.running_task_timer);
    },

    reloadTask(row) {
      row.status = 'BEGIN';
      axios
        .put(url, { id: row.id })
        .then(res => {
          if (res.status === 200) this.getData();
        })
        .catch(err => {
          alert(err);
          row.status = 'DONE';
        });
    },
  },
};
</script>

数据定义

  • tableData : 这是一个数组,用于存储从服务器获取的表格数据。
  • running_task_timer : 这是一个定时器变量,用于轮询正在运行的任务。
  • running_task_list : 这是一个数组,用于存储状态为“RUNNING”的任务。

生命周期钩子

  • created() : 当组件被创建时,它调用 getData() 方法来获取数据。
  • beforeDestroy() : 当组件即将被销毁时,它调用 clearMyInterval() 方法来清除定时器,以避免资源浪费。

方法

  • getData() : 使用 axios 发送 GET 请求来获取数据。如果响应的状态码是 200,它将更新 tableData 并调用 addRunningTask() 方法。
  • addRunningTask() : 遍历 tableData ,找出状态为“RUNNING”的任务,并将它们存储在 running_task_list 中。如果 running_task_list 有内容,则调用 startInterval() 方法来启动定时器;否则,调用 clearMyInterval() 方法清除定时器。
  • startInterval() : 首先调用 clearMyInterval() 来清除可能已经存在的定时器,然后设置一个新的定时器,每隔1秒调用 getData() 方法来轮询数据。
  • clearMyInterval() : 如果 running_task_timer 不为 null,则清除该定时器。
  • reloadTask(row) : 更新特定行的状态为“BEGIN”,并使用 axios 发送 PUT 请求来重新加载任务。如果响应的状态码是 200,则再次调用 getData() 方法。如果请求失败,它会将行的状态设置为“DONE”。

改进

提升性能:只更新修改项数据

<script>
export default {
  data() {
    return {
      tableData: [],
      running_task_timer: null,
      running_task_list: [],
    };
  },
  created() {
    this.getData();
  },
  beforeDestroy() {
    this.clearMyInterval();
  },
  methods: {
    getData() {
      axios
        .get(url, query)
        .then(res => {
          if (res.status === 200) {
            tableData = res.data.data;
            this.addRunningTask();
          }
        })
        .catch(error => alert(error));
    },

    addRunningTask() {
      this.running_task_list = [];
      this.tableData.forEach(item => {
        if (item.status === 'RUNNING') this.running_task_list.push(item);
      });
      if (this.running_task_list.length > 0) this.startInterval();
    },

    startInterval() {
      const _this = this;
      this.clearMyInterval();
      this.running_task_timer = setInterval(() => _this.updateTask, 1000);
    },

    clearMyInterval() {
      if (this.running_task_timer !== null) clearInterval(this.running_task_timer);
    },

    updateTask() {
      if (this.running_task_list.length === 0) this.clearMyInterval();
      const queryIds = [];
      this.running_task_list.forEach(item => queryIds.push(item.id));
      axios.get(url, { params: { ids: queryIds } }).then(res => {
        if (res.status === 200) {
          res.data.data.forEach(update_item => {
            this.running_task_list.forEach(item => {
              if (update_item.id === item.id) item = { ...update_item };
              // 去除已经完成的任务
              if (item.status !== 'RUNNING') this.running_task_list.splice(this.running_task_list.indexOf(item), 1);
            });
          });
        }
      });
    },

    reloadTask(row) {
      row.status = 'BEGIN';
      axios
        .put(url, { id: row.id })
        .then(res => {
          if (res.status === 200) this.getData();
        })
        .catch(err => {
          alert(err);
          row.status = 'DONE';
        });
    },
  },
};
</script>

更新机制的改进 :之前的代码可能存在一个性能问题,即定时器会不断轮询服务器,无论运行的条数多少,都会全量更新接口返回的数据,这可能导致不必要的性能浪费。在新的代码中,我们通过在 updateTask 方法中只更新 running_task_list 中存储的需要更新的项,来避免了这一问题。任务完成后,从 running_task_list 中移除,从而在当前页更新完毕后停止轮询。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

VUE实践优化:轮询机制与代码结构升级 的相关文章

随机推荐