You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3+Vuetify3中v-data-table多排序状态捕获与控制问题

问题:Vuetify多排序绑定后无法监听变化

环境:Vue 3.5.13、Vuetify 3.8.5

我用v-data-table做了支持多排序的表格,需要在某些场景下临时切换到指定排序,之后恢复用户之前选的排序规则。本来想通过绑定:sort-by.sync="sortBy"来监听并保存当前排序,但发现手动调整排序时,watch没触发,sortBy变量也没更新。

初始化代码是正常的:

:sort-by.sync="sortBy"

const sortBy = ref([]);

挂载时赋值也能生效:

sortBy.value = [
  { key: 'calories', order: 'asc' },
  { key: 'fat', order: 'desc' },
]

但用户手动改排序时,watch完全没反应,不知道怎么才能正确获取当前生效的排序条件?

以下是我基于Vuetify官方多排序示例修改的测试代码:

<template>
  <h1>{{ msg }}</h1>
  <v-data-table
    :headers="headers"
    :items="desserts"
    :sort-by.sync="sortBy"
    multi-sort
  ></v-data-table>
</template>

<script setup>
  import { ref, watch, onMounted, computed } from 'vue'
  const sortBy = ref([])
  const msg = ref('x')

  onMounted(() => {
    watch(
      () => sortBy.value,
      () => {
        msg.value = 'Sorting by ' + JSON.stringify(sortBy.value)
      }
    )
  })

  onMounted(
    () =>
      (sortBy.value = [
        { key: 'calories', order: 'asc' },
        { key: 'fat', order: 'desc' },
      ])
  )

  const headers = [
    {
      title: 'Dessert (100g serving)',
      align: 'start',
      sortable: false,
      key: 'name',
    },
    { title: 'Calories', key: 'calories' },
    { title: 'Fat (g)', key: 'fat' },
    { title: 'Carbs (g)', key: 'carbs' },
    { title: 'Protein (g)', key: 'protein' },
    { title: 'Iron (%)', key: 'iron' },
  ]
  const desserts = [
    {
      name: 'Frozen Yogurt',
      calories: 200,
      fat: 6,
      carbs: 24,
      protein: 4,
      iron: 1,
    },
    {
      name: 'Ice cream sandwich',
      calories: 200,
      fat: 9,
      carbs: 37,
      protein: 4.3,
      iron: 1,
    },
    {
      name: 'Eclair',
      calories: 300,
      fat: 16,
      carbs: 23,
      protein: 6,
      iron: 7,
    },
    {
      name: 'Cupcake',
      calories: 300,
      fat: 3.7,
      carbs: 67,
      protein: 4.3,
      iron: 8,
    },
    {
      name: 'Gingerbread',
      calories: 400,
      fat: 16,
      carbs: 49,
      protein: 3.9,
      iron: 16,
    },
    {
      name: 'Jelly bean',
      calories: 400,
      fat: 0,
      carbs: 94,
      protein: 0,
      iron: 0,
    },
    {
      name: 'Lollipop',
      calories: 400,
      fat: 0.2,
      carbs: 98,
      protein: 0,
      iron: 2,
    },
    {
      name: 'Honeycomb',
      calories: 400,
      fat: 3.2,
      carbs: 87,
      protein: 6.5,
      iron: 45,
    },
    {
      name: 'Donut',
      calories: 500,
      fat: 25,
      carbs: 51,
      protein: 4.9,
      iron: 22,
    },
    {
      name: 'KitKat',
      calories: 500,
      fat: 26,
      carbs: 65,
      protein: 7,
      iron: 6,
    },
  ]
</script>

解决方案

问题根源有两个:

  1. watch创建时机不对:你把watch放在onMounted里,虽然能捕获初始化赋值,但Vuetify内部更新排序时,可能因为watch绑定时机晚了导致没触发。而且默认watch只监听数组引用变化,不会管内部元素的修改。
  2. Vuetify多排序的更新逻辑:多排序模式下,sort-by的更新是原地修改数组(比如新增/删除排序项、修改order值),不是替换整个数组引用,所以默认watch检测不到变化。

直接按下面的步骤改:

  • 把watch移到script setup顶层,不用嵌套在onMounted里。
  • 开启deep: true选项,监听数组内部的变化。
  • 可选开启immediate: true,让初始化时也触发一次watch。

修改后的完整代码:

<template>
  <h1>{{ msg }}</h1>
  <v-data-table
    :headers="headers"
    :items="desserts"
    :sort-by.sync="sortBy"
    multi-sort
  ></v-data-table>
</template>

<script setup>
import { ref, watch, onMounted } from 'vue'
const sortBy = ref([])
const msg = ref('x')

// 顶层定义watch,开启深度监听
watch(
  sortBy,
  (newSort) => {
    msg.value = '当前排序:' + JSON.stringify(newSort)
    // 这里可以把newSort存到localStorage或者全局状态,比如:
    // localStorage.setItem('userSort', JSON.stringify(newSort))
  },
  { deep: true, immediate: true }
)

onMounted(() => {
  sortBy.value = [
    { key: 'calories', order: 'asc' },
    { key: 'fat', order: 'desc' },
  ]
})

const headers = [
  {
    title: '甜点(100g份)',
    align: 'start',
    sortable: false,
    key: 'name',
  },
  { title: '热量', key: 'calories' },
  { title: '脂肪(g)', key: 'fat' },
  { title: '碳水(g)', key: 'carbs' },
  { title: '蛋白质(g)', key: 'protein' },
  { title: '铁(%)', key: 'iron' },
]

const desserts = [
  {
    name: '冻酸奶',
    calories: 200,
    fat: 6,
    carbs: 24,
    protein: 4,
    iron: 1,
  },
  {
    name: '冰淇淋三明治',
    calories: 200,
    fat: 9,
    carbs: 37,
    protein: 4.3,
    iron: 1,
  },
  {
    name: '泡芙',
    calories: 300,
    fat: 16,
    carbs: 23,
    protein: 6,
    iron: 7,
  },
  {
    name: '纸杯蛋糕',
    calories: 300,
    fat: 3.7,
    carbs: 67,
    protein: 4.3,
    iron: 8,
  },
  {
    name: '姜饼',
    calories: 400,
    fat: 16,
    carbs: 49,
    protein: 3.9,
    iron: 16,
  },
  {
    name: '果冻豆',
    calories: 400,
    fat: 0,
    carbs: 94,
    protein: 0,
    iron: 0,
  },
  {
    name: '棒棒糖',
    calories: 400,
    fat: 0.2,
    carbs: 98,
    protein: 0,
    iron: 2,
  },
  {
    name: '蜂巢糖',
    calories: 400,
    fat: 3.2,
    carbs: 87,
    protein: 6.5,
    iron: 45,
  },
  {
    name: '甜甜圈',
    calories: 500,
    fat: 25,
    carbs: 51,
    protein: 4.9,
    iron: 22,
  },
  {
    name: '奇巧巧克力',
    calories: 500,
    fat: 26,
    carbs: 65,
    protein: 7,
    iron: 6,
  },
]
</script>

现在用户调整排序时,watch会实时触发,你可以在回调里把排序规则保存起来。之后要恢复的话,直接把保存的值赋值给sortBy.value就行,比如:

// 恢复用户之前的排序
const savedSort = localStorage.getItem('userSort')
if (savedSort) sortBy.value = JSON.parse(savedSort)

内容的提问来源于stack exchange,提问作者djna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 02:28:11