Vue 3 v-for 指南:从基础到精通,彻底掌握列表渲染的艺术

作者:前端摸鱼匠日期:2025/10/28

文章目录

    • 一、初识 v-for:动态世界的基石
      • 1.1 遍历数组:最常见的场景
          • 1.1.1 基础语法:`item in items`
            * 1.1.2 获取索引:`(item, index) in items`
            * 1.1.3 嵌套循环:处理二维或多维数据
        • 1.2 遍历对象:探索属性的奥秘
        • 1.3 遍历数字:生成固定序列
    • 二、核心机制:Key 的深度解析
      • 2.1 为什么需要 Key:虚拟 DOM 与 Diff 算法
        • 2.2 Key 的作用:为元素颁发“身份证”
        • 2.3 如何选择一个好的 Key:稳定、唯一、可预测
        • 2.4 Key 与组件:在 v-for 中使用组件
    • 三、高级技巧与最佳实践
      • 3.1 v-for 与 v-if 的“爱恨情仇”
        • 3.2 数据变更的响应式陷阱与对策
          • 3.2.1 数组的变更方法 vs 非变更方法
            * 3.2.2 直接通过索引修改数组
            * 3.2.3 直接修改数组长度
        • 3.3 列表过滤与排序的实战
        • 3.4 性能优化:当列表变得巨大
          • 3.4.1 什么是虚拟滚动?
            * 3.4.2 实现虚拟滚动
    • 四、总结与回顾

一、初识 v-for:动态世界的基石

嘿,朋友!欢迎来到 Vue 3 列表渲染的世界。想象一下,你正在构建一个炫酷的社交媒体应用,用户的动态、评论列表、好友列表……这些内容都不是一成不变的,它们会随着时间、用户操作而动态增删改。如果每一条数据我们都要手动在 HTML 里写一份,那工作量将是灾难性的,而且完全无法应对数据的变化。

这时候,v-for 指令就像一位拥有“分身术”的魔法师,闪亮登场。它的核心使命就是:基于一组数据(比如一个数组或一个对象),帮你自动地、高效地重复渲染一段模板结构。你只需要告诉它“数据源是什么”以及“每一份‘分身’长什么样”,它就能为你生成一整排整齐划一的 DOM 元素。

官方文档对 v-for 的定义是:“v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。”

这个定义非常精准,但我们可以用更通俗的话来理解。把 v-for 想象成一个勤劳的流水线工人:

  • items (数据源):就是传送带上待加工的零件。
  • item (别名):是工人拿起每一个零件时,给这个零件起的一个临时代号,方便在加工过程中称呼它。
  • v-for 指令本身:就是那条“对传送带上每个零件都执行一遍加工动作”的命令。

通过这个比喻,你应该能感觉到 v-for 的本质:它是一个循环指令,将模板逻辑与数据紧密地绑定在一起。接下来,我们就从最基础的用法开始,一步步揭开它的神秘面纱。

1.1 遍历数组:最常见的场景

在 Web 开发中,我们遇到的数据结构,十有八九是数组。用户列表、商品列表、文章列表……无一例外。因此,掌握 v-for 遍历数组是基本功中的基本功。

1.1.1 基础语法:item in items

让我们从一个最简单的例子开始。假设我们有一个任务清单,我们想把它们渲染成一个无序列表。

1<script setup>
2import { ref } from 'vue';
3
4// 准备一个包含任务对象的数组,作为我们的数据源
5// ref() 是 Vue 3 的 Composition API 中用来创建响应式数据的方法
6const tasks = ref([
7  { id: 1, title: '学习 Vue 3 的 v-for' },
8  { id: 2, title: '完成项目报告' },
9  { id: 3, title: '晚上吃顿好的' }
10]);
11</script>
12
13<template>
14  <h2>我的任务清单</h2>
15  <ul>
16    <!-- 
17      v-for 指令的核心语法:
18      - `task` 是我们为每个数组元素起的别名(临时变量名),你可以叫它 `item`, `t`, `taskItem` 等任何合法的变量名。
19      - `tasks` 是我们在 <script> 中定义的响应式数组。
20      - [`:key="task.id"`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.id.md) 是一个至关重要的属性,我们稍后会详细讲解。现在你只需要知道,给 v-for 生成的每个元素一个独一无二的 key 是一个好习惯。
21    -->
22    <li v-for="task in tasks" :key="task.id">
23      <!-- 在这里,我们可以直接使用别名 `task` 来访问当前循环到的对象 -->
24      {{ task.title }}
25    </li>
26  </ul>
27</template>
28
29<style>
30/* 添加一点简单的样式,让列表更好看 */
31ul {
32  list-style-type: none;
33  padding: 0;
34}
35li {
36  background-color: #f0f0f0;
37  margin: 5px 0;
38  padding: 10px;
39  border-radius: 4px;
40}
41</style>
42

代码剖析与解读:

  1. 数据准备:在 <script setup> 中,我们使用 ref 创建了一个名为 tasks 的响应式数组。数组里的每个元素都是一个对象,包含 idtitleref 的作用是让 Vue 能够追踪这个数组的变化,一旦数组内容改变(比如增加、删除、修改了某个任务),Vue 就会自动更新视图。
  2. 模板绑定:在 <template> 部分,我们在 <li> 标签上写下了 v-for="task in tasks"
    • Vue 解析到这个指令时,会立刻去 tasks 数组里取数据。
    • 它会取出第一个元素 { id: 1, title: '学习 Vue 3 的 v-for' },并将其赋值给我们定义的别名 task
    • 然后,它会用这个 task 对象去渲染 <li> 标签及其内部内容。此时,{{ task.title }} 就会被替换成 “学习 Vue 3 的 v-for”。
    • 接着,Vue 重复这个过程,取出第二个元素,渲染第二个 <li>;取出第三个元素,渲染第三个 <li>……直到遍历完整个数组。
  3. 结果呈现:最终,浏览器会得到一个包含三个 <li> 元素的 <ul> 列表,每个 <li> 都显示着对应的任务标题。

这就是 v-for 最基础、最核心的工作流程。它就像一个翻译官,把 JavaScript 的数组数据“翻译”成了对应的 HTML 结构。

1.1.2 获取索引:(item, index) in items

在实际开发中,我们常常需要知道当前正在处理的是第几个元素。比如,显示排名、序号,或者在某些特定操作中需要用到索引。v-for 也贴心地提供了获取索引的方式。

语法上,我们只需要在别名后面增加一个变量,用括号括起来即可:v-for="(task, index) in tasks"

