1.最基础的数组遍历,v for咋写?
p>前端开发中,列表渲染是再常见不过的需求了——从商品列表到评论区,从导航菜单到表格数据,都得把一组数据循环展示出来,Vue2里的v - for就是干这个事儿的“大杀器”,但刚接触的时候,不少同学会纠结“咋遍历数组和对象?key到底有啥用?和v - if一起用咋报错?”这些问题,今天咱就用问答形式,把v - for的常见用法、坑点、优化思路一次性讲明白~
Vue2里用v - for遍历数组,语法是v - for="(item, index) in 数组数据"
,item对应数组里每个元素,index是从0开始的下标,举个简单例子,渲染待办事项列表:
<ul> <li v - for="(todo, index) in todoList" :key="index"> {{ index + 1 }}. {{ todo.title }} </li> </ul> <script> export default { data() { return { todoList: [ { title: '买咖啡' }, { title: '写代码' }, { title: '遛狗' } ] } } } </script>
这里只要todoList的数据变了(比如用push新增一项),Vue会自动更新DOM,不过得记着,v - for要配合:key
用(后面专门讲key的作用),先把基础写法逻辑吃透~
遍历对象的时候,v - for咋处理?
遍历对象时,v - for语法是v - for="(value, key, index) in 对象数据"
,value是对象的属性值,key是属性名,index是遍历顺序(按Object.keys
的顺序来,Vue2里是这么处理的),比如展示用户信息:
<div v - for="(val, k, i) in userInfo" :key="k"> 第{{ i + 1 }}个属性:{{ k }} → {{ val }} </div> <script> export default { data() { return { userInfo: { name: '小明', age: 25, job: '前端开发' } } } } </script>
渲染后会生成三行,分别对应name、age、job的键值对,但要注意,对象新增属性时,Vue默认监听不到变化(后面讲响应式更新问题),得用Vue.set
才能让视图跟着变~
v - for和v - if一起用,为啥容易踩坑?
Vue2里v - for的优先级比v - if高!也就是说,同一个元素同时写这俩指令时,会先循环每个元素,再对每个元素做v - if判断,比如想“只显示已完成的todo”,要是这么写:
<li v - for="todo in todoList" v - if="todo.done" :key="todo.id"> {{ todo.title }} </li>
假设todoList有10条数据,就会先循环10次,再逐个判断是否“done”,性能浪费特别严重(列表长的时候更明显)。
正确做法是把v - if提到循环外面,比如用<template>
包一层,或者先在JS里过滤数组:
<!-- 用template包一层,先判断再循环 --> <template v - if="todoList.length"> <li v - for="todo in todoList" v - if="todo.done" :key="todo.id"> {{ todo.title }} </li> </template> <!-- 更高效:用计算属性先过滤 --> <script> export default { computed: { doneTodos() { return this.todoList.filter(todo => todo.done) } } } </script> <template> <li v - for="todo in doneTodos" :key="todo.id"> {{ todo.title }} </li> </template>
这样就能避免不必要的循环,性能友好很多~
v - for里的key必须加吗?有啥用?
key必须加! 但很多新手爱用index当key,这其实埋了不少坑。
key的作用是给Vue的虚拟DOM做“身份标识”,列表数据变化时,Vue靠key判断哪些元素是新增、删除、修改的,从而高效更新DOM(也就是diff算法里的“就地复用”逻辑)。
举个🌰:如果用index当key,列表增删时index会变,Vue就会认错元素,todoList是[{id:1, title:'a'}, {id:2, title:'b'}]
,用index当key渲染后,key是0、1,要是删除第一个元素,新列表变成[{id:2, title:'b'}]
,此时key变成0,Vue会误以为“原来key=1的元素被删了,key=0的元素是原来的id=2”,但其实内容没变化,这时候如果元素里有输入框这类可交互组件,就会出现“数据和视图不匹配”的bug(比如输入框内容被错误复用)。
所以正确做法是用数据里的唯一标识当key,比如todo的id:
<li v - for="todo in todoList" :key="todo.id"> {{ todo.title }} <input type="text" placeholder="备注"> </li>
这样就算列表增删,每个元素的key唯一且稳定,Vue能准确识别,避免DOM复用出错~
想做嵌套循环,v - for咋嵌套?
嵌套循环在表格、多级菜单里很常见,思路是外层v - for循环父数据,内层v - for循环子数据,比如渲染“周课程表”,外层是星期,内层是每天的课程:
<table> <tr v - for="(week, weekIndex) in weekCourses" :key="weekIndex"> <td>{{ week.name }}</td> <td v - for="(course, courseIndex) in week.courses" :key="courseIndex"> {{ course.name }}({{ course.time }}) </td> </tr> </table> <script> export default { data() { return { weekCourses: [ { name: '周一', courses: [ { name: '数学', time: '09:00' }, { name: '英语', time: '14:00' } ] }, { name: '周二', courses: [ { name: '语文', time: '10:00' }, { name: '体育', time: '15:00' } ] } ] } } } </script>
外层循环weekCourses
数组(每个元素是某天的课程包),内层循环每个week
里的courses
数组,注意每层循环都得加key,而且key要保证当前层级唯一~
列表数据更新了,v - for咋没自动渲染?
这是因为Vue2的响应式原理有“限制”,数组和对象的更新,不是所有操作都会触发视图更新:
-
数组:只有调用变异方法(push/pop/shift/unshift/splice/sort/reverse)才会触发更新,如果直接改索引(比如
todoList[0] = {title: '新内容'}
),Vue监听不到,视图也不会变,这时候得用Vue.set(todoList, 0, {title: '新内容'})
或者todoList.splice(0, 1, {title: '新内容'})
。 -
对象:直接给对象新增属性(比如
userInfo.gender = '男'
),Vue也监听不到,得用Vue.set(userInfo, 'gender', '男')
或者替换整个对象(this.userInfo = { ...this.userInfo, gender: '男' }
)。
举个🌰:想给todoList第一个元素改标题,正确操作得这么写:
// 错误写法:直接改索引,视图不更新 this.todoList[0].title = '买奶茶' // 正确写法1:用Vue.set import Vue from 'vue' Vue.set(this.todoList, 0, { title: '买奶茶' }) // 正确写法2:用splice this.todoList.splice(0, 1, { title: '买奶茶' })
理解了Vue2的响应式机制,才能避免“数据改了视图没反应”的尴尬~
v - for能循环渲染组件吗?咋传数据?
必须能!很多场景下,列表里的每个项都是独立组件(比如商品卡片、用户卡片),做法是在父组件用v - for循环数据,把每个数据项传给子组件的props。
比如做一个“用户卡片列表”,子组件叫UserCard
:
<!-- 父组件模板 --> <user - card v - for="user in userList" :key="user.id" :user - info="user" ></user - card> <!-- 子组件UserCard --> <template> <div class="card"> <h3>{{ userInfo.name }}</h3> <p>年龄:{{ userInfo.age }}</p> <p>职业:{{ userInfo.job }}</p> </div> </template> <script> export default { props: { userInfo: { type: Object, required: true } } } </script>
父组件里userList
是用户数据数组,每个user
传给子组件的userInfo
props,这样每个子组件都能独立展示数据,还能在子组件里处理点击事件等交互,实现组件化的列表渲染~
说到底,v - for是Vue2列表渲染的核心,把数组/对象遍历、key的作用、和v - if的配合、响应式更新这些知识点吃透,实际项目里才能少踩坑,要是你还有其他细节问题,评论区随时喊我唠~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。