Vue 3的`v-if`与`v-show`:如何根据场景进行选择?

Vue 3 的 v-ifv-show: 场景化选择策略

大家好,今天我们来深入探讨 Vue 3 中 v-ifv-show 这两个条件渲染指令,以及如何在实际开发中根据不同场景做出最佳选择。理解它们的差异不仅仅是掌握两个指令的用法,更是提升代码性能和用户体验的关键。

1. 核心区别:渲染方式的不同

v-ifv-show 最根本的区别在于它们控制元素显示的方式。

  • v-if:条件渲染。 当条件为真时,元素及其包含的子元素才会被渲染到 DOM 中;当条件为假时,元素及其子元素会被完全移除出 DOM。这意味着 v-if 具有“真正的”条件渲染特性。

  • v-show:CSS display 控制。 无论条件真假,元素及其子元素都会被渲染到 DOM 中。 v-show 只是通过切换元素的 CSS display 属性来控制元素的可见性 (display: none 隐藏,display: block 或其他原始 display 值显示)。

可以用一个简单的表格来概括:

特性 v-if v-show
渲染机制 条件为真时渲染,否则移除 DOM 始终渲染到 DOM,通过 CSS display 控制可见性
初始渲染代价 条件为假时初始渲染代价较低 初始渲染代价较高,因为始终渲染
切换代价 条件切换时有较高的切换代价 (销毁和创建) 条件切换时切换代价较低 (CSS 属性切换)
适用场景 很少改变条件,或初始渲染代价很关键的场景 频繁改变条件的场景

2. 代码示例:直观展示差异

让我们通过一些代码示例来更直观地理解这两种指令的差异。

<template>
  <div>
    <button @click="toggleIf">Toggle v-if</button>
    <button @click="toggleShow">Toggle v-show</button>

    <div v-if="isIfVisible">
      v-if: This element is conditionally rendered.
    </div>

    <div v-show="isShowVisible">
      v-show: This element's visibility is controlled by CSS display.
    </div>
  </div>
</template>

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

export default {
  setup() {
    const isIfVisible = ref(true);
    const isShowVisible = ref(true);

    const toggleIf = () => {
      isIfVisible.value = !isIfVisible.value;
    };

    const toggleShow = () => {
      isShowVisible.value = !isShowVisible.value;
    };

    return {
      isIfVisible,
      isShowVisible,
      toggleIf,
      toggleShow,
    };
  },
};
</script>

在这个例子中,我们有两个按钮分别控制 v-ifv-show 指令绑定的变量。 当点击 "Toggle v-if" 按钮时,isIfVisible 的值会改变,导致 v-if 控制的 div 元素被添加到 DOM 或从 DOM 中移除。 而当点击 "Toggle v-show" 按钮时,isShowVisible 的值会改变,导致 v-show 控制的 div 元素的 display 属性在 blocknone 之间切换。

你可以打开浏览器的开发者工具,观察元素的变化。 你会发现 v-if 控制的元素在条件为 false 时完全不存在于 DOM 树中,而 v-show 控制的元素始终存在,只是 display 属性发生了变化。

3. 性能考量:切换频率的重要性

v-if 的优势在于初始渲染时,如果条件为假,则不会渲染元素,从而减少了初始渲染的负担。 但是,当条件发生变化时,v-if 需要重新创建或销毁元素,这会带来一定的性能开销。

v-show 的优势在于切换可见性时,只需要改变 CSS 属性,性能开销很小。 但是,无论条件真假,元素都会被渲染到 DOM 中,这会增加初始渲染的负担。

因此,选择 v-if 还是 v-show 的关键在于条件切换的频率

  • 如果条件很少改变,或者初始渲染性能至关重要,那么应该选择 v-if。 例如,一个只在用户登录后才显示的组件,用户登录状态很少改变,此时使用 v-if 可以避免在用户未登录时渲染该组件,从而提高初始渲染速度。

  • 如果条件频繁改变,那么应该选择 v-show。 例如,一个模态框,用户可能会频繁地打开和关闭它,此时使用 v-show 可以避免频繁地创建和销毁模态框,从而提高性能。

4. 高级用法:v-elsev-else-if

v-if 还可以与 v-elsev-else-if 结合使用,创建更复杂的条件渲染逻辑。 这些指令必须紧跟在 v-ifv-else-if 之后。

<template>
  <div>
    <button @click="incrementType">Increment Type</button>

    <div v-if="type === 'A'">
      Type A: This is the first type.
    </div>
    <div v-else-if="type === 'B'">
      Type B: This is the second type.
    </div>
    <div v-else>
      Type C: This is the default type.
    </div>
  </div>
</template>

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

export default {
  setup() {
    const type = ref('A');

    const incrementType = () => {
      if (type.value === 'A') {
        type.value = 'B';
      } else if (type.value === 'B') {
        type.value = 'C';
      } else {
        type.value = 'A';
      }
    };

    return {
      type,
      incrementType,
    };
  },
};
</script>

在这个例子中,我们使用 v-ifv-else-ifv-else 来根据 type 变量的值显示不同的内容。 当 type 的值为 ‘A’ 时,显示 "Type A";当 type 的值为 ‘B’ 时,显示 "Type B";否则,显示 "Type C"。

5. 结合 template 标签使用 v-if

有时候,我们可能需要根据条件渲染多个元素,而不想在外面包裹一个额外的 DOM 元素。 这时,我们可以使用 <template> 标签结合 v-if 指令。 <template> 标签本身不会被渲染到 DOM 中,它只是一个占位符,用于包裹需要条件渲染的元素。