让我们改造一下上面的例子,给每个任务加上序号。

1<script setup>
2import { ref } from 'vue';
3
4const tasks = ref([
5  { id: 1, title: '学习 Vue 3 的 v-for' },
6  { id: 2, title: '完成项目报告' },
7  { id: 3, title: '晚上吃顿好的' }
8]);
9</script>
10
11<template>
12  <h2>我的任务清单(带序号)</h2>
13  <ul>
14    <!-- 
15      语法升级:
16      - `task` 依然是元素的别名。
17      - `index` 是当前元素在数组中的索引,从 0 开始计数。
18    -->
19    <li v-for="(task, index) in tasks" :key="task.id">
20      <!-- 
21        我们现在可以在模板中同时使用 `task` 和 `index` 了。
22        注意:索引是从 0 开始的,所以通常我们会显示 `index + 1` 来作为人类习惯的序号。
23      -->
24      任务 #{{ index + 1 }}: {{ task.title }}
25    </li>
26  </ul>
27</template>
28
29<style>
30/* ... 样式保持不变 ... */
31</style>
32

代码剖析与解读:

  • 参数顺序(item, index) 的顺序是固定的。第一个参数永远是数组元素的值,第二个参数才是索引。你当然可以叫它们别的名字,比如 (val, idx),但位置不能错。
  • 索引的起点:请务必记住,JavaScript 中的数组索引是从 0 开始的。所以第一个元素的 index0,第二个是 1,以此类推。这就是为什么我们在显示序号时使用了 index + 1
  • 应用场景:索引虽然好用,但也要谨慎使用,尤其是在作为 key 时,我们后面会重点讨论。它的常见合法用途包括:
    • 显示序号(如本例)。
    • 在没有唯一 ID 的情况下,作为临时的、不稳定的标识符(不推荐用于 key)。
    • 在某些计算或逻辑判断中需要用到位置信息。
1.1.3 嵌套循环:处理二维或多维数据

世界是复杂的,数据也是。我们经常会遇到“嵌套”的数据结构,比如一个分类列表,每个分类下又有一堆商品。这时,就需要用到嵌套的 v-for

假设我们要渲染一个电影分类列表,每个分类下有多部电影。

1<script setup>
2import { ref } from 'vue';
3
4// 这是一个嵌套数组,外层数组是分类,内层数组是电影
5const categories = ref([
6  {
7    id: 'cat1',
8    name: '科幻片',
9    movies: [
10      { id: 'm1', title: '星际穿越' },
11      { id: 'm2', title: '盗梦空间' }
12    ]
13  },
14  {
15    id: 'cat2',
16    name: '喜剧片',
17    movies: [
18      { id: 'm3', title: '功夫' },
19      { id: 'm4', title: '大话西游' }
20    ]
21  }
22]);
23</script>
24
25<template>
26  <h1>电影分类</h1>
27  <div v-for="category in categories" :key="category.id">
28    <h2>{{ category.name }}</h2>
29    <ul>
30      <!-- 
31        这是一个嵌套的 v-for。
32        外层循环遍历 `categories`,别名是 `category`。
33        内层循环遍历当前 `category` 对象的 `movies` 数组,别名是 `movie`。
34        注意作用域:内层循环可以访问外层循环的 `category` 变量,但外层循环无法访问内层的 `movie`。
35      -->
36      <li v-for="movie in category.movies" :key="movie.id">
37        {{ movie.title }}
38      </li>
39    </ul>
40  </div>
41</template>
42
43<style>
44/* ... 略 ... */
45</style>
46

代码剖析与解读:

  1. 数据结构categories 是一个数组,每个元素是一个对象,代表一个分类。这个分类对象自身又包含一个 movies 数组。
  2. 外层循环v-for="category in categories" 负责遍历所有的分类。它为每个分类生成一个 <div> 容器,并显示分类的名称 <h2>{{ category.name }}</h2>
  3. 内层循环:在 <div> 内部,我们又写了一个 v-for="movie in category.movies"。这个指令的作用是,针对当前正在处理的 category,去遍历它的 movies 数组。
  4. 作用域链:这里有一个非常重要的概念——作用域。内层 v-for 的模板(即 <li> 及其内部)可以访问到它自己定义的 movie 变量,同时也能“继承”或访问到外层 v-for 定义的 category 变量。这就是为什么我们可以直接写 category.movies。反之,在外层 v-for 的模板中(比如 <h2> 标签那里),你是无法访问到 movie 变量的,因为它还没被定义。
  5. Key 的层级:请注意 :key 的使用。外层循环的 key 使用了分类的 id (category.id),内层循环的 key 使用了电影的 id (movie.id)。这确保了在任何一个层级的数据发生变化时,Vue 都能准确地找到需要更新的 DOM 元素。

嵌套循环是处理复杂数据结构的利器,只要你理解了作用域的概念,就可以像套娃一样一层一层地处理下去。

1.2 遍历对象:探索属性的奥秘

v-for 不仅能遍历数组,还能遍历一个对象的所有属性。这在处理一些配置信息、用户资料等键值对数据时非常有用。

1.2.1 基础语法:(value, key, index) in object

遍历对象的语法稍微复杂一点,因为它可以同时获取到三个值:属性值、属性名和索引。

  • value: 属性的值。
  • key: 属性的名(字符串)。
  • index: 索引,从 0 开始,表示是第几个属性。

让我们用一个用户信息对象来演示。

1<script setup>
2import { reactive } from 'vue';
3
4// 使用 reactive 创建一个响应式对象
5// reactive 和 ref 类似,但专门用于对象和数组
6const userProfile = reactive({
7  name: '张三',
8  age: 28,
9  city: '北京',
10  occupation: '前端工程师'
11});
12</script>
13
14<template>
15  <h2>用户资料</h2>
16  <table border="1" style="border-collapse: collapse; width: 300px;">
17    <thead>
18      <tr>
19        <th>属性名</th>
20        <th>属性值</th>
21      </tr>
22    </thead>
23    <tbody>
24      <!-- 
25        遍历对象的语法:
26        - `value` 是属性值 (e.g., '张三')
27        - `key` 是属性名 (e.g., 'name')
28        - `index` 是索引 (e.g., 0, 1, 2...)
29      -->
30      <tr v-for="(value, key, index) in userProfile" :key="key">
31        <td>{{ key }}</td>
32        <td>{{ value }}</td>
33      </tr>
34    </tbody>
35  </table>
36</template>
37

