主题
v-bind(属性渲染)-重点中的重点与v-on 事件(Lesson1 4/4)
本页对应 Lesson01,共 5 段视频,约 44 分钟。
学习目标
- 能够使用 v-for 分别遍历数组、对象、数字和字符串,并在模板中正确渲染元素值、属性值、索引或键名。
- 能够为 v-for 列表项设置 key,并说明优先使用唯一 ID、无唯一 ID 时使用下标的理由及其对 diff 渲染效率的影响。
- 能够使用 v-bind 动态绑定 class 与 style,实现选项卡按钮高亮和内容区域显示/隐藏。
- 能够使用 v-on 绑定点击事件,通过更新选中索引 index 完成“乞丐版”与“加强版”选项卡切换。
- 能够使用 v-model 完成表单数据的双向绑定,并在数据变化后验证视图同步更新。
- 能够将新闻列表数据渲染为分类按钮和内容列表,并实现点击分类切换及新闻链接跳转。
知识点
先定位:本节重点是把数据渲染到“属性”上
为什么需要:模板里文本可以用 {{ }},但属性不能直接写变量。比如按钮高亮、列表 key、新闻链接,都要把数据绑定到 class、key、href、target 这些属性上,所以要用 v-bind,简写是冒号 :。转写稿里反复出现的“冒号 class”“:key”“:href”“:target”,本质都是 v-bind 的属性渲染。
怎么想:先判断“这个值是不是属性值”,是就用 :属性名="表达式";再判断表达式是普通值还是对象/数组语法,比如 class 常用对象语法。
怎么做: :class="{ active: index === i }"、:key="item.id"、:href="item.url"、:target="_blank"。
易错点:冒号不能丢;属性绑定里不要再套 {{ }};v-for 的 :key 不要重复;v-for 和 v-if 不要写在同一个元素上,转写稿也明确说这样效率慢,后面单独讲。
v-for 的遍历与 :key
为什么需要:列表渲染要重复生成结构,Vue 用 v-for 比手写 DOM 更直接。转写稿强调它比 React 里的 map 更灵活:数组、对象、数字、字符串都能遍历。
怎么想:先确定数据源,再决定要不要下标。数组常用 item in arr;需要下标写 (item, index) in arr;对象可写 (value, key) in obj;数字会从 1 开始;字符串会按下标拆开。
怎么做,按转写稿整理关键片段:
html
<div v-for="item in arr">{{ item }}</div>
<div v-for="(value, index) in arr">{{ index }} - {{ value }}</div>
<div v-for="(value, key) in obj">{{ key }}:{{ value }}</div>
<div v-for="n in 10">{{ n }}</div>
<div v-for="(s, i) in str">{{ i }} - {{ s }}</div>列表项要加 :key:
html
<div v-for="item in list" :key="item.id">{{ item.bookName }}</div>易错点:key 建议优先用 id,没有 id 再用下标;只是单纯渲染时用下标也可以,转写稿说效果一样。但不要出现重复 key。
v-bind 渲染 class:切换高亮的核心
为什么需要:切换案例里“当前按钮高亮”不是改文本,而是改 class。原生要 className = 'active',Vue 用 :class 把数据映射成样式。
怎么想:选中项由 index 表示,遍历时用 i 表示当前项下标,条件就是 index === i。
怎么做:
html
<button
v-for="(item, i) in newsList"
:key="item.id"
:class="{ active: index === i }"
@click="index = i"
>
{{ item.typeName }}
</button>易错点:active 是类名,右边 index === i 是布尔表达式;点击后只改 index,不要手动操作 DOM。转写稿调试时先确认点击事件有没有执行、this 是不是按钮,再确认下标有没有更新,最后看 :class 条件是否满足。
从原生切换到 Vue:调试推导
为什么需要:转写稿先用原生写切换,是为了对比出 Vue 的优势。原生要获取按钮、绑事件、控制 display、清空/添加 active,还要处理初始化;Vue 只关注数据。
怎么想:原生步骤是“获取按钮和内容区 → 给按钮加点击 → 隐藏/显示对应 DIV → 改高亮类”;Vue 步骤是“声明 index → 模板用 :class 和 v-show 判断 → 点击改 index”。
怎么做,Vue 版核心:
html
<div v-show="index === 0">体育新闻</div>
<div v-show="index === 1">娱乐新闻</div>
<div v-show="index === 2">财经新闻</div>js
data() {
return { index: 0 }
}易错点:转写稿里点击没反应时,先查事件有没有绑上、index 有没有变;Vue 里不要一边改数据一边又手动改 display,否则容易冲突。重复的隐藏/高亮逻辑可以抽成方法,初始化时调用一次。
加强版新闻切换:v-bind 渲染 href/target
为什么需要:真实切换里按钮和内容都来自数据,链接也要渲染。转写稿用 newsList 存类型和新闻列表,按钮 v-for 渲染,内容区也 v-for 渲染。
怎么想:外层按钮用 :class 判断选中;内容区用 v-show="index === i" 控制显示;新闻链接用 :href 和 :target 渲染属性。
怎么做,关键片段:
html
<button
v-for="(item, i) in newsList"
:key="item.id"
:class="{ active: index === i }"
@click="index = i"
>
{{ item.typeName }}
</button>
<div v-for="(item, i) in newsList" :key="item.id" v-show="index === i">
<p v-for="news in item.items" :key="news.id">
<a :href="news.url" :target="_blank">{{ news.title }}</a>
</p>
</div>易错点::href 的冒号不能丢;target="_blank" 是普通属性,不需要冒号;v-for 和 v-if 不要同元素混用;key 用新闻 id,不要重复。转写稿最后也提醒:这部分务必自己敲一遍,重点理解“改数据 → 模板重新渲染 → 属性条件生效”的流程。
本节说明与 v-for 的基本定位
本节标题是“v-on 事件”,但转写稿实际从 v-for 遍历讲到用点击事件完成切换案例;v-on 的落点是“按钮点击后改数据,视图自动更新”。本节笔记未附图片,以下保留转写稿中的代码结构,并把口述中的“微 for、微刚 for、微商货”等统一纠正为 v-for,“in带/inex”统一为 index,“微刚售/微装数”按上下文统一为 v-show。
为什么需要: 列表渲染是前端高频需求。原生里常用 map,但 map 主要面向数组;Vue 的 v-for 还能遍历对象、数字、字符串,写列表更直接。
怎么想: 先判断“要遍历什么”:数组拿值和下标,对象拿值和键,数字从 1 开始,字符串按字符拆。
怎么做:
html
<div v-for="item in arr">{{ item }}</div>
<div v-for="(value, index) in arr">{{ index }} - {{ value }}</div>
<div v-for="item in obj">{{ item }}</div>
<div v-for="(value, k) in obj">{{ k }}: {{ value }}</div>
<div v-for="n in num">{{ n }}</div>
<div v-for="(n, i) in num">{{ i }} - {{ n }}</div>
<div v-for="s in str">{{ s }}</div>
<div v-for="(s, i) in str">{{ i }} - {{ s }}</div>易错点: 数字遍历从 1 开始,但第二个参数下标从 0 开始;对象直接写一个参数拿到的是属性值,不是键;字符串会被拆成一个个字符。
key 的作用与写法
为什么需要: 转写稿提到 key 和 diff 算法有关,能提升渲染效率;列表项最好有稳定标识。
怎么想: 有 id 优先用 id;没有 id 时,如果只是单纯渲染,用下标也可以。
怎么做:
html
<div v-for="item in arr" :key="item.id">{{ item.bookName }}</div>转写稿还说明::key 写在 v-for 左边或右边都行,关键是值要对应。
易错点: 不要手写重复 key;不要把 key 当成普通展示字段;没有稳定 id 时才退而用下标。
原生切换案例:先调通事件,再控制显示
为什么需要: 用原生写一遍切换,能看清“获取按钮、绑定事件、初始化、点击后隐藏/显示、高亮”的完整流程,再对照 Vue 会更容易。
怎么想: 先让点击事件能输出当前按钮内容,确认 this 是谁;再引入 index=0 做初始化;点击时若 i === index 就不处理,否则先清旧状态,再设新状态。
怎么做: 转写稿中的调试过程是:获取 #root 下所有按钮和 div,给按钮加点击事件,输出 this 和 textContent;确认事件生效后,用 index 控制对应 div 的 display 和按钮 className;最后把重复的隐藏/显示逻辑抽成 hide 函数,并把初始化逻辑收进初始化阶段。
易错点: 点击当前已选按钮时不必重复操作;清旧高亮和设新高亮要成对;抽函数时注意传入当前下标 i,否则函数里默认用的 index 可能不是刚点击的项。
Vue 版切换:v-on 点击改数据,视图自动渲染
为什么需要: 原生要手动找 DOM、改样式;Vue 里只需维护 index,用 v-on(简写 @click)改数据,模板自动重新渲染。
怎么想: 把“当前选中项”抽象成 index;按钮高亮用 :class="{ active: index === i }";内容显示用 v-show="index === 0/1/2"。
怎么做:
html
<div id="root">
<button :class="{ active: index === 0 }" @click="index = 0">体育</button>
<button :class="{ active: index === 1 }" @click="index = 1">娱乐</button>
<button :class="{ active: index === 2 }" @click="index = 2">财经</button>
<div v-show="index === 0">体育新闻</div>
<div v-show="index === 1">娱乐新闻</div>
<div v-show="index === 2">财经新闻</div>
</div>易错点: 点击事件里改的是数据 index,不是直接改 DOM;v-show 控制的是显示隐藏,不是删除节点;类名对象写法里条件为真才加 active。
加强版切换:v-for 渲染按钮和新闻列表
为什么需要: 真实切换的数据通常来自数组,按钮和内容都不应写死。
怎么想: newsList 里每项有 id、typeName、items;按钮用 v-for 渲染,点击时 @click="index = i";内容区也用 v-for,并用 v-show="index === i" 控制当前分类。
怎么做:
html
<button
v-for="(item, i) in newsList"
:key="item.id"
:class="{ active: index === i }"
@click="index = i"
>{{ item.typeName }}</button>
<div
v-for="(data, i) in newsList"
:key="data.id"
v-show="index === i"
>
<p v-for="news in data.items" :key="news.id">
<a :href="news.link" target="_blank">{{ news.title }}</a>
</p>
</div>易错点: 转写稿明确提醒 v-for 不要和 v-if 结合使用,效率较慢;这里用 v-show 配合 index 判断。v-for 的第二个参数 i 才是下标,点击时要把 i 赋给 index,否则切换不生效。
本节标题写作“v-model”,但转写稿实际讲的是 v-for 列表渲染、key 以及原生/Vue 切换案例,未见 v-model 讲解;下面严格按转写内容整理,不补写未讲知识。原笔记未提供图片,以下保留转写中出现的代码。
v-for 遍历数组:为什么需要、怎么想、怎么做、易错点
为什么需要:列表渲染和 React 里用 map 类似,但 Vue 的 v-for 更灵活,不只数组,还能遍历对象、数字、字符串。
怎么想:数据驱动视图,数组长度决定生成多少个节点;每一项通过 item 拿到。
怎么做:
html
<div v-for="item in arr">{{ item }}</div>
<div v-for="(value, index) in arr">{{ index }} - {{ value }}</div>转写中数组为 arr,渲染出张三、王五、赵六等;第二个参数 index 是下标,从 0 开始。
易错点:item 只是形参,可换名;下标从 0 开始;不要把 v-for 写成 v-for 之外的拼写。
v-for 遍历对象、数字、字符串
为什么需要:对象要输出属性值/键;数字可用于页码;字符串也可逐字遍历。
怎么想:对象参数顺序是 (value, key);数字遍历从 1 开始,第二个参数是下标从 0 开始;字符串类似数组。
怎么做:
html
<div v-for="(value, key) in obj">{{ key }}:{{ value }}</div>
<div v-for="n in 10">{{ n }}</div>
<div v-for="(n, i) in 10">{{ i }} - {{ n }}</div>
<div v-for="(s, i) in str">{{ i }} - {{ s }}</div>转写调试:对象输出 username:张三、age:12;数字输出 1 到 10;字符串输出每个字及下标。
易错点:对象别把 (value, key) 写反;数字遍历不是从 0 开始;字符串下标从 0 开始。
key:为什么需要、怎么想、怎么做、易错点
为什么需要:key 帮助 Vue 的 diff 算法复用节点、提升渲染效率,减少状态错位。
怎么想:优先用稳定唯一 ID;没有 ID 才用下标;纯静态渲染用下标也可以。
怎么做:
html
<div v-for="item in arr" :key="item.id">{{ item.bookName }}</div>
<div v-for="(item, index) in arr" :key="index">{{ item.bookName }}</div>转写中强调 :key 写在 v-for 左边或右边都可,值用 item.id;重复 key 控制台会警告。
易错点:key 不要用随机数;列表会增删排序时慎用下标;转写提醒 v-for 不要和 v-if 同层结合使用,效率较慢,后续用面试题展开。
原生切换:为什么需要、怎么想、怎么做、易错点
为什么需要:先用原生写一遍,理解“按钮高亮 + 内容显示隐藏”的切换逻辑,再对照 Vue 的数据驱动。
怎么想:用 index 记录当前选中项;按钮点击后先清旧状态,再设新状态;内容按 index 控制 display。
怎么做:获取按钮和内容区,给按钮加点击事件;调试时打印 this.innerText 确认 this 指向按钮;初始化让体育显示、体育按钮高亮。
易错点:当前项重复点击可先判断跳过;先清旧再设新;初始化逻辑集中到初始化函数,避免散落。
Vue 乞丐版切换:为什么需要、怎么想、怎么做、易错点
为什么需要:体会框架效率:只改数据,视图自动渲染,几乎不写 DOM 操作。
怎么想:data 里放 index: 0;按钮用 :class 判断高亮;内容用 v-show 判断显示。
怎么做:
html
<button :class="{ active: index === 0 }" @click="index = 0">体育</button>
<button :class="{ active: index === 1 }" @click="index = 1">娱乐</button>
<div v-show="index === 0">体育新闻</div>
<div v-show="index === 1">娱乐新闻</div>点击后 index 改变,模板重新渲染,对应内容和 active 样式自动切换。
易错点:v-show 与 v-if 别混;index 初始值要对应默认项;调试工具不支持时转写中通过刷新页面重开解决。
加强版切换:为什么需要、怎么想、怎么做、易错点
为什么需要:把写死按钮改成数组渲染,更接近接口数据驱动。
怎么想:newsList 每项含 id、typeName、items;外层遍历按钮和内容区,内层遍历新闻。
怎么做:
html
<button v-for="(item, i) in newsList" :key="item.id"
:class="{ active: index === i }" @click="index = i">
{{ item.typeName }}
</button>
<div v-for="(item, i) in newsList" :key="item.id" v-show="index === i">
<p v-for="news in item.items" :key="news.id">
<a :href="news.link" target="_blank">{{ news.title }}</a>
</p>
</div>易错点:嵌套 v-for 的变量名别冲突;key 用 ID;转写强调 v-for 与 v-if 不要同层结合,这里用 v-show;链接 target="_blank" 拼写要正确。
为什么 v-for 是重点
列表渲染是前端最常见的需求。React 里常用 map 做列表,而 Vue 里用 v-for。转写稿里强调:Vue 的 v-for 比 React 的 map 更灵活,因为 React 里不能直接拿数字做遍历,通常要先把数字转成数组再 map;Vue 里可以直接遍历数组、对象、数字、字符串。
怎么想:先判断“我要渲染的数据是什么类型”,再决定 v-for 的写法。怎么做:把 v-for 写在要重复的标签上,表达式里拿到当前项,必要时再拿索引或键名。易错点:不要一上来就写复杂结构,先渲染出数量,再填内容。
基本用法:数组、对象、数字、字符串
数组遍历最常见。转写稿里用 item in arr,item 是数组元素;写成 (value, index) in arr 时,value 是元素值,index 是下标,从 0 开始。
html
<div v-for="item in arr">{{ item }}</div>
<div v-for="(value, index) in arr">{{ index }} - {{ value }}</div>对象遍历时,item in obj 拿到的是属性值;写成 (value, key) in obj,第一个是属性值,第二个是属性名。转写稿里用 value、key 是为了语义清楚,实际写 a、b 也可以。
html
<div v-for="(value, key) in obj">{{ key }}:{{ value }}</div>数字遍历时,item in num 会从 1 开始输出到 num;带第二个参数时,第二个参数是下标,从 0 开始。转写稿提到它可以用来做页码、总页数遍历。字符串也可以遍历,拿到的是每个字符,第二个参数是字符下标。
html
<div v-for="n in 10">{{ n }}</div>
<div v-for="(s, i) in str">{{ i }} - {{ s }}</div>易错点:对象参数顺序是“值、键”,不要写反;数字遍历从 1 开始,不是 0;索引从 0 开始。
key 的作用与写法
转写稿里说,key 的作用和 React 里类似,是为了提升渲染效率,配合 diff 算法做比较。建议能用唯一 id 就用 id,没有 id 时可以用下标。只做单纯渲染时,用下标也可以,效果一样;但如果后续有增删、复用等操作,唯一 id 更稳。
html
<div v-for="item in list" :key="item.id">{{ item.bookName }}</div>易错点:key 要写在 v-for 所在标签上;不要写重复的 key;:key 写在 v-for 左边或右边都可以,但不要漏冒号。
原生切换的推导与调试
转写稿用“体育、娱乐、财经”做切换。原生思路:先获取所有按钮和所有内容 div,给按钮绑定点击事件;初始化时让第一个按钮高亮、第一个内容显示,其余隐藏。点击时先判断当前下标是否和已选下标相同,相同就不处理;不同则清空旧按钮样式、隐藏旧内容,更新下标,再给新按钮加 active、让新内容显示。
调试时转写稿先输出点击事件里的 this 和按钮文字,确认事件绑定对象;再操作 className、style.display。易错点:旧状态没清空会导致多个按钮高亮或多个内容显示;初始化和点击逻辑重复时,可以抽成 hide/show 方法,初始化代码放到 init 里。
Vue 乞丐版切换
Vue 版的核心是“改数据,视图自动渲染”。在 data 里定义 index: 0,按钮用 v-for 渲染,:class="{ active: index === i }" 控制高亮,@click="index = i" 改数据;内容区用条件判断显示对应内容。
html
<button
v-for="(item, i) in tabs"
:key="item.id"
:class="{ active: index === i }"
@click="index = i"
>{{ item.typeName }}</button>易错点::class 是对象写法,别漏冒号;点击后只改 index,不要再手动操作 DOM;条件判断的索引要和按钮索引对应。
加强版切换与 v-for/v-if 注意
加强版把按钮和新闻数据放进 newsList:每项有 id、typeName、items。按钮继续 v-for 渲染,key 用 item.id;内容区再遍历 newsList,用 index === i 判断显示哪一组,组内再遍历 items 渲染标题和链接,链接可加 target="_blank"。
html
<div v-for="(item, i) in newsList" :key="item.id">
<template v-if="index === i">
<p v-for="news in item.items" :key="news.id">
<a :href="news.url" target="_blank">{{ news.title }}</a>
</p>
</template>
</div>转写稿特别提醒:v-for 不要和 v-if 直接结合使用,效率比较慢,后面可用计算属性等方案优化。易错点:外层 v-for 的 key 不要漏;内层新闻也要有 key;v-if 的判断下标要和按钮下标一致。
一、v-for:一个指令覆盖四种遍历
为什么需要:列表渲染是最高频的需求。React 里用 map 把数组转成元素,但 map 是数组独有的方法——想渲染 1 到 10 这样的页码,你得先 Array.from 造一个数组再 map。Vue 的 v-for 直接支持数组、对象、数字、字符串四类目标,省掉这层转换。
怎么想:把 v-for 理解成“写在谁身上,谁就被复制多少份”。in 后面跟的可以是 data 里的变量,也可以直接写数组字面量,两种写法等价。
怎么做:
html
<!-- 数组:只取值 -->
<div v-for="item in arr">{{ item }}</div>
<!-- 数组:取值 + 下标 -->
<div v-for="(value, index) in arr">{{ index }} - {{ value }}</div>
<!-- 对象:默认遍历属性值 -->
<div v-for="item in obj">{{ item }}</div>
<!-- 对象:值 + 键名 -->
<div v-for="(value, key) in obj">{{ key }}:{{ value }}</div>
<!-- 数字:从 1 开始 -->
<div v-for="n in num">{{ n }}</div>
<div v-for="(n, i) in num">{{ i }} - {{ n }}</div>
<!-- 字符串:按下标拆成字符 -->
<div v-for="s in str">{{ s }}</div>
<div v-for="(s, i) in str">{{ i }} - {{ s }}</div>数组里有 6 个元素,就会渲染 6 个 div;num 为 10 时输出 1 到 10,第二参数是 0 到 9;字符串同理,下标从 0 开始。
易错点:
- 数字遍历的值从 1 开始、下标从 0 开始,两个参数起点不同,做页码时别混。
- 括号里两个参数的名字是自己起的,叫
value/index只是为了语义清晰,不影响结果。 - 第二个参数只有在你写了括号时才有,别在不写括号的地方引用
index。
二、key:给 diff 一个稳定身份
为什么需要:key 的作用和 React 里的一样,配合 diff 算法提升渲染效率——更新时能认出“哪个节点还是原来那个”,而不是整段重绘。
怎么想:优先用数据自带的唯一标识 id;数据没有 id,又只是纯展示、不涉及增删排序,用下标也可以。
怎么做:
html
<!-- 用 id -->
<div v-for="item in arr" :key="item.id">{{ item.bookName }}</div>
<!-- 用下标 -->
<div v-for="(item, i) in arr" :key="i">{{ item.bookName }}</div>:key 写在 v-for 左边还是右边都行,同一个标签上位置不影响。
易错点:
- 手写死一个固定值(比如
:key="1")会立刻报重复 key 的警告。 - 转写里演示的列表数据形如
{ id, bookName, author },控制台一打开就能看到重复 key 的提示——这是最好的自查手段。
三、原生 JS 版 tab 切换:先把逻辑跑通
为什么需要:先用原生写一遍,才能体会 Vue 到底替我们省了什么。
怎么做:三个按钮、三个内容块,配好样式(按钮高亮 .active、内容块宽高和边框)。
js
var btns = document.querySelectorAll('#root button')
var divs = document.querySelectorAll('#root div')
var index = 0 // 当前选中的下标
function hide(n = index) {
divs[n].style.display = 'none'
btns[n].className = ''
}
function show(n = index) {
divs[n].style.display = 'block'
btns[n].className = 'active'
}
btns.forEach(function (item, i) {
item.onclick = function () {
if (i === index) return // 点自己,什么都不做
hide() // 先收掉旧的
index = i // 再更新选中下标
show() // 最后展示新的
}
})
show() // 初始化调试过程值得记住:先只写 console.log(this.innerText),确认事件绑上了、this 指向的是被点的按钮;再补显隐逻辑。当时就是因为把 this 写错、输出成了别的东西,才回头检查的。
易错点:
forEach的回调参数i和外层的index是两个东西,hide()里用的是index,不是i。- 顺序不能反:必须先
hide()旧的,再index = i,否则你隐藏的是新下标对应的块。 - 重复的“清样式 + 藏块”“加样式 + 显块”抽成
hide/show两个函数,默认参数取index,点击时传i,初始化和点击就都能复用。
四、乞丐版切换:让数据驱动视图
为什么需要:原生版每次改状态都要手动碰 DOM,Vue 里只改数据。
怎么做:
html
<div id="root">
<button :class="{ active: index === 0 }" @click="index = 0">体育</button>
<button :class="{ active: index === 1 }" @click="index = 1">娱乐</button>
<button :class="{ active: index === 2 }" @click="index = 2">财经</button>
<div v-show="index === 0">体育新闻</div>
<div v-show="index === 1">娱乐新闻</div>
<div v-show="index === 2">财经新闻</div>
</div>js
new Vue({ el: '#root', data: { index: 0 } })易错点::class 的对象写法里值是布尔表达式,别写成 { active: index };v-show 是控制 display,和 v-if 的销毁重建不同,这里用 v-show 更合适。
五、加强版切换:用真实的数据结构渲染
为什么需要:乞丐版把按钮和内容写死了,真实项目里这些都来自接口。
怎么做:把数据组织成数组,每项形如 { id, typeName, items: [{ id, title, link }] },items 里放该类下的新闻。
html
<div id="root">
<button
v-for="(item, i) in newsList"
:key="item.id"
:class="{ active: index === i }"
@click="index = i"
>{{ item.typeName }}</button>
<div v-for="(item, i) in newsList" :key="item.id" v-show="index === i">
<p v-for="data in item.items" :key="data.id">
<a :href="data.link" target="_blank">{{ data.title }}</a>
</p>
</div>
</div>index 初值 0,所以默认高亮第一个按钮、显示第一组新闻;点第二个按钮时 index 变 1,高亮和显示随之切换。
易错点:
- 内外两层
v-for的形参别重名,外层叫item,内层就换data,否则内层取不到外层的数据。 v-show里比较的是外层下标i,别写成item。v-for不要和v-if写在同一个元素上,效率差,这里统一用v-show。- 两层都要写
:key,且各自唯一。
课堂画面





