Skip to content

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,且各自唯一。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

代码示例 ​

示例一: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/vue@2.7.16/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 过滤)。

原视频对照 ​

#视频时长
1v-for遍历08:48
2key04:28
3优化12:21
4乞丐版切换04:09
5加强版切换14:22

小结 ​

  1. v-for 是列表渲染的唯一入口,可遍历数组、对象、数字、字符串;数组 (item, index)、对象 (value, key, index)、数字 (n, index) 且 n 从 1 开始——参数顺序不要混。
  2. :key 不是可选项。有唯一 id 就用 id,没有才退回下标;下标 key 只在列表不增删、不排序、不涉及组件内部状态时安全。
  3. v-for 与 v-if 不要同标签共存,要么下沉 v-if,要么先用 computed 过滤数据源——这既是正确性问题也是性能问题。
  4. 从原生切到 Vue,最大的转变是“删掉所有 DOM 操作”:不取节点、不加 onclick、不手改 className 和 style,只改数据。
  5. tab 切换的本质是“一个选中状态 + 数据驱动视图”:按钮高亮靠 :class 对象语法,内容显隐靠 v-show/v-if,事件里只做状态赋值。
  6. v-model 只是 :value 加 @input 的语法糖,绑定目标必须可写;遇到 props 用 get/set 的 computed 或 update:xxx 事件。
  7. 渲染效率的优先级:正确的 key > 避免 v-for/v-if 混用 > 用 computed 缓存过滤结果 > 组件拆分,不要一上来就做过早优化。
  8. 判断自己是否真正掌握的标准:给你一份“分类 + 子列表”的接口数据,能否不看笔记写出可切换、可过滤、带正确 key 的完整渲染。