代码剖析与解读:

  1. 数据准备:这里我们使用了 reactive 而不是 ref。对于对象类型的数据,reactive 更为直接,它会将对象的深层属性都变为响应式的。ref 也可以,但访问时需要 .valueuserProfile.value.name),reactive 则可以直接访问(userProfile.name)。
  2. 模板遍历v-for="(value, key, index) in userProfile" 展开了 userProfile 对象。
    • 第一次循环:value'张三'key'name'index0
    • 第二次循环:value28key'age'index1
    • ……以此类推。
  3. 参数顺序:和数组遍历一样,参数顺序是固定的。(value, key, index)。你也可以只获取前两个:v-for="(value, key) in userProfile",或者只获取值:v-for="value in userProfile"
  4. Key 的选择:在遍历对象时,使用 key(属性名)作为 :key 的值是一个非常自然且稳定的选择,因为对象的属性名通常是唯一的。
1.2.2 对象遍历的顺序

一个有趣的知识点是:JavaScript 对象的属性遍历顺序是怎样的?在 ES2015 (ES6) 之前,这个顺序并没有被标准化,不同引擎的实现可能不同。但从 ES2015 开始,规范规定了 Object.getOwnPropertyNames()Reflect.ownKeys() 等方法以及 for...in 循环的属性遍历顺序:

  1. 首先遍历所有数值键,按数值升序排列。
  2. 然后遍历所有字符串键,按加入时间升序排列。
  3. 最后遍历所有 Symbol 键,按加入时间升序排列。

Vue 的 v-for 在遍历对象时,其底层实现依赖于 JavaScript 的 for...in 循环(或类似的机制),所以它也遵循这个顺序。

看个例子:

1<script setup>
2import { reactive } from 'vue';
3
4const weirdObject = reactive({
5  '1': '数字键1',
6  name: '字符串键name',
7  '3': '数字键3',
8  age: '字符串键age',
9  '2': '数字键2'
10});
11</script>
12
13<template>
14  <h2>对象属性遍历顺序演示</h2>
15  <ul>
16    <li v-for="(value, key) in weirdObject" :key="key">
17      Key: {{ key }}, Value: {{ value }}
18    </li>
19  </ul>
20  <!-- 
21    最终渲染顺序会是:
22    Key: 1, Value: 数字键1
23    Key: 2, Value: 数字键2
24    Key: 3, Value: 数字键3
25    Key: name, Value: 字符串键name
26    Key: age, Value: 字符串键age
27  -->
28</template>
29

了解这个顺序可以帮助你预测 v-for 的渲染结果,避免因顺序问题产生困惑。不过,在绝大多数业务场景中,我们并不需要过分依赖这个顺序,如果顺序很重要,通常会用数组来组织数据。

1.3 遍历数字:生成固定序列

有时候,我们只是想简单地重复渲染某个元素 N 次,比如生成一个 5 星评分组件的星星,或者创建一个分页器的页码按钮。这时,v-for 也可以遍历一个数字。

语法非常简单:v-for="n in count"。这里的 n 会从 1 开始,一直到 count

注意: 这和数组索引从 0 开始不同,遍历数字时,变量是从 1 开始的。这是一个需要特别留意的“小陷阱”。

1<script setup>
2import { ref } from 'vue';
3
4const rating = ref(4); // 假设当前评分是 4 星
5const maxRating = 5; // 满分是 5 星
6</script>
7
8<template>
9  <h2>评分组件</h2>
10  <div class="rating">
11    <!-- 
12      遍历数字 1 到 5。
13      `star` 的值会依次是 1, 2, 3, 4, 5。
14    -->
15    <span 
16      v-for="star in maxRating" 
17      :key="star"
18      class="star"
19      :class="{ filled: star <= rating }"
20    >
21      <!-- 使用 Unicode 字符作为星星 -->
2223    </span>
24  </div>
25</template>
26
27<style>
28.rating {
29  font-size: 2rem;
30}
31.star {
32  color: #ccc; /* 默认灰色 */
33}
34.star.filled {
35  color: #f5c518; /* 激活的金色 */
36}
37</style>
38

代码剖析与解读:

  1. 循环逻辑v-for="star in maxRating" 会循环 5 次。第一次,star1;第二次,star2……
  2. 动态 Class:我们使用了 Vue 的动态 Class 绑定 :class="{ filled: star <= rating }"
    • 这是一个对象语法,filled 是类名,star <= rating 是一个布尔表达式。
    • star 的值小于或等于 rating(当前评分 4)时,表达式为 truefilled 类就会被添加到 <span> 元素上,星星变成金色。
    • star 的值大于 rating 时(即第 5 颗星),表达式为 falsefilled 类不会被添加,星星保持灰色。
  3. Key 的使用:这里我们直接用循环变量 star 作为 key,因为在这个场景下,star (1, 2, 3, 4, 5) 是固定且唯一的,非常适合做 key

这个例子完美地展示了 v-for 在处理固定序列时的简洁与强大。


二、核心机制:Key 的深度解析

如果说 v-for 是列表渲染的“术”,那么 :key 就是其中的“道”。理解 :key 的工作原理,是从 Vue 新手走向进阶的关键一步。很多关于列表渲染的诡异 bug,比如输入框内容错乱、动画状态异常,其根源都与 :key 有关。

2.1 为什么需要 Key:虚拟 DOM 与 Diff 算法

要理解 :key,我们必须先简单了解一下 Vue 底层的两个核心概念:虚拟 DOM (Virtual DOM)Diff 算法

  • 虚拟 DOM (VDOM):你可以把它想象成真实 DOM 的一个轻量级的 JavaScript “蓝图”或“副本”。每当数据变化时,Vue 不会立即去操作真实的、昂贵的 DOM 元素。相反,它会先在内存中根据新数据重新生成一个新的虚拟 DOM 树。
  • Diff 算法:当新的虚拟 DOM 树生成后,Vue 会用 Diff 算法去比较“新旧”两个虚拟 DOM 树的差异。这个算法非常高效,它能精确地找出哪些地方发生了变化(比如一个文本变了、一个元素被删除了、一个元素的顺序变了)。
  • 打补丁:最后,Vue 只把这些差异应用到真实的 DOM 上,这个过程就叫“打补丁”。这样就避免了大规模、低效的 DOM 操作,大大提升了性能。

现在,:key 登场了。当 v-for 渲染列表时,Diff 算法面临一个难题:当列表数据发生变化(比如排序、增删)时,如何高效地更新对应的真实 DOM 元素?

如果没有 :key,Vue 会采用一种“就地复用”的策略。它会尽可能地复用已有的 DOM 元素,只修改它们的内容。这种策略在某些情况下是高效的,但在另一些情况下则会引发灾难。