代码示例
示例一:v-for 基本用法(数组 / 对象 / 数字 / 字符串 + key)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>v-for 基本用法</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 1. 遍历数组:item in arr -->
<div v-for="item in arr" :key="item">{{ item }}</div>
<hr>
<!-- 2. 遍历数组:(值, 下标) -->
<div v-for="(value, index) in arr" :key="index">{{ index }} - {{ value }}</div>
<hr>
<!-- 3. 遍历对象:(值, 键) -->
<div v-for="(value, key) in obj" :key="key">{{ key }} : {{ value }}</div>
<hr>
<!-- 4. 遍历数字:从 1 开始,共 10 个 -->
<div v-for="(n, i) in 10" :key="i">{{ n }}</div>
<hr>
<!-- 5. 遍历字符串 -->
<div v-for="(s, i) in str" :key="i">{{ i }} - {{ s }}</div>
</div>
<script>
new Vue({
el: '#root',
data: {
arr: ['张三', '李四', '王五', '赵六', '孙七', '秦八'],
obj: { username: '张三', age: 12 },
str: '我爱你中国'
}
})
</script>
</body>
</html>关键行与易错点
v-for="item in arr":数组长度是 6,就会渲染出 6 个div,item是元素本身;也可以直接写v-for="item in ['张三','李四']"把数组写在模板里。v-for="(value, index) in arr":括号里第一个是值、第二个是下标,顺序写反是最常见的错误;下标从0开始。v-for="(value, key) in obj":遍历对象时渲染的是属性值;加括号后第二个参数才是键名,别把value/key写反。v-for="(n, i) in 10":数字遍历n从 1 开始(1~10),而i从 0 开始(0~9),可用来做页码。v-for="(s, i) in str":字符串等价于被“转成数组”后遍历,s是字符、i是下标。:key建议能用唯一 id 就用 id,没有 id 才用下标;只在纯静态渲染时用下标也没问题。转写稿强调key的作用与 React 一样——提升 diff 效率。
常见错误
1. v-for 里用 index 当 key,列表增删后内容错位
- 现象:在列表头部插入或删除一项后,被删掉的却是另一行;输入框里的内容跟着串行。
- 原因:把
index作为 key,数组变动后同一位置的下标对应的元素已经换了,Vue 复用旧 DOM,导致状态错位。 - 修法:优先用数据自身的唯一标识(如
item.id),写成:key="item.id";只有纯静态渲染、列表不增删不排序时才可用下标兜底。
2. v-for 与 v-if 写在同一个标签上
- 现象:能出效果但控制台/评审提示不推荐,切换 tab 时渲染明显卡顿,甚至出现判断条件拿不到当前项数据。
- 原因:同一元素上
v-if优先级高于v-for,判断时循环变量还不存在;且每次渲染都要对所有项做一遍条件判断,浪费性能。 - 修法:把
v-if移到内层元素上,或先用computed过滤出要显示的列表再v-for;也可用<template v-for>包一层。
3. tab 切换里把 index 写死成 0/1/2,或在事件里改了 index 却没同步高亮
- 现象:点击娱乐后内容变了,但按钮高亮还停在体育;或者新增一个 tab 后点击无反应。
- 原因:高亮 class 用固定字符串判断(如
index === 0 ? 'active' : ''),与真实选中状态脱节;数据驱动不彻底。 - 修法:统一用
:class="{ active: index === currentIndex }",点击事件只做一件事——currentIndex = i,其余交给模板。
4. 原生写法遗留的 document.querySelectorAll 与 Vue 数据混用
- 现象:Vue 版本里还留着
btns[i].className = 'active'、divs[i].style.display = 'none',切几次后样式全乱,刷新才恢复。 - 原因:手动改 DOM 与 Vue 的虚拟 DOM 各管一套,Vue 重新渲染时会把手改的样式覆盖或叠加。
- 修法:删除所有 DOM 操作,只保留数据(
index、list),显示隐藏用v-show/v-if,样式用:class。
5. v-model 绑在 props、computed(无 setter)或非响应式字段上
- 现象:输入框能打字,但数据不变;或控制台报 “Computed property was assigned to but it has no setter”。
- 原因:
v-model本质是:value+@input的双向赋值,目标必须是可写的响应式数据。 - 修法:绑定到
data中的字段;必须基于 props 时改用computed的 get/set,或v-model+emit('update:xxx')。
6. 遍历对象/数字/字符串时参数顺序记反
- 现象:
v-for="(index, value) in obj"输出的是属性名而不是属性值;遍历数字时以为从 0 开始,页码少了一页。 - 原因:数组是
(item, index),对象是(value, key, index),数字是(n, index)且n从 1 开始——三者顺序规则不同。 - 修法:写之前先确认遍历目标类型;对象遍历用
(value, key),数字遍历页码直接用n(1 起始),下标另取第二个参数。
练习
练习 1(基础遍历) 给定 list = [{id:1, name:'张三'}, {id:2, name:'李四'}],用 v-for 渲染成 <li>,要求:key 用 id,并同时输出序号(从 1 开始)。再补一个用 v-for 遍历数字 1–10 生成页码按钮的例子。
练习 2(对象与字符串遍历) 把 { username: '张三', age: 12 } 渲染成 “属性名:属性值” 的列表;再把字符串 '我爱你中国' 逐字渲染,并在每个字后面标出它的下标。
练习 3(乞丐版 tab) 用 index 一个数据 + :class + v-show(或 v-if)实现体育/娱乐/财经三个 tab 的切换,要求点击事件里只允许写一行赋值语句,不允许出现任何 document.xxx。
练习 4(加强版 tab + 嵌套遍历) 数据结构为 [{id:1, typeName:'体育', items:[{id:11, title:'...', link:'...'}, ...]}, ...]。要求:外层遍历生成按钮(:key="item.id"),内层遍历生成新闻标题并加 <a :href="..." target="_blank">;切换用 currentIndex 控制。注意 v-if 不要和 v-for 写在同一个标签上。
练习 5(双向绑定) 在上题基础上加一个搜索框,用 v-model 绑定关键字,实时过滤当前分类下的新闻标题(提示:用 computed,不要在 v-for 上直接写 v-if 过滤)。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | v-for遍历 | 08:48 |
| 2 | key | 04:28 |
| 3 | 优化 | 12:21 |
| 4 | 乞丐版切换 | 04:09 |
| 5 | 加强版切换 | 14:22 |
小结
v-for是列表渲染的唯一入口,可遍历数组、对象、数字、字符串;数组(item, index)、对象(value, key, index)、数字(n, index)且 n 从 1 开始——参数顺序不要混。:key不是可选项。有唯一 id 就用 id,没有才退回下标;下标 key 只在列表不增删、不排序、不涉及组件内部状态时安全。v-for与v-if不要同标签共存,要么下沉v-if,要么先用computed过滤数据源——这既是正确性问题也是性能问题。- 从原生切到 Vue,最大的转变是“删掉所有 DOM 操作”:不取节点、不加
onclick、不手改className和style,只改数据。 - tab 切换的本质是“一个选中状态 + 数据驱动视图”:按钮高亮靠
:class对象语法,内容显隐靠v-show/v-if,事件里只做状态赋值。 v-model只是:value加@input的语法糖,绑定目标必须可写;遇到 props 用 get/set 的 computed 或update:xxx事件。- 渲染效率的优先级:正确的 key > 避免
v-for/v-if混用 > 用computed缓存过滤结果 > 组件拆分,不要一上来就做过早优化。 - 判断自己是否真正掌握的标准:给你一份“分类 + 子列表”的接口数据,能否不看笔记写出可切换、可过滤、带正确 key 的完整渲染。