Vue3 列表渲染

Vue3 列表渲染

  • [1. v-for 与数组(v-for 通常用于遍历数组,渲染列表)](#1. v-for 与数组(v-for 通常用于遍历数组,渲染列表))
    • [1.1 通常用法(迭代item、index)](#1.1 通常用法(迭代item、index))
    • [1.2 迭代项的解构](#1.2 迭代项的解构)
    • [1.3 多重嵌套 v-for(作用域可访问到父级)](#1.3 多重嵌套 v-for(作用域可访问到父级))
    • [1.4 v-for 中的 key(迭代项唯一标识,优化渲染性能)](#1.4 v-for 中的 key(迭代项唯一标识,优化渲染性能))
  • [2. v-for 与对象(迭代value、key、index)](#2. v-for 与对象(迭代value、key、index))
  • [3. template 上使用 v-for](#3. template 上使用 v-for)
  • [4. 组件上使用 v-for(通过props注入item和index)](#4. 组件上使用 v-for(通过props注入item和index))
  • [5. v-for 与数字(快速遍历一定次数,从1开始)](#5. v-for 与数字(快速遍历一定次数,从1开始))
  • [6. 数组变化侦测](#6. 数组变化侦测)
    • [6.1 变更方法](#6.1 变更方法)
    • [6.2 非变更方法](#6.2 非变更方法)
  • [7. 过滤或排序的一些方法 / 注意事项](#7. 过滤或排序的一些方法 / 注意事项)
    • [7.1 结合计算属性](#7.1 结合计算属性)
    • [7.2 结合方法](#7.2 结合方法)
    • [7.3 计算属性改变原始数据时,建议创建副本](#7.3 计算属性改变原始数据时,建议创建副本)

1. v-for 与数组(v-for 通常用于遍历数组,渲染列表)

1.1 通常用法(迭代item、index)

在 Vue 模板中,我们通常使用内置指令 v-for 来遍历数组,从而进行列表的渲染。

javascript 复制代码
<template>
  <div>
    <h2>商品列表</h2>
    <ul>
      <li v-for="(item, index) in items" :key="index">
        {{ index + 1 }} - {{ item.name }} - {{ item.price }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const items = ref([
  { name: '辣条', price: 3.01, id: '001' },
  { name: '泡面', price: 5.05, id: '002' },
  { name: '火腿肠', price: 1.00, id: '003' }
])
</script>

<style scoped></style>


以上代码中,items 是遍历的数组数据源,item 为迭代项的别名(别名没有强制要求,可根据情况而定),index(也可修改为其他英文,比如itemIndex,避免多重遍历时的重名) 为当前迭代项的数组下标。

1.2 迭代项的解构

当迭代项是对象时,可以进行解构操作,方便取值。比如:

javascript 复制代码
<template>
  <div>
    <h2>商品列表</h2>
    <ul>
      <li v-for="({ name, price }, index) in items" :key="index">
        {{ index + 1 }} - {{ name }} - {{ price }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const items = ref([
  { name: '辣条', price: 3.01, id: '001' },
  { name: '泡面', price: 5.05, id: '002' },
  { name: '火腿肠', price: 1.00, id: '003' }
])
</script>

<style scoped></style>


1.3 多重嵌套 v-for(作用域可访问到父级)

和函数作用域类似,多重嵌套 v-for 时,可访问到父级作用域的迭代源数组、迭代项和下标。比如:

javascript 复制代码
<template>
  <div>
    <h2>商品列表</h2>
    <div v-for="item in items">
      <div v-for="childrenItem in item.children">
        <div>{{item.id}} 货架:</div>
        <div>{{childrenItem.name}} - ¥{{ childrenItem.price }}</div>
        ---
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const items = ref([
  {
    id: '001',
    children: [
      {
        name: '商品101', price: 100,
        name: '商品102', price: 200,
      },
      {
        name: '商品111', price: 101,
        name: '商品112', price: 110,
      },
    ]
  },
  {
    id: '002',
    children: [
      {
        name: '商品201', price: 99,
        name: '商品202', price: 100,
      },
      {
        name: '商品211', price: 90,
        name: '商品212', price: 100,
      },
    ]
  },
  {
    id: '003',
    children: [
      {
        name: '商品301', price: 80,
        name: '商品302', price: 90,
      },
      {
        name: '商品311', price: 200,
        name: '商品312', price: 100,
      },
    ]
  },
])
</script>

<style scoped></style>

1.4 v-for 中的 key(迭代项唯一标识,优化渲染性能)

通常我们在使用 v-for 时,会添加一个 key,用于做每个迭代项的唯一标识,在数据发生变化时,用于加速排序,从而优化虚拟 dom 的渲染性能。

如果数据源内容不发生变化,可以使用 迭代项下标 index 作为key,这也比较省事。

但是数据源是会发生变化的,此时就 key 属性就不能使用 index,因为顺序变化(甚至整个数组都发生了改变),此时index 和之前的迭代项可能是不匹配的。

所以通常我们会使用字段的 id 或者其他确认的唯一标识作为 key,用于优化虚拟dom的渲染性能

javascript 复制代码
<template>
  <div>
    <h2>商品列表</h2>
    <ul>
      <li v-for="(item, index) in items" :key="item.id">
        {{ index + 1 }} - {{ item.name }} - {{ item.price }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const items = ref([
  { name: '辣条', price: 3.01, id: '001' },
  { name: '泡面', price: 5.05, id: '002' },
  { name: '火腿肠', price: 1.00, id: '003' }
])
</script>

<style scoped></style>

2. v-for 与对象(迭代value、key、index)

javascript 复制代码
<template>
  <div>
    <h2>书籍信息:</h2>
    <ul>
      <li v-for="(value, key, index) in myObject">
      {{ index }}. {{ key }}: {{ value }}
    </li>
    </ul>
  </div>
</template>

<script setup>
import { reactive } from 'vue'
const myObject = reactive({
  title: 'How to do lists in Vue',
  author: 'Jane Doe',
  publishedAt: '2016-04-10'
})
</script>

<style scoped></style>

迭代的最多3个参数,依次分别是 属性值value、索引key 和 索引位置index

3. template 上使用 v-for

template 上使用 v-for,可用于遍历,只会渲染内部元素,并不会渲染template 本身。比如:

javascript 复制代码
<template>
  <div>
    <h2>商品列表</h2>
    <ul>
      <template v-for="item in items">
        <li>{{ item.name }}</li>
        <li>¥{{ item.price }}</li>
        --------------------
      </template>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const items = ref([
  { name: '辣条', price: 3.01, id: '001' },
  { name: '泡面', price: 5.05, id: '002' },
  { name: '火腿肠', price: 1.00, id: '003' }
])
</script>

<style scoped></style>

4. 组件上使用 v-for(通过props注入item和index)

子组件使用 defineProps 接收 itemindex

javascript 复制代码
<template>
  <div>{{ index + 1 }}. {{ item.name }}</div>
  <div>¥{{ item.price || '--' }}</div>
  <div>-------------------------</div>
</template>

<script setup>
defineProps({
  item: Object,
  index: Number
})
</script>

<style lang="scss" scoped></style>

父组件使用 v-for 遍历子组件,通过 props 传递参数:

javascript 复制代码
<template>
  <div>
    <h2>商品列表</h2>
    <ul>
      <MyComponent v-for="(item, index) in items" :item="item" :index="index" :key="item.id" />
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import MyComponent from '@/components/MyComponent.vue';
const items = ref([
  { name: '辣条', price: 3.01, id: '001' },
  { name: '泡面', price: 5.05, id: '002' },
  { name: '火腿肠', price: 1.00, id: '003' }
])
</script>

<style scoped></style>

之所以要通过 props 将迭代数据传入,而非自动将 item 注入子组件的原因是,子组件的数据来源可能并非 item,可能是其中的一个字段,甚至是其他数据项。

5. v-for 与数字(快速遍历一定次数,从1开始)

有时候我们只是为了单纯地遍历一定的次数,无需数组和对象,也可以使用数字进行v-for遍历,比如:

javascript 复制代码
<template>
  <div>
    <span v-for="n in 10">{{ n }}</span>
  </div>
</template>

<script setup>
</script>

<style scoped></style>

6. 数组变化侦测

数组变化侦测通常分为两大类:变更方法和非变更方法(替换整个数组)。

6.1 变更方法

数组变更方法(调用这些方法时会对原来的数组进行变更):

  • push
  • pop
  • shift
  • unshift
  • splice
  • sort
  • reverse

针对变更方法,数组只要一更新,就会触发它的响应式,页面会重新渲染

javascript 复制代码
setTimeout(() => {
  projects.value.push({
    id: 3,
    name: '大项目',
    tasks: [
      {
        id: 1,
        name: '搭建工程',
        subtasks: ['调研框架', '熟悉框架']
      },
      {
        id: 2,
        name: '分解模块',
        subtasks: ['调研', '分析']
      }
    ]
  })
}, 3000)

6.2 非变更方法

非变更方法(调用这些方法不会对原来的数组进行变更,而是会返回一个新的数组):

  • filter
  • concat
  • slice
  • map

如果是非变更方法,那么需要使用方法的返回值去替换原来的值:

javascript 复制代码
// `items` 是一个数组的 ref
items.value = items.value.filter((item) => item.message.match(/Foo/))

你可能认为这将导致 Vue 丢弃现有的 DOM 并重新渲染整个列表------幸运的是,情况并非如此。Vue 实现了一些巧妙的方法来最大化对 DOM 元素的重用,因此用另一个包含部分重叠对象的数组来做替换,仍会是一种非常高效的操作

7. 过滤或排序的一些方法 / 注意事项

7.1 结合计算属性

如果希望显示数组经过过滤或排序后的内容,而不实际变更或重置原始数据。可以创建对应的计算属性。比如:

javascript 复制代码
<template>
  <div>
    <li v-for="n in evenNumbers">{{ n }}</li>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
const numbers = ref([1, 2, 3, 4, 5])

const evenNumbers = computed(() => {
  return numbers.value.filter((n) => n % 2 === 0)
})
</script>

<style scoped></style>

7.2 结合方法

在计算属性不可行的情况下 (例如在多层嵌套的 v-for 循环中),你可以使用方法对迭代项数据进行二次处理:

javascript 复制代码
<template>
  <div>
    <ul v-for="numbers in sets">
      <li v-for="n in even(numbers)">{{ n }}</li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const sets = ref([
  [1, 2, 3, 4, 5],
  [6, 7, 8, 9, 10]
])

function even(numbers) {
  return numbers.filter((number) => number % 2 === 0)
}
</script>

<style scoped></style>

7.3 计算属性改变原始数据时,建议创建副本

在计算属性中使用 reverse() 和 sort() 的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。

javascript 复制代码
<template>
  <div>
    <li v-for="n in reverseNumbers">{{ n }}</li>
  </div>
  <div>-------------------------</div>
  <div>
    <li v-for="n in numbers">{{ n }}</li>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
const numbers = ref([1, 2, 3, 4, 5])

const reverseNumbers = computed(() => {
  return numbers.value.reverse()
})
</script>

<style scoped></style>

请在调用这些方法之前创建一个原数组的副本:

javascript 复制代码
return [...numbers.value].reverse()
javascript 复制代码
<template>
  <div>
    <li v-for="n in reverseNumbers">{{ n }}</li>
  </div>
  <div>-------------------------</div>
  <div>
    <li v-for="n in numbers">{{ n }}</li>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
const numbers = ref([1, 2, 3, 4, 5])

const reverseNumbers = computed(() => {
  return [...numbers.value].reverse()
})
</script>

<style scoped></style>

上一章 《Vue3 条件渲染

下一章 《Vue3 事件处理

相关推荐
北极星日淘1 小时前
可买免税店货物与安耐晒——特殊商品代购技术方案
javascript·vue.js·elementui
youyu-youyu2 小时前
oss阿里云图片链接url高清图片设置为缩略图 vue 减少加载体积流量
前端·javascript·vue.js·阿里云·云计算
低保和光头哪个先来2 小时前
聊聊 CSS 编译和 scoped 实现
前端·css·vue.js
_codeOH17 小时前
Vue 3 vs React 19:框架还在卷,核心原理就这些
前端·vue.js
英勇无比的消炎药18 小时前
新手必看玩转TinyRobot一定要避开这些坑
前端·vue.js
英勇无比的消炎药18 小时前
别再盲目混用AI组件库和传统组件库差距原来这么大
前端·vue.js
英勇无比的消炎药20 小时前
前端提效神器全新AI组件库TinyRobot改写日常开发模式
前端·vue.js
英勇无比的消炎药20 小时前
前端提效神器TinyRobot
前端·vue.js
CDwenhuohuo21 小时前
uni 背景色渐变 全屏
前端·javascript·vue.js
爱怪笑的小杰杰21 小时前
Vue 项目交付第三方开发,如何隐藏核心 JS 源码?
前端·javascript·vue.js