让我们用一个经典的例子来感受一下这个“灾难”。

1<script setup>
2import { ref } from 'vue';
3
4const items = ref([
5  { id: 1, text: '项目 A' },
6  { id: 2, text: '项目 B' },
7  { id: 3, text: '项目 C' }
8]);
9
10const shuffle = () => {
11  // 打乱数组顺序
12  items.value = items.value.sort(() => Math.random() - 0.5);
13};
14</script>
15
16<template>
17  <button @click="shuffle">打乱顺序</button>
18  <ul>
19    <!-- 注意:这里我们故意不加 :key -->
20    <li v-for="item in items">
21      {{ item.text }}
22      <input type="text" placeholder="在这里输入..." />
23    </li>
24  </ul>
25</template>
26

操作与现象:

  1. 在页面加载后,你在第一个输入框里输入一些内容,比如“我是A的输入”。
  2. 点击“打乱顺序”按钮。
  3. 你会发现,<li> 标签里的文本(项目 A, B, C)确实被打乱了,但是你输入的内容“我是A的输入”却留在了原来的第一个 <li> 位置,而不是跟着“项目 A”一起移动。现在它可能和“项目 C”或“项目 B”配对在了一起。

原理剖析:

这就是“就地复用”的副作用。

  • 没有 Key 时:当 items 数组顺序改变后,Vue 的 Diff 算法看到新旧列表都有 3 个 <li>。为了高效,它决定不销毁和重新创建 <li> 元素,而是复用它们。
  • 它会拿着旧的 items (A, B, C) 和新的 items (比如 C, A, B) 按位置一一比较:
    • 旧的第 1 个 <li> (内容是 “项目 A”) vs 新的第 1 个 <li> (内容应该是 “项目 C”)。算法发现文本不同,于是只修改了文本内容,把 “项目 A” 改成了 “项目 C”。但是,它完全没动这个 <li> 内部的 <input> 元素!你输入的内容还好好地待在里面。
    • 旧的第 2 个 <li> (内容是 “项目 B”) vs 新的第 2 个 <li> (内容应该是 “项目 A”)。修改文本为 “项目 A”,<input> 状态保留。
    • 旧的第 3 个 <li> (内容是 “项目 C”) vs 新的第 3 个 <li> (内容应该是 “项目 B”)。修改文本为 “项目 B”,<input> 状态保留。

Vue 的算法只关心“同一位置的元素内容是否变了”,它不知道“哪个元素是哪个”。这就导致了“魂不附体”的现象。

2.2 Key 的作用:为元素颁发“身份证”

:key 的出现,就是为了解决这个身份识别问题。它给 v-for 生成的每一个元素提供了一个独一无二的标识,就像一张身份证。

当我们给 v-for 加上 :key="item.id" 后,Diff 算法的工作方式就变了:

  • 有 Key 时:算法不再按位置比较,而是按 key 比较。
  • 它会拿着旧列表的 key 集合 ([1, 2, 3]) 和新列表的 key 集合 (比如打乱后是 [3, 1, 2]) 进行比对。
  • 算法发现:
    • key=3 的元素现在排在了第一位。它会在旧的 DOM 树里找到 key=3 的那个 <li>,然后把它移动到列表的开头。
    • key=1 的元素现在排在了第二位。它会在旧的 DOM 树里找到 key=1 的那个 <li>,然后把它移动到列表的第二位。
    • key=2 的元素同理,移动到第三位。

在这个过程中,DOM 元素、它们的状态(包括 <input> 的内容)以及它们上面的事件监听器,都会被完整地保留和移动。文本内容自然也会跟着正确的元素一起走。

现在,我们把 :key 加回去,再试一次:

1<script setup>
2// ... script 部分不变 ...
3</script>
4
5<template>
6  <button @click="shuffle">打乱顺序</button>
7  <ul>
8    <!-- 
9      添加了 :key="item.id"
10      现在,每个 <li> 都有了一个独一无二的、与数据绑定的身份标识。
11    -->
12    <li v-for="item in items" :key="item.id">
13      {{ item.text }}
14      <input type="text" placeholder="在这里输入..." />
15    </li>
16  </ul>
17</template>
18

操作与现象:

  1. 在第一个输入框(对应“项目 A”)输入“我是A的输入”。
  2. 点击“打乱顺序”。
  3. 奇迹发生了! 你会发现,“项目 A” 这行文本,连同你输入的“我是A的输入”,像一个整体一样,一起移动到了新的位置。数据和它的状态完美地绑定在了一起。

这就是 :key 的魔力。它告诉 Vue:“别傻傻地按位置复用了,请根据这个 key 来识别、移动、创建或销毁元素,确保每个数据项都能找到它自己的 DOM 宿主。”

2.3 如何选择一个好的 Key:稳定、唯一、可预测

