← 返回主页

第4课: 条件渲染与列表渲染

v-if条件渲染

v-if指令用于条件性地渲染元素,条件为真时元素才会被渲染。

<template>
  <div>
    <h1 v-if="awesome">Vue is awesome!</h1>
    <h1 v-else>Oh no</h1>

    <div v-if="type === 'A'">A</div>
    <div v-else-if="type === 'B'">B</div>
    <div v-else-if="type === 'C'">C</div>
    <div v-else>Not A/B/C</div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const awesome = ref(true)
const type = ref('A')
</script>

template上的v-if

<template>
  <div>
    <template v-if="ok">
      <h1>标题</h1>
      <p>段落1</p>
      <p>段落2</p>
    </template>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const ok = ref(true)
</script>

v-show

v-show通过CSS的display属性来控制元素的显示和隐藏。

<template>
  <div>
    <h1 v-show="isVisible">Hello!</h1>
    <button @click="isVisible = !isVisible">切换显示</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const isVisible = ref(true)
</script>
v-if vs v-show:
- v-if:真正的条件渲染,切换时元素会被销毁和重建
- v-show:只是切换CSS的display属性,元素始终存在
- v-if有更高的切换开销,v-show有更高的初始渲染开销
- 频繁切换使用v-show,运行时条件很少改变使用v-if

v-for列表渲染

v-for指令用于基于数组或对象渲染列表。

遍历数组

<template>
  <div>
    <ul>
      <li v-for="item in items" :key="item.id">
        {{ item.text }}
      </li>
    </ul>

    <!-- 带索引 -->
    <ul>
      <li v-for="(item, index) in items" :key="item.id">
        {{ index }} - {{ item.text }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const items = ref([
  { id: 1, text: '学习Vue3' },
  { id: 2, text: '学习组件' },
  { id: 3, text: '学习路由' }
])
</script>

遍历对象

<template>
  <div>
    <ul>
      <li v-for="value in user" :key="value">
        {{ value }}
      </li>
    </ul>

    <!-- 带键名 -->
    <ul>
      <li v-for="(value, key) in user" :key="key">
        {{ key }}: {{ value }}
      </li>
    </ul>

    <!-- 带键名和索引 -->
    <ul>
      <li v-for="(value, key, index) in user" :key="key">
        {{ index }}. {{ key }}: {{ value }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { reactive } from 'vue'

const user = reactive({
  name: '张三',
  age: 25,
  city: '北京'
})
</script>

遍历数字范围

<template>
  <div>
    <span v-for="n in 10" :key="n">{{ n }} </span>
  </div>
</template>

key属性的重要性

<template>
  <div>
    <!-- 不推荐:使用索引作为key -->
    <div v-for="(item, index) in items" :key="index">
      {{ item.name }}
    </div>

    <!-- 推荐:使用唯一ID作为key -->
    <div v-for="item in items" :key="item.id">
      {{ item.name }}
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const items = ref([
  { id: 1, name: 'Item 1' },
  { id: 2, name: 'Item 2' },
  { id: 3, name: 'Item 3' }
])
</script>
为什么需要key?
key帮助Vue识别哪些元素改变了,从而高效地更新虚拟DOM。使用唯一且稳定的值作为key,避免使用索引。

数组变更方法

<template>
  <div>
    <ul>
      <li v-for="item in items" :key="item.id">{{ item.text }}</li>
    </ul>
    <button @click="addItem">添加</button>
    <button @click="removeItem">删除</button>
    <button @click="reverseItems">反转</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const items = ref([
  { id: 1, text: '项目1' },
  { id: 2, text: '项目2' }
])

let nextId = 3

function addItem() {
  items.value.push({ id: nextId++, text: `项目${nextId - 1}` })
}

function removeItem() {
  items.value.pop()
}

function reverseItems() {
  items.value.reverse()
}
</script>

v-for与v-if结合

<template>
  <div>
    <!-- 不推荐:v-if和v-for在同一元素上 -->
    <li v-for="user in users" v-if="user.isActive" :key="user.id">
      {{ user.name }}
    </li>

    <!-- 推荐:使用计算属性过滤 -->
    <li v-for="user in activeUsers" :key="user.id">
      {{ user.name }}
    </li>

    <!-- 或使用template包裹 -->
    <template v-for="user in users" :key="user.id">
      <li v-if="user.isActive">{{ user.name }}</li>
    </template>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const users = ref([
  { id: 1, name: '张三', isActive: true },
  { id: 2, name: '李四', isActive: false },
  { id: 3, name: '王五', isActive: true }
])

const activeUsers = computed(() => {
  return users.value.filter(user => user.isActive)
})
</script>

实战示例:待办事项列表

<template>
  <div>
    <input v-model="newTodo" @keyup.enter="addTodo" placeholder="添加待办事项">
    <button @click="addTodo">添加</button>

    <ul>
      <li v-for="todo in todos" :key="todo.id">
        <input type="checkbox" v-model="todo.done">
        <span :style="{ textDecoration: todo.done ? 'line-through' : 'none' }">
          {{ todo.text }}
        </span>
        <button @click="removeTodo(todo.id)">删除</button>
      </li>
    </ul>

    <p v-if="todos.length === 0">暂无待办事项</p>
    <p v-else>
      已完成: {{ completedCount }} / {{ todos.length }}
    </p>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const newTodo = ref('')
const todos = ref([])
let nextId = 1

function addTodo() {
  if (newTodo.value.trim()) {
    todos.value.push({
      id: nextId++,
      text: newTodo.value,
      done: false
    })
    newTodo.value = ''
  }
}

function removeTodo(id) {
  todos.value = todos.value.filter(todo => todo.id !== id)
}

const completedCount = computed(() => {
  return todos.value.filter(todo => todo.done).length
})
</script>

练习

  1. 创建一个用户列表,根据用户状态显示不同的样式
  2. 实现一个商品列表,支持添加、删除和筛选功能
  3. 创建一个表格,使用v-for渲染行和列
  4. 实现一个标签页切换功能,使用v-if或v-show