一、v-for指令详解
v-for是Vue中最常用的指令之一,用于遍历展示列表数据。它的基本语法是:v-for="(val, key) in xxx" :key="key"
,其中in
也可以替换为of
。
1.1 基本用法
v-for可以遍历多种数据类型:
<div id="app">
<!-- 遍历数组 -->
<h2>人员列表</h2>
<ul>
<li v-for="(p, index) in persons" :key="p.id">
{{index}}:{{p.name}} - {{p.age}}
</li>
</ul>
<!-- 遍历对象 -->
<h2>车辆信息</h2>
<ul>
<li v-for="(val, key) in car" :key="key">
{{key}}:{{val}}
</li>
</ul>
<!-- 遍历字符串 -->
<h2>字符信息</h2>
<ul>
<li v-for="(char, index) in str" :key="index">
{{index}}:{{char}}
</li>
</ul>
<!-- 遍历数字 -->
<h2>数字</h2>
<ul>
<li v-for="(num, index) in 5" :key="index">
{{index}}:{{num}}
</li>
</ul>
</div>
对应的JavaScript代码:
new Vue({
el: '#app',
data: {
persons: [
{id: '001', name: '张三', age: 18},
{id: '002', name: '李四', age: 19},
{id: '003', name: '王五', age: 20}
],
car: {
brand: '奔驰',
price: '40万',
color: '黑色'
},
str: 'hello'
}
})
1.2 遍历不同数据类型的说明
数组:第一个参数是数组元素,第二个参数是索引
对象:第一个参数是属性值,第二个参数是属性名
字符串:第一个参数是字符,第二个参数是索引
数字:遍历从1开始到该数字的整数,第一个参数是数字值,第二个参数是索引
二、key的原理与重要性
2.1 虚拟DOM中key的作用
key是虚拟DOM对象的唯一标识,当数据发生变化时,Vue会执行以下步骤:
根据新数据生成新的虚拟DOM
将新虚拟DOM与旧虚拟DOM进行Diff算法比较
如果找到相同key的节点:
内容没变 → 直接复用之前的真实DOM
内容变化 → 生成新的真实DOM替换旧的真实DOM
如果没找到相同key的节点 → 创建新的真实DOM并渲染
2.2 使用index作为key的问题
<ul>
<li v-for="(p, index) in persons" :key="index">
{{p.name}} <input type="text">
</li>
</ul>
假设我们有以下操作:
在列表开头添加一个新项
在输入框中输入一些内容
问题表现:
会产生不必要的真实DOM更新,降低效率
输入框内容会出现错位(因为index变化导致输入框与数据对应关系错乱)
2.3 最佳实践
理想情况:使用数据的唯一标识作为key(id、手机号、身份证号等)
<li v-for="p in persons" :key="p.id">{{p.name}}</li>
2. 简单场景:如果没有逆序增删等操作,可以使用index作为key
三、列表过滤与排序
3.1 列表过滤的实现方式
方式一:使用watch实现
new Vue({
data: {
keyword: '',
persons: [...],
filterPerson: []
},
watch: {
keyword: {
immediate: true,
handler(newVal) {
this.filterPerson = this.persons.filter(p => {
return p.name.includes(newVal)
})
}
}
}
})
方式二:使用computed实现(推荐)
new Vue({
data: {
keyword: '',
persons: [...]
},
computed: {
filterPerson() {
return this.persons.filter(p => {
return p.name.includes(this.keyword)
})
}
}
})
3.2 列表排序的实现
computed: {
filterPerson() {
let arr = this.persons.filter(p => {
return p.name.includes(this.keyword)
})
if(this.sortType) {
arr.sort((p1, p2) => {
return this.sortType === 1
? p2.age - p1.age // 降序
: p1.age - p2.age // 升序
})
}
return arr
}
}
四、Vue数据监测原理深度解析
4.1 对象监测
Vue会通过setter监视data中所有层次的对象属性:
初始化时定义:只有在new Vue时就存在的属性才会被监测
新增属性问题:后期添加的属性默认没有响应式
vm.newProp = 'value' // 非响应式
3. 解决方案:使用Vue.set或vm.$set
Vue.set(vm.someObject, 'newProp', 'value')
// 或
this.$set(this.someObject, 'newProp', 'value')
4.2 数组监测
Vue通过重写数组的变更方法实现监测:
包裹的方法:push/pop/shift/unshift/sort/splice/reverse
原理:
调用原生数组方法更新数据
重新解析模板,更新页面
注意事项:
直接通过索引修改数组元素不是响应式的
vm.items[0] = newValue // 不是响应式的
解决方案
Vue.set(vm.items, 0, newValue) // 或使用splice vm.items.splice(0, 1, newValue)
4.3 数据劫持原理
Vue在初始化时会对data进行"数据劫持":
使用Object.defineProperty将data属性转换为getter/setter
每个组件实例都有对应的watcher实例
当属性被访问或修改时,会触发getter/setter通知watcher
watcher会触发组件重新渲染
// 简化的数据劫持示例
function observe(data) {
if (!data || typeof data !== 'object') return
Object.keys(data).forEach(key => {
let value = data[key]
Object.defineProperty(data, key, {
get() {
console.log(`读取${key}: ${value}`)
return value
},
set(newVal) {
console.log(`设置${key}: ${newVal}`)
value = newVal
}
})
})
}
五、实际应用案例
5.1 动态表单实现
<div id="dynamic-form">
<button @click="addField">添加字段</button>
<div v-for="(field, index) in formFields" :key="field.id">
<input v-model="field.value" :placeholder="field.placeholder">
<button @click="removeField(index)">删除</button>
</div>
</div>
<script>
new Vue({
el: '#dynamic-form',
data: {
formFields: [],
nextId: 1
},
methods: {
addField() {
this.formFields.push({
id: this.nextId++,
value: '',
placeholder: '请输入字段' + this.nextId
})
},
removeField(index) {
this.formFields.splice(index, 1)
}
}
})
</script>
5.2 性能优化技巧
避免大型列表:对于大型列表,考虑使用虚拟滚动(如vue-virtual-scroller)
避免不必要的响应式:冻结不需要响应式的大数据对象
this.largeData = Object.freeze(largeData)
合理使用key:确保key的稳定性和唯一性
六、总结
v-for是Vue列表渲染的核心指令,支持多种数据类型
key的正确使用对列表渲染性能和正确性至关重要
计算属性是实现列表过滤和排序的理想选择
Vue的数据监测机制基于getter/setter和数组方法重写
对于动态添加的属性,需要使用Vue.set保证响应式
理解这些核心概念,能够帮助开发者编写更高效、更可靠的Vue应用程序。在实际项目中,合理应用这些知识可以避免许多常见的性能问题和bug。