自定义指令-优化v-if和v-show上的使用

v-show-if

背景

像我们在开发vue过程,有时候会遇到这样的问题,在初始化渲染时候,并不希望某个组件渲染,当需要时候再渲染,但不需要又不希望DOM把被删除,导致下次需要时候重新渲染DOM节点。即是希望拥有v-if初始化下不渲染的能力,也要v-show在渲染后不被销毁的能力

使用场景

像很多时候,在使用组件库的开发上,使用tabs,我们就很容易写出如下的代码:

xml 复制代码
<template>
  <a-tabs v-model:activeKey="activeKey">
    <a-tab-pane key="1" tab="Tab 1">Content of Tab Pane 1</a-tab-pane>
    <a-tab-pane key="2" tab="Tab 2" force-render>Content of Tab Pane 2</a-tab-pane>
    <a-tab-pane key="3" tab="Tab 3">Content of Tab Pane 3</a-tab-pane>
  </a-tabs>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const activeKey = ref('1');
</script>

在tabs下的,每一个pane都是已经被渲染出来的,但有时候我们并不是很希望渲染这部分的,这是我们会采用 v-if 去防止第一个渲染,这时候就会得到如下的代码:

xml 复制代码
<template>
  <a-tabs v-model:activeKey="activeKey">
    <a-tab-pane v-if="activeKey === '1'" key="1" tab="Tab 1">Content of Tab Pane 1</a-tab-pane>
    <a-tab-pane v-if="activeKey === '2'" key="2" tab="Tab 2" force-render>Content of Tab Pane 2</a-tab-pane>
    <a-tab-pane v-if="activeKey === '3'" key="3" tab="Tab 3">Content of Tab Pane 3</a-tab-pane>
  </a-tabs>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const activeKey = ref('1');
</script>

但在这种情况下,我们就会发现,在v-if切换时候,DOM节点被删除了,会造成一定的性能。因此,我们希望有个指令能实现首次不渲染,需要时候渲染,并在下次为falsy时候不清除DOM节点

xml 复制代码
<template>
  <a-tabs v-model:activeKey="activeKey">
    <a-tab-pane v-show-if="activeKey === '1'" key="1" tab="Tab 1">Content of Tab Pane 1</a-tab-pane>
    <a-tab-pane v-show-if="activeKey === '2'" key="2" tab="Tab 2" force-render>Content of Tab Pane 2</a-tab-pane>
    <a-tab-pane v-show-if="activeKey === '3'" key="3" tab="Tab 3">Content of Tab Pane 3</a-tab-pane>
  </a-tabs>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const activeKey = ref('1');
</script>

设计

1、挂载时候判断是否挂载注释节点,还是渲染节点

2、在下一次渲染时候触发修改

相关代码实现

ini 复制代码
export default {
  mounted(el, binding, vnode) {
    // 初始值为空情况
    if (!binding.value) {
      // 创建一个v-show-if的注释节点
      el.__showIfComment = document.createComment(' v-show-if ');
      // 初始为 false,用注释节点替代
      vnode.el = el.__showIfComment;
      el.__showIfInitialHidden = true;
      replaceNode(el, el.__showIfComment);
    }
    // 初始值非空情况
    if (binding.value) {
      el.style.display = '';
    }
  },
  updated(el, binding, vnode) {
    if (binding.value === binding.oldValue) return;
​
    if (binding.value) {
      // 从 false 变为 true
      if (el.__showIfInitialHidden) {
        vnode.el = el;
        replaceNode(el.__showIfComment, el);
        el.__showIfInitialHidden = false;
      }
      el.style.display = '';
    } else {
      // 从 true 变为 false
      el.style.display = 'none';
    }
  },
  beforeUnmount(el) {
    // 清理注释节点
    if (el.__showIfComment && el.__showIfComment.parentNode) {
      el.__showIfComment.parentNode.removeChild(el.__showIfComment);
    }
  },
};
​
function replaceNode(oldNode, newNode) {
  if (oldNode.parentNode) {
    oldNode.parentNode.replaceChild(newNode, oldNode);
  }
}

总结

在项目开发过程,难免会遇到上诉描述的问题。本指令v-show-if因此被设计出来,但在使用上还是会存在一定的局限性的,还是需要和业务系统绑定。

相关推荐
东风破_6 小时前
danci 项目(三):从 JSON 数据到 AI Coding,真实项目里的数据清洗、Prompt 和工程规范
前端·后端·node.js
东风破_6 小时前
danci 项目(一):从需求到架构,一个单词学习系统为什么会这样设计
前端·后端·node.js
蒲公英eric7 小时前
从客户端到服务端:DVWA DOM 型 XSS 模块完整漏洞分析教程
前端·web安全·ai·xss·dvwa·ai安全
单线程_017 小时前
从案例分析 Vue3 Tokenizer+Parser 源码三
前端·javascript·vue.js
顶点多余7 小时前
那些在算法中适合巩固的知识点---1
java·前端·算法
Setsuna_F_Seiei8 小时前
前端转型 Agent 开发 03 之 Agent Tools - 给 Agent 装上手脚
前端·agent·ai编程
jay神8 小时前
【计算机毕业设计】基于SpringBoot的程序教学辅助系统
java·前端·vue.js·spring boot·后端·毕业设计·课程设计
小磊哥er9 小时前
深入解构Claude Code - 第 8 篇 · 数据放哪、钱怎么算
javascript·ai编程
kyriewen10 小时前
我装了30多个Skill,给AI安排了8个岗位
前端·javascript·ai编程
风骏时光牛马11 小时前
大模型落地实践中的技术选型思路与方案对比
前端