Vue.js列表类切换异常:Bug还是框架限制?
问题根源:Vue数组响应式检测的局限性
这个问题的核心是Vue无法检测到通过直接索引赋值添加的数组元素的响应式变化,咱们一步步拆解:
为什么会出现这个问题?
Vue的响应式系统对数组的监听,是通过重写数组的push/pop/splice等7个方法实现的——只有调用这些方法修改数组时,Vue才能感知到变化并更新视图。
而你初始化二维数组时用了这样的代码:
this.timeblocks = []; for (var i = 1; i < 8; i++) { this.timeblocks[i] = []; // 直接通过索引给数组赋值 // ... 向子数组添加元素 }
这里直接给timeblocks的索引i赋值了一个新数组,Vue并没有把这个新的子数组转化为响应式对象。所以当你修改timeblocks[1]里的obj.val时,Vue根本感知不到这个变化,自然不会更新对应的active类样式。
至于你用nextTick能临时生效,是因为nextTick会等待下一次DOM更新周期,但这只是绕开了问题,并没有从根源解决响应式的问题。
彻底解决的方案
方案1:初始化时用响应式方法添加子数组
不要直接通过索引赋值,而是用push来添加子数组,让Vue自动把它们变成响应式的:
this.timeblocks = []; for (var i = 1; i < 8; i++) { // 先创建子数组,再push到父数组中 const subBlock = []; this.timeblocks.push(subBlock); // 向子数组添加AM时段的块 for (var j = 1; j < 25; j++) { const id = `iam${i}_${j}`; subBlock.push({ id, val: false, zone: 'mid', time: `${this.getTime(j)} AM` }); } // 添加PM时段的块 for (var j = 1; j < 25; j++) { const id = `ipm${i}_${j}`; subBlock.push({ id, val: false, zone: 'noon', time: `${this.getTime(j)} PM` }); } }
方案2:用$set手动触发响应式
如果一定要保留索引赋值的写法,可以用Vue提供的$set方法(全局的Vue.set也可以),手动告诉Vue这个位置的数组元素发生了变化:
this.timeblocks = []; for (var i = 1; i < 8; i++) { const subBlock = []; // 用$set把新数组设置到指定索引,让Vue感知到变化 this.$set(this.timeblocks, i, subBlock); // 后续添加元素的代码和之前一致 for (var j = 1; j < 25; j++) { const id = `iam${i}_${j}`; subBlock.push({ id, val: false, zone: 'mid', time: `${this.getTime(j)} AM` }); } for (var j = 1; j < 25; j++) { const id = `ipm${i}_${j}`; subBlock.push({ id, val: false, zone: 'noon', time: `${this.getTime(j)} PM` }); } }
验证效果
修改完初始化代码后,你就可以直接在点击事件里切换obj.val了:
<a class="mid" :id="obj.id" v-for="(obj, index) in timeblocks[1]" :data-value="obj.time" :class="{'active':obj.val}" @click="obj.val = !obj.val"> {{obj.id}} </a>
这时候active类会随着obj.val的变化正常切换,完全不需要依赖jQuery的toggleClass了。
内容的提问来源于stack exchange,提问作者Gotts
相关产品推荐
相关产品推荐

