在列表中我经常遇到隐藏与显示部分内容的操作,通常的做法是在列表中动态添加一个属性用于标记隐藏或显示的状态,但是要求动态添加的这个属性必须是响应式的。
响应式的注意事项
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。
-
对于对象
Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
如下所示:
new Vue({ data(){ return { obj:{ a: 0 // obj.a 是响应式的 } } }, methods:{ modify(){ this.obj.b = 0 // obj.b不是响应式的 } } })
-
对于数组
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
Vue 不能检测以下数组的变动:
- 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:vm.items.length = newLength
如下所示:
new Vue({ data() { return { items: ['a', 'b', 'c'] } }, methods:{ modify(){ this.items[1] = 'x' // 不是响应性的 this.items.length = 2 // 不是响应性的 } } })
添加响应式属性
这里我们就需要使用到 Vue.set 方法,先看一下官方的说明:
Vue.set( target, propertyName/index, value )
参数:
- {Object | Array} target
- {string | number} propertyName/index
- {any} value
返回值:设置的值。
用法:
向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi')
-
对象动态添加响应式属性
new Vue({ data(){ return { obj:{ a: 0 // obj.a 是响应式的 } } }, methods:{ modify(){ // this.obj.b = 0 // obj.b不是响应式的 this.$set(this.obj, "b", 0) // obj.b 是响应式的 } } })
-
数组的响应式变动
new Vue({ data() { return { items: ['a', 'b', 'c'] } }, methods:{ modify(){ // this.items[1] = 'x' // 不是响应性的 // this.items.length = 2 // 不是响应性的 this.$set(this.items, 1, 'x') // 响应性的 this.items.splice(1, 1, 'x') // 也可以使用 splice 实现响应性的 } } })
示例
<template>
<div>
<div v-for="(item, index) in list" :key="index">
<span>{{item.name}}</span>
<span>
<template v-if="!item.showPhone">***</template>
<template v-else>{{item.phone}}</template>
</span>
<span @click="handleShow(item)">显示/隐藏</span>
</div>
</div>
</template>
<script>
export default{
data(){
return {
list:[
{
name:'Rick',
phone:'19988888888'
},
{
name:'Jack',
phone:'19166666666'
}
]
}
},
mehtods:{
handleShow(item){
if(item.showPhone === undefined){
this.$set(item, "showPhone", false)
}
item.showPhone = !item.showPhone
}
}
}
</script>