<template>
  <div>
    <button @click="toggleList">Toggle List</button>

    <template v-if="showList">
      <p>Item 1</p>
      <p>Item 2</p>
      <p>Item 3</p>
    </template>
  </div>
</template>

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

export default {
  setup() {
    const showList = ref(true);

    const toggleList = () => {
      showList.value = !showList.value;
    };

    return {
      showList,
      toggleList,
    };
  },
};
</script>

在这个例子中,我们使用 <template v-if="showList"> 包裹了三个 <p> 元素。 当 showList 的值为 true 时,这三个 <p> 元素会被渲染到 DOM 中;当 showList 的值为 false 时,这三个 <p> 元素会被从 DOM 中移除。 注意,<template> 标签本身不会被渲染到 DOM 中。

6. 避免过度使用:控制组件复杂度

虽然 v-ifv-show 非常有用,但过度使用它们可能会导致组件变得复杂难以维护。 如果一个组件中包含大量的条件渲染逻辑,那么可以考虑将组件拆分成更小的、更专注的子组件,每个子组件负责处理自己的条件渲染逻辑。 这样做可以提高代码的可读性和可维护性。

7. 最佳实践:明确你的选择标准

在选择 v-if 还是 v-show 时,应该明确你的选择标准。 以下是一些可以参考的标准:

  • 初始渲染性能:如果初始渲染性能至关重要,那么应该优先考虑 v-if
  • 切换频率:如果条件频繁改变,那么应该优先考虑 v-show
  • 代码可读性:选择能够使代码更易于理解和维护的指令。
  • 组件复杂度:避免过度使用条件渲染,尽量保持组件的简单性。

8. v-ifv-for 的优先级问题

v-ifv-for 同时存在于一个元素上时,v-for 的优先级高于 v-if。这意味着 Vue 会先执行循环,然后再对循环生成的每个元素执行条件判断。

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

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

export default {
  setup() {
    const items = ref([
      { id: 1, name: 'Item A', isActive: true },
      { id: 2, name: 'Item B', isActive: false },
      { id: 3, name: 'Item C', isActive: true },
    ]);

    return {
      items,
    };
  },
};
</script>

在这个例子中,Vue 会先循环 items 数组,然后对每个 item 判断 item.isActive 的值,如果为 true,则渲染 <li> 元素,否则不渲染。

这种写法存在一定的性能问题,因为即使 item.isActivefalse,Vue 仍然会创建 <li> 元素,然后再将其移除。 为了解决这个问题,可以使用 computed 属性来过滤数据,然后再使用 v-for 循环过滤后的数据。

<template>
  <ul>
    <li
      v-for="item in activeItems"
      :key="item.id"
    >
      {{ item.name }}
    </li>
  </ul>
</template>

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

export default {
  setup() {
    const items = ref([
      { id: 1, name: 'Item A', isActive: true },
      { id: 2, name: 'Item B', isActive: false },
      { id: 3, name: 'Item C', isActive: true },
    ]);

    const activeItems = computed(() => {
      return items.value.filter(item => item.isActive);
    });

    return {
      activeItems,
    };
  },
};
</script>

在这个例子中,我们使用 computed 属性 activeItems 来过滤 items 数组,只保留 isActivetrue 的元素。 然后,我们使用 v-for 循环 activeItems 数组,这样可以避免创建不必要的 <li> 元素,从而提高性能。

9. 条件渲染与 Key 属性

在使用 v-ifv-for 时,一定要注意 key 属性的使用。 key 属性用于 Vue 识别虚拟 DOM 节点,从而更高效地更新 DOM。 当使用 v-if 切换元素时,如果元素没有 key 属性,Vue 可能会复用之前的元素,导致出现意想不到的问题。

例如,以下代码:

<template>
  <div>
    <button @click="toggleInput">Toggle Input</button>

    <input v-if="showInput" type="text">
  </div>
</template>

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

export default {
  setup() {
    const showInput = ref(true);

    const toggleInput = () => {
      showInput.value = !showInput.value;
    };

    return {
      showInput,
      toggleInput,
    };
  },
};
</script>

在这个例子中,当点击 "Toggle Input" 按钮时,showInput 的值会改变,导致 v-if 控制的 <input> 元素被添加到 DOM 或从 DOM 中移除。 由于 <input> 元素没有 key 属性,Vue 可能会复用之前的 <input> 元素,导致输入框中的内容被保留。

为了避免这个问题,可以给 <input> 元素添加一个 key 属性:

<template>
  <div>
    <button @click="toggleInput">Toggle Input</button>

    <input v-if="showInput" type="text" key="myInput">
  </div>
</template>

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

export default {
  setup() {
    const showInput = ref(true);

    const toggleInput = () => {
      showInput.value = !showInput.value;
    };

    return {
      showInput,
      toggleInput,
    };
  },
};
</script>

添加 key 属性后,Vue 就会正确地创建和销毁 <input> 元素,避免出现复用问题。 key 的值应该是唯一的,可以是元素的 idindex 或其他唯一标识符。

10. 总结

v-ifv-show 是 Vue 3 中常用的条件渲染指令。 理解它们的差异,并根据实际场景选择合适的指令,可以有效提高代码的性能和可维护性。 记住,选择的关键在于条件切换的频率,以及初始渲染性能的需求。

核心要点回顾

  • v-if 进行真正的条件渲染,条件为假时元素不会渲染到 DOM 中。
  • v-show 通过 CSS display 属性控制元素的可见性,元素始终存在于 DOM 中。
  • 根据条件切换频率和初始渲染性能需求选择合适的指令。
  • 注意 v-ifv-for 的优先级问题,以及 key 属性的使用。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注