既然 :key 这么重要,那我们应该用什么值来做 key 呢?这里有几个黄金法则:

  1. 唯一性key 必须在当前列表的兄弟元素之间是唯一的。你不能用 item.text 作为 key,万一有两个项目的文本相同,key 就重复了,会引发警告和不可预期的行为。
  2. 稳定性key 应该是与数据本身绑定的、不会随数据内容或索引变化而变化的值。这就是为什么强烈推荐使用后端返回的唯一 ID(如 item.id。因为无论项目内容如何变化,它的 ID 是不变的。
  3. 避免使用 index 作为 key:这是一个非常常见的错误,尤其是在初学者中。v-for="(item, index) in items" :key="index" 看起来很方便,但它几乎和不加 key 一样糟糕。
    为什么不能用 index
    因为 index 是不稳定的。当列表发生增删或排序时,每个元素的 index 都会改变。这又回到了“就地复用”的老问题。
    • 场景一:列表排序
      * 原列表:A(index:0), B(index:1), C(index:2)
      * 排序后:C(index:0), A(index:1), B(index:2)
      * Diff 算法看到 key=0 的元素内容从 A 变成了 C,它只会修改文本,而不是移动元素。key 失去了身份识别的意义。
    • 场景二:列表头部插入
      * 原列表:A(key:0), B(key:1)
      * 在头部插入 X 后:X(key:0), A(key:1), B(key:2)
      * Diff 算法比较:
      * key=0:旧 A -> 新 X,修改文本。
      * key=1:旧 B -> 新 A,修改文本。
      * key=2:新增 B,创建新元素。
      * 结果:Vue 错误地把 A 的内容更新成了 X,把 B 的内容更新成了 A,最后又创建了一个新的 B。这导致了大量不必要的 DOM 操作,并且同样会引发状态错乱。
      什么时候 index 可以用?
      只有在一种极其罕见的情况下可以:你的列表是静态的,永远不会被重新排序、过滤或在中间增删元素。比如,你只是用它来渲染 5 个固定的导航选项。但即便如此,使用一个有意义的 key 依然是更好的实践。
  4. 不要使用随机数或时间戳key="Math.random()"key="Date.now()"。这会导致每次渲染,key 都是一个全新的值。Vue 会认为这是一个全新的元素,从而销毁旧的,创建新的。这会完全破坏组件的复用,导致性能急剧下降,并且所有状态都会丢失。

最佳实践总结表:

Key 的选择推荐度原因分析
item.id (唯一且稳定的ID)⭐⭐⭐⭐⭐ (强烈推荐)完美满足唯一性和稳定性,是 key 的最佳选择。
item (对象本身)⭐⭐⭐ (谨慎使用)如果对象本身是唯一的且引用不变,可以。但如果对象内容变化,Vue 可能无法识别。不如 id 明确。
index (数组索引)(强烈不推荐)不稳定,列表变化时会导致错误的 DOM 更新和状态错乱。
随机数/时间戳(绝对禁止)每次都变,导致组件被频繁销毁和重建,性能灾难。

2.4 Key 与组件:在 v-for 中使用组件

v-for 用于渲染自定义组件时,key 的使用变得更加重要,并且有一个需要特别注意的细节。

假设我们有一个 TodoItem.vue 组件:

1<!-- TodoItem.vue -->
2<script setup>
3defineProps({
4  todo: {
5    type: Object,
6    required: true
7  }
8});
9</script>
10
11<template>
12  <li>{{ todo.text }}</li>
13</template>
14

现在我们在父组件中使用它:

1<!-- ParentComponent.vue -->
2<script setup>
3import { ref } from 'vue';
4import TodoItem from './TodoItem.vue';
5
6const todos = ref([
7  { id: 1, text: '学习 v-for' },
8  { id: 2, text: '学习 key' }
9]);
10</script>
11
12<template>
13  <ul>
14    <!-- 
15      在组件上使用 v-for,:key 必须直接写在 <TodoItem> 组件标签上。
16      这样 Vue 才能正确地将 key 与组件实例关联起来。
17    -->
18    <TodoItem 
19      v-for="todo in todos" 
20      :key="todo.id" 
21      :todo="todo" 
22    />
23  </ul>
24</template>
25

关键点:

  • key 的位置key 必须直接设置在 <TodoItem> 组件标签上,而不是组件内部的模板根元素上(比如 TodoItem.vue 里的 <li>)。这是因为 keyv-for 指令用来识别外部循环中每个“项”的,它需要绑定到 v-for 直接作用的那个元素或组件上。
  • Props 传递:我们通过 :todo="todo" 将当前循环到的 todo 对象作为 prop 传递给了 TodoItem 组件。这是父子组件通信的标准方式。

todos 列表变化时,Vue 会根据 todo.id 这个 key 来决定是复用、移动、更新还是销毁哪个 TodoItem 组件实例。这确保了组件内部的状态(如果有的话)能够被正确地保留。


三、高级技巧与最佳实践

掌握了基础和核心机制后,我们来聊聊一些更高级的话题,这些技巧能让你在实战中写出更高效、更健壮、更优雅的代码。

3.1 v-for 与 v-if 的“爱恨情仇”

在 Vue 2 中,当 v-forv-if 用在同一个元素上时,v-for 的优先级更高。这导致 v-if 无法访问到 v-for 的变量,是一个常见的错误。

在 Vue 3 中,这个情况发生了逆转。v-if 的优先级现在高于 v-for。这意味着,如果你把它们写在同一个元素上,v-if 将无法访问到 v-for 中的变量(如 item),因为 v-if 先执行,此时 item 还没有被定义。

1<!-- 错误示范!在 Vue 3 中会报错 -->
2<li v-for="user in users" :key="user.id" v-if="user.isActive">
3  {{ user.name }}
4</li>
5

这段代码会抛出一个错误,类似于 Property "user" was accessed during render but is not defined on instance。因为 Vue 会先尝试计算 v-if="user.isActive",但此时它根本不知道 user 是什么。

那么,如何实现“只渲染列表中满足条件的项”呢?

3.1.1 解决方案一:使用 <template> 标签

<template> 标签是一个不可见的包裹元素,它不会在最终的 DOM 中渲染成任何实际标签,但可以作为指令的载体。我们可以把 v-for 放在 <template> 上,然后把 v-if 放在内部的元素上。

1<script setup>
2import { ref } from 'vue';
3
4const users = ref([
5  { id: 1, name: 'Alice', isActive: true },
6  { id: 2, name: 'Bob', isActive: false },
7  { id: 3, name: 'Charlie', isActive: true }
8]);
9</script>
10
11<template>
12  <h2>活跃用户列表 (使用 template)</h2>
13  <ul>
14    <!-- 
15      1. v-for 指令作用在 <template> 标签上,负责循环。
16      2. <template> 内部的 <li> 元素会被重复渲染。
17      3. v-if 指令作用在 <li> 上,此时它可以访问到外层 v-for 定义的 `user` 变量。
18    -->
19    <template v-for="user in users" :key="user.id">
20      <li v-if="user.isActive">
21        {{ user.name }}
22      </li>
23    </template>
24  </ul>
25</template>
26

优点

  • 语法直观,逻辑清晰。
  • 解决了 v-if 无法访问 v-for 变量的问题。

缺点

  • 性能问题:这种方式仍然有性能开销。Vue 会遍历整个 users 数组,为每一个 user 都创建一个 <li> 的虚拟节点,然后才通过 v-if 判断是否要渲染它。如果列表很大,而满足条件的元素很少,这就造成了不必要的计算。
3.1.2 解决方案二:使用计算属性(最佳实践)

更推荐、性能更好的方法是使用计算属性。我们可以在 JavaScript 代码中预先对数组进行过滤,然后 v-for 只需要遍历过滤后的结果即可。

1<script setup>
2import { ref, computed } from 'vue';
3
4const users = ref([
5  { id: 1, name: 'Alice', isActive: true },
6  { id: 2, name: 'Bob', isActive: false },
7  { id: 3, name: 'Charlie', isActive: true }
8]);
9
10// 创建一个计算属性 `activeUsers`
11// 它会根据 `users` 的变化自动重新计算
12const activeUsers = computed(() => {
13  // 使用数组的 filter 方法,返回一个只包含活跃用户的新数组
14  return users.value.filter(user => user.isActive);
15});
16</script>
17
18<template>
19  <h2>活跃用户列表 (使用计算属性)</h2>
20  <ul>
21    <!-- 
22      v-for 现在只需要遍历 `activeUsers`,这个数组已经是过滤好的结果。
23      模板变得非常干净,没有任何条件判断逻辑。
24    -->
25    <li v-for="user in activeUsers" :key="user.id">
26      {{ user.name }}
27    </li>
28  </ul>
29</template>
30

优点

  • 性能卓越:计算属性是基于它们的响应式依赖进行缓存的。只有当 users 数组发生变化时,activeUsers 才会重新计算。v-for 遍历的是一个更小的、已经过滤好的数组,大大减少了循环次数和 DOM 节点的创建。
  • 关注点分离:将数据过滤的逻辑(JavaScript)和视图渲染的逻辑(HTML)清晰地分离开。模板只负责展示,业务逻辑放在 <script> 中,代码更易维护。
  • 可复用性activeUsers 这个计算属性可以在模板的多个地方被复用,而无需重复编写过滤逻辑。

结论永远优先使用计算属性来处理列表的过滤、排序等逻辑。这是 Vue 开发中最重要的最佳实践之一。只有在极少数无法使用计算属性的场景下,才考虑 <template> 标签方案。

3.2 数据变更的响应式陷阱与对策

Vue 的响应式系统非常强大,但它也有一些“规则”。如果你不按规则来修改数据,Vue 可能无法检测到变化,从而导致视图不更新。这在 v-for 中尤其需要注意。

3.2.1 数组的变更方法 vs 非变更方法

Vue 能够侦测到数组的一些“变更方法”调用,因为这些方法会原地修改数组。

  • 变更方法push(), pop(), shift(), unshift(), splice(), sort(), reverse()
    当你调用这些方法时,Vue 会知道数组变了,并自动触发视图更新。
  • 非变更方法filter(), concat(), slice()
    这些方法不会修改原数组,而是返回一个新数组。如果你直接调用它们,Vue 是不知道变化的。

错误示范:

1// 假设 `items` 是一个 ref 数组
2// 这样做是无效的!因为 filter 返回了一个新数组,但我们没有用它来替换旧的数组。
3items.value.filter(item => item.id !== targetId);
4

正确做法:

1// 将 filter 返回的新数组赋值给原来的 ref
2items.value = items.value.filter(item => item.id !== targetId);
3

通过重新赋值,我们改变了 items.value 这个 ref 的引用,Vue 的响应式系统就能捕捉到这个变化,并更新视图。

数组变更方法与非变更方法对比表:

方法类型方法列表特点Vue 响应式行为使用示例
变更方法push, pop, shift, unshift, splice, sort, reverse原地修改原数组自动触发更新items.value.push(newItem)
非变更方法filter, concat, slice返回一个新数组,不修改原数组不会自动触发更新items.value = items.value.filter(...)
3.2.2 直接通过索引修改数组

JavaScript 中,我们可以直接通过索引来修改数组元素:arr[0] = 'new value'。然而,这种操作在 Vue 2 中不是响应式的,在 Vue 3 中也有限制

虽然在 Vue 3 中,由于使用了 Proxy,直接 items.value[0] = ... 在大多数情况下是响应式的,但这仍然不被推荐,因为它存在一些边缘情况和性能上的考量。更可靠、更明确的方式是使用 splice

场景: 将索引为 1 的元素替换为新对象。

推荐做法:使用 splice

1// 从索引 1 开始,删除 1 个元素,并插入 `newItem`
2items.value.splice(1, 1, newItem);
3

splice 是一个变更方法,Vue 完全可以追踪到它,并且这种方式意图明确,代码可读性更好。

3.2.3 直接修改数组长度

同样,直接修改数组的 length 属性(arr.length = newLength)也不是响应式的。

场景: 将数组长度清零。

推荐做法: 直接赋值一个空数组。

1items.value = [];
2

或者使用 splice(0)

1items.value.splice(0);
2

这两种方式都能被 Vue 正确地侦测到。

总结: 当你需要修改数组时,优先使用 Vue 的变更方法。如果需要通过索引或长度来修改,请使用 splice 或直接替换整个数组引用。这样可以确保你的数据变化总能如实地反映在视图上。

3.3 列表过滤与排序的实战

结合计算属性,我们可以非常优雅地实现列表的搜索过滤和动态排序功能。

让我们构建一个完整的例子:一个可以搜索、可以按不同字段排序的用户列表。

1<script setup>
2import { ref, computed } from 'vue';
3
4const users = ref([
5  { id: 1, name: 'Alice', age: 25, city: 'New York' },
6  { id: 2, name: 'Bob', age: 30, city: 'Paris' },
7  { id: 3, name: 'Charlie', age: 22, city: 'London' },
8  { id: 4, name: 'David', age: 35, city: 'New York' }
9]);
10
11// 搜索关键词
12const searchQuery = ref('');
13// 排序依据的字段
14const sortKey = ref('name'); // 默认按 name 排序
15// 排序顺序 (asc: 升序, desc: 降序)
16const sortOrder = ref('asc');
17
18// 创建一个计算属性,它会根据搜索和排序条件返回处理后的列表
19const processedUsers = computed(() => {
20  // 1. 创建一个数组的副本,避免直接修改原数据
21  let result = [...users.value];
22
23  // 2. 过滤
24  if (searchQuery.value) {
25    const query = searchQuery.value.toLowerCase();
26    result = result.filter(user => {
27      return user.name.toLowerCase().includes(query) ||
28             user.city.toLowerCase().includes(query);
29    });
30  }
31
32  // 3. 排序
33  if (sortKey.value) {
34    result.sort((a, b) => {
35      let aValue = a[sortKey.value];
36      let bValue = b[sortKey.value];
37
38      // 简单的字符串或数字比较
39      if (aValue < bValue) return sortOrder.value === 'asc' ? -1 : 1;
40      if (aValue > bValue) return sortOrder.value === 'asc' ? 1 : -1;
41      return 0;
42    });
43  }
44
45  return result;
46});
47
48// 切换排序顺序的函数
49function toggleSort(key) {
50  if (sortKey.value === key) {
51    // 如果点击的是当前排序列,则切换升降序
52    sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc';
53  } else {
54    // 如果点击的是新列,则设置为该列,并默认为升序
55    sortKey.value = key;
56    sortOrder.value = 'asc';
57  }
58}
59</script>
60
61<template>
62  <div>
63    <h2>用户列表管理</h2>
64    
65    <!-- 搜索框 -->
66    <input 
67      type="text" 
68      v-model="searchQuery" 
69      placeholder="搜索姓名或城市..."
70    >
71
72    <!-- 排序按钮 -->
73    <div class="sort-buttons">
74      <button @click="toggleSort('name')">
75        按姓名排序 {{ sortKey === 'name' ? (sortOrder === 'asc' ? '↑' : '↓') : '' }}
76      </button>
77      <button @click="toggleSort('age')">
78        按年龄排序 {{ sortKey === 'age' ? (sortOrder === 'asc' ? '↑' : '↓') : '' }}
79      </button>
80    </div>
81
82    <!-- 用户列表 -->
83    <ul>
84      <!-- v-for 只负责渲染最终处理好的数据 -->
85      <li v-for="user in processedUsers" :key="user.id">
86        {{ user.name }} ({{ user.age }}) - {{ user.city }}
87      </li>
88    </ul>
89  </div>
90</template>
91
92<style>
93/* ... 略 ... */
94</style>
95

代码剖析:

  1. 数据驱动:我们定义了三个响应式变量:users(原始数据)、searchQuery(搜索条件)、sortKeysortOrder(排序条件)。
  2. 计算属性 processedUsers:这是整个功能的核心。
    • 它首先复制了原始数据,这是一个好习惯,可以避免污染源数据。
    • 然后,它检查 searchQuery 是否有值,如果有,就使用 filter 方法进行过滤。
    • 接着,它使用 sort 方法根据 sortKeysortOrder 对结果进行排序。
    • 这个计算属性会自动缓存。只有当 users, searchQuery, sortKey, sortOrder 中任何一个发生变化时,它才会重新执行。
  3. 模板的纯粹性:模板部分非常干净。v-model="searchQuery" 将输入框和搜索词绑定。点击按钮会调用 toggleSort 函数来改变排序条件。v-for="user in processedUsers" 则只负责渲染最终的结果。
  4. 逻辑与视图分离:所有的业务逻辑(过滤、排序)都被封装在了 processedUsers 这个计算属性中。模板只负责展示和用户交互,职责清晰,易于测试和维护。

这个例子充分展示了计算属性在处理复杂列表逻辑时的强大威力。

3.4 性能优化:当列表变得巨大

当你的列表有成百上千甚至上万个条目时,即使 Vue 的 Diff 算法再高效,一次性创建和渲染这么多的 DOM 节点也会导致页面卡顿,尤其是在移动设备上。

这时,就需要引入“虚拟滚动”技术。

3.4.1 什么是虚拟滚动?

虚拟滚动的核心思想是:只渲染可视区域内的列表项,以及可视区域上下少量缓冲区的列表项

当用户滚动列表时,动态地计算哪些项应该进入可视区域,哪些应该离开,并只更新这些必要的 DOM 元素。列表中成千上万的数据在内存中,但只有几十个在页面上是真实存在的 DOM。

这极大地减少了 DOM 节点的数量,从而提升了渲染性能。

3.4.2 实现虚拟滚动

虚拟滚动的实现比较复杂,涉及到计算滚动高度、动态变换元素位置等。幸运的是,社区已经有很多成熟的库可以帮助我们。

推荐库:vue-virtual-scroller

这是一个非常流行且功能强大的虚拟滚动库。

使用步骤简介:

  1. 安装库
1npm install --save vue-virtual-scroller  
  1. 全局引入(在你的 main.js 中):
1import { createApp } from 'vue'  
2import App from './App.vue'  
3import VueVirtualScroller from 'vue-virtual-scroller'  
4import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'  
5const app = createApp(App)  
6app.use(VueVirtualScroller)  
7app.mount('#app')  
  1. 在组件中使用
1<script setup>  
2import { ref } from 'vue';  
3// 生成一个包含 10000 个项目的巨大数组  
4const hugeList = ref(Array.from({ length: 10000 }, (_, index) => ({  
5  id: index,  
6  text: `Item ${index + 1}`  
7})));  
8</script>  
9<template>  
10  <h2>虚拟滚动列表</h2>  
11  <!--  
12    使用 <RecycleScroller> 组件  
13    - items: 数据源  
14    - item-size: 每个列表项的固定高度(像素)  
15    - key-field: 指定哪个字段作为唯一标识  
16  -->  
17  <RecycleScroller  
18    class="scroller"  
19    :items="hugeList"  
20    :item-size="50"  
21    key-field="id"  
22    v-slot="{ item }"  
23  >  
24    <!--  
25      通过 v-slot="{ item }" 获取到当前渲染项的数据  
26      这里渲染的内容就是你的列表项模板  
27    -->  
28    <div class="item">  
29      {{ item.text }}  
30    </div>  
31  </RecycleScroller>  
32</template>  
33<style>  
34.scroller {  
35  height: 500px; /* 必须给容器一个固定高度 */  
36}  
37.item {  
38  height: 50px; /* 必须与 item-size 一致 */  
39  padding: 0 12px;  
40  display: flex;  
41  align-items: center;  
42  border-bottom: 1px solid #eee;  
43}  
44</style>  

代码剖析:

  • 我们用 RecycleScroller 组件替代了原生的 <ul>v-for
  • items 属性绑定我们的巨大数据源。
  • item-size 告诉组件每个列表项的高度是多少,这是计算可视区域能容纳多少项的关键。
  • key-field 告诉组件用数据对象的哪个属性作为 key
  • v-slot="{ item } 是一个作用域插槽,RecycleScroller 会把当前需要渲染的那个 item 对象传递给我们,我们用它来展示内容。

通过这种方式,即使 hugeList 有 10000 条数据,页面上也始终只渲染大约 500px / 50px = 10<div class="item"> 元素,性能得到了极大的提升。


四、总结与回顾

好了,朋友,我们一起对 Vue 3 的 v-for 指令进行了一次从入门到精通的深度探索。让我们来回顾一下这段旅程中的关键知识点。

v-for 不仅仅是一个指令,它体现了 Vue 框架“数据驱动视图”的核心思想。通过深入理解它,你不仅能掌握如何渲染列表,更能理解 Vue 响应式系统的工作方式,以及如何编写高性能、可维护的前端代码。


Vue 3 v-for 指南:从基础到精通,彻底掌握列表渲染的艺术》 是转载文章,点击查看原文


相关推荐


Python编程实战 · 基础入门篇 | 元组(tuple)
程序员爱钓鱼2025/10/26

在学习完列表(list)之后,你会发现 Python 中的序列类型非常灵活。但除了可变的列表,还有一种不可变的序列类型——元组(tuple)。 元组在数据处理、函数返回值、解包操作等场景中都非常常见,是 Python 程序员必须掌握的基础结构之一。 一 什么是元组(tuple) 元组(Tuple)是一个有序、不可变的序列类型。 它与列表类似,也可以存放多个不同类型的元素,但一旦创建,内容就不能被修改。 创建元组使用小括号 (),元素之间用逗号分隔: t = (1, 2, 3) print(t


复杂结构数据挖掘(三)关联规则挖掘实验
nju_spy2025/10/23

目录 1. 数据集导入观察 2. Apriori算法、FP-Growth算法 使用方式 3. 朴素 brute-force 4. grid_search 参数比较 + 三种算法比较 5. main 函数 与 报告生成 6. 实验结果总结 – 通过调整不同级别的支持度与置信度,比较 Apriori 算法、FP-Growth 算法以及一种进行穷举搜索的基准方法。 比较维度包括:生成的频繁项集数量、规则挖掘所用的存储空间、计算成本 – 发现一些有趣的关联规则,并就这些规则所揭


MySQL数据库05:DQL查询运算符
Doro再努力2025/10/22

文章目录 一、运算符简介 二、算术运算符 三、比较运算符 四、逻辑运算符 五、条件查询 五、位运算符(了解) 5.1按位与 5.2按位或 5.3按位异或 5.4按位右移 5.5按位左移 5.6按位取反 一、运算符简介         数据库中的表结构确立后,表中的数据代表的意义就已经确定。通过MSQL运算符进行运算,就可以获取到表结构以外的另一种数据。例如,学生表中存在一个birth字段,这个字段表示学生的出生年份,而运用MySQL的算术运算符用当前的年份减学生出


主流的 MCU 开发语言为什么是 C 而不是 C++?
淘晶驰AK2025/10/20

要说主流 MCU 开发为啥是 C 而不是 C++,你先琢磨琢磨 MCU 那点家当 —— 存储空间按 KB 算,RAM 可能就几十 KB,算力更是抠抠搜搜,稍微重点的操作都怕给它干死机。这种环境下,代码的每一个字节、每一次内存访问都得精打细算,而 C 语言天生就带着这种 "极简主义" 的基因。 你想啊,写 MCU 程序,核心就是跟寄存器打交道,直接往地址里塞数,中断服务程序得像闪电一样快,容不得半点拖泥带水。C 语言就像一把朴实的扳手,没有多余的零件,编译器一转就是干干净净的机器码,你写*(vo


jenkins在使用中遇到的问题
tingting01192025/10/19

一、jenkins使用docker命令权限拒绝 将jenkins用户添加到docker组: # usermod -aG docker jenkins # systemctl restart jenkins 二、推送私有仓库提示拒绝 # cp -r /root/.docker ./jenkins # chown -R jenkins.jenkins .docker 三、jenkins 提示kubectl 命令Authentication required # s


美团LongCat-Audio-Codec:给语音大模型装上“顺风耳”与“巧舌”
墨风如雪2025/10/18

各位AI圈的朋友们,又一个重磅消息!美团在2025年10月17日悄然揭开了它在语音AI领域深耕已久的秘密武器——LongCat-Audio-Codec的神秘面纱。这可不是一个普通的编解码器,它旨在为语音大模型(Speech LLM)打造一套全新的“听”与“说”的链路。想象一下,我们离那个能真正流畅、自然、高效对话的AI,又近了一大步! 语音AI的“死结”与LongCat的破解之道 你可能想不到,现在我们与智能设备的那些看似流畅的语音交互背后,其实隐藏着不少“死结”。语音大模型在理解语义、生成逼


AI 在数据库操作中的各类应用场景、方案与实践指南
木易 士心2025/10/16

文章目录 概述1. 数据库探索与结构分析场景说明AI 驱动的数据库探索方案 2. 智能报表生成场景说明AI 自动生成的销售分析报表 3. CRUD 操作优化场景说明AI 优化的智能 CRUD 模板 4. 查询性能优化场景说明AI 驱动的查询优化流程优化前(慢查询)AI 优化建议优化后查询 AI 推荐的索引策略 5. 复杂问题处理方案方案 1:递归查询处理层级数据方案 2:数据质量自动化检查 6. AI 辅助的数据库维护场景说明 7.


C++设计模式之行为型模式:模板方法模式(Template Method)
bkspiderx2025/10/15

模板方法模式(Template Method)是行为型设计模式的一种,它定义了一个算法的骨架,将算法的一些步骤延迟到子类中实现。这种模式允许子类在不改变算法结构的情况下,重新定义算法中的某些步骤,从而实现算法的复用与定制。 一、核心思想与角色 模板方法模式的核心是“固定流程,可变步骤”,通过在父类中定义算法的框架,将可变部分委托给子类实现。其核心角色如下: 角色名称核心职责抽象类(AbstractClass)定义算法的骨架(模板方法),包含多个抽象方法(子类必须实现的步骤)和可选的钩子方法(子


苦练Python第64天:从零掌握多线程,threading模块全面指南
倔强青铜三 VIP.1 初学乍练2025/10/14

前言 大家好,我是倔强青铜三。欢迎关注我,微信公众号:倔强青铜三。点赞、收藏、关注,一键三连! 欢迎继续 苦练Python第64天。 今天咱们把“并发”这把瑞士军刀——threading 模块,从开箱到实战一次性讲透。全程只用 Python 自带标准库,代码复制即可运行! 一、为什么需要线程? I/O 密集场景:爬虫、文件下载、日志采集,CPU 在等网络/磁盘,闲着也是闲着。 共享内存:比多进程轻量,数据不用序列化来回拷贝。 GIL?别慌:I/O 密集时线程照样提速;CPU 密集请转投 mu


局域网IP地址冲突排查与解决全指南:从诊断到预防
Bruce_xiaowei2025/10/12

局域网IP地址冲突排查与解决全指南:从诊断到预防 在局域网管理和维护中,IP地址冲突是一个常见但令人头疼的问题。当两台或多台设备被分配了相同的IP地址时,网络连接就会变得不稳定甚至中断。本文将详细介绍如何快速定位、解决并预防IP地址冲突问题。 IP地址冲突的识别与现象 典型症状表现: 设备网络连接时断时续频繁出现"网络电缆被拔出"提示Ping测试出现"一般故障"或"请求超时"特定网络服务无法访问 冲突根源分析: 手动配置IP地址时出现重复分配DHCP服务器范围设置不当网络中存在未经授权的DHCP

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0