当前位置: 首页 > article >正文

vue3学习记录-自定义指令

vue3学习记录-自定义指令

  • 1.指令钩子
    • 1.1 局部注册
    • 1.2 全局注册
  • 2.简化形式
  • 3.对象字面量
  • 4.在组件上使用
  • 5.实例
    • 5.1 权限控制指令
    • 5.2 图片懒加载指令
    • 5.3 点击外部关闭指令
    • 5.4 复制文本指令
    • 5.5 防抖指令

1.指令钩子

vue3的自定义指令

1.1 局部注册

<script setup>
import A from './components/A.vue';
import B from './components/B.vue';
import CustomForm from './components/CustomForm.vue';
import { ref } from 'vue';
const flag = ref(true);
const vShowtext = {
//必须以 vNameOfDirective 的形式来命名本地自定义指令,以使得它们可以直接在模板中使用。
  created(el, binding, vnode) {
    console.log('Created', el, binding, vnode)
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode) {
    console.log('BeforeMount', el, binding, vnode)
  },
  // 在绑定元素的父组件

  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode) {
    console.log('Mounted', el, binding, vnode)
  },
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {
    console.log('BeforeUpdate', el, binding, vnode, prevVnode)
  },
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {
    console.log('Updated', el, binding, vnode, prevVnode)
  },
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode) {
    console.log('BeforeUnmount', el, binding, vnode)
  },
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode) {
    console.log('Unmounted', el, binding, vnode)
  }
}
</script>
<template>
  <div class="container">
    <el-button @click="flag=!flag">change</el-button>
    <div v-showtext:aaa.b.c="flag" class="box">自定义指令</div>
  </div>
</template>

<style scoped>
.container {
  width: 300px;
  height: 200px;
  margin: 0 auto;
  margin-bottom: 8px;
  .box{
    background-color: aquamarine;
    height: 100%;
  }
}
</style>

1.2 全局注册

const app = createApp(App)
app.directive('demo',{
    mounted(el,binding){
        console.log('全局注册的组件',el,binding)
    }
})

2.简化形式

对于自定义指令来说,一个很常见的情况是仅仅需要在 mounted 和 updated 上实现相同的行为,除此之外并不需要其他钩子。这种情况下我们可以直接用一个函数来定义指令,如下所示:

<div v-color="color"></div>
app.directive('color', (el, binding) => {
  // 这会在 `mounted` 和 `updated` 时都调用
  el.style.color = binding.value
})

3.对象字面量

如果你的指令需要多个值,你可以向它传递一个 JavaScript 对象字面量

<div v-showtext:aaa.b.c="{'background-color':'red',fontSize:'16px',flag}" class="box">自定义指令</div>

在这里插入图片描述
通过binding.value.属性名 可以访问到对应的属性值

4.在组件上使用

不推荐在组件上使用自定义指令。为什么呢。
1.组件生命周期与指令钩子的执行时机不一致

<script setup>
<script setup>
import A from './components/A.vue';
import B from './components/B.vue';
import CustomForm from './components/CustomForm.vue';
import { ref } from 'vue';
const flag = ref(true);
const vFocus = {
  created(el, binding, vnode) {
    console.log('Created', el, binding, vnode)
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode) {
    console.log('BeforeMount', el, binding, vnode)
  },
  // 在绑定元素的父组件

  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode) {
    console.log('Mounted', el, binding.value, vnode)
    el.style.color = binding.value['background-color']
    el.focus();
  },
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {
    console.log('BeforeUpdate', el, binding, vnode, prevVnode)
  },
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {
    console.log('Updated', el, binding, vnode, prevVnode)
  },
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode) {
    console.log('BeforeUnmount', el, binding, vnode)
  },
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode) {
    console.log('Unmounted', el, binding, vnode)
  }
}
</script>
<template>
  <div class="container">
    <el-button @click="flag=!flag">change</el-button>
    <A v-focus:aaa.b.c="{'background-color':'red',fontSize:'16px',flag}" class="box">自定义指令</A>
  </div>
</template>

<style scoped>
.container {
  width: 300px;
  height: 200px;
  margin: 0 auto;
  margin-bottom: 8px;
  .box{
    background-color: aquamarine;
    height: 100%;
  }
}
</style>

//A组件
<script setup>
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
  console.log('组件 mounted')
})

onUnmounted(() => {
  console.log('组件 unmounted')
})
</script>

<template>
  <div>
    a组件
    <header>头部</header>
    <main>主要内容</main>
    <input type="text">
  </div>
</template>

在这里插入图片描述
可以看到是父组件的自定义指令钩子函数执行完了才会去执行子组件的生命周期
2.组件可能有多个根节点,导致指令行为不明确
可以根据我的自定义指令v-focus的意思就是聚焦嘛,但是可以看到自定义的mounted钩子拿到的el是整个子组件div的,不清楚指令应该作用于哪个根节点。虽然子组件的dom div都拿到了,用js也能把其中的input标签自动聚焦,但是完全没必要吧。还不如把自定义指令写在子组件的input上呢。
但凡子组件不是很特殊的,建议,最好不要在组件上使用自定义指令

5.实例

5.1 权限控制指令

app.directive('permission', (el, binding) => {
    const { value } = binding
    //const userRoles = getCurrentUserRoles() // 获取当前用户角色的方法
    const userRoles = ['admin']  //模拟数据
    if (value && value instanceof Array && value.length > 0) {
        const hasPermission = userRoles.some(role => value.includes(role))
        if (!hasPermission) {
            el.parentNode && el.parentNode.removeChild(el)
        }
    } else {
        throw new Error(`需要指定权限,例如 v-permission="['admin','editor']"`)
    }
}
)

<template>
  <div class="container">
    <button v-permission="['admin']">管理员按钮</button>
    <button v-permission="['editor']">编辑者按钮</button>
  </div>
</template>

结果:
在这里插入图片描述

5.2 图片懒加载指令

app.directive('lazyload', {
    created(el, binding) {
        el.setAttribute('data-src', binding.value)
    },
    mounted(el) {
        const observer = new IntersectionObserver(entries => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    const img = entry.target
                    const dataSrc = img.getAttribute('data-src')
                    if (dataSrc) {
                        img.src = dataSrc
                        observer.unobserve(img)
                    }
                }
            })
        })
        observer.observe(el)
    }
})
<template>
  <div class="container">
    <img v-lazyload="'https://picsum.photos/200/300'" class="lazy-image" alt="Lazy loaded image">
    <img 
      v-for="id in 20" 
      :key="id"
      v-lazyload="`https://picsum.photos/200/300?random=${id}`"
      alt="随机图片"
      class="lazy-image"
    >
  </div>
</template>

<style scoped>
.container {
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  .lazy-image {
    width: 100%;
    height: 300px;
    object-fit: cover;
    background-color: #f5f5f5;
    /* 加载前的背景色 */
    transition: opacity 0.3s;
  }
}
</style>

效果:
在这里插入图片描述

5.3 点击外部关闭指令

app.directive('clickOutside', {
    mounted(el, binding) {
        el._clickOutside = (event) => {
            if (!(el === event.target || el.contains(event.target))) {
                binding.value(event)
            }
        }
        document.addEventListener('click', el._clickOutside)
    },
    unmounted(el) {
        document.removeEventListener('click', el._clickOutside)
    }
})

<template>
  <div class="container">
    <div v-click-outside="closeDropdown" class="dropdown">
      拉菜单内容
    </div>
  </div>
</template>

<style scoped>
.container {
  width: 300px;
  height: 200px;
  margin: 0 auto;
  margin-bottom: 8px;

  .dropdown {
    height: 100px;
    background-color: aqua;

  }
}
</style>

5.4 复制文本指令

app.directive('copy', {
    mounted(el, binding) {
        el._copyHandler = () => {
            const value = binding.value
            const input = document.createElement('input')
            input.value = value
            document.body.appendChild(input)
            input.select()
            document.execCommand('copy')
            document.body.removeChild(input)

            // 可以添加复制成功的提示
            alert('复制成功!')
        }
        el.addEventListener('click', el._copyHandler)
    },
    unmounted(el) {
        el.removeEventListener('click', el._copyHandler)
    }
})

<script setup>
const textToCopy = '要复制的文本';
</script>
<template>
  <button v-copy="textToCopy">点击复制文本</button>
</template>

5.5 防抖指令

app.directive('debounce', {
    mounted(el, binding) {
        const { value, arg = 300 } = binding // arg 用于设置延迟时间
        let timer = null
        el.addEventListener('click', () => {
            if (timer) {
                clearTimeout(timer)
            }
            timer = setTimeout(() => {
                value()
            }, Number(arg))
        })
    }
})
<script setup>
const handleClick = () => {
  console.log('按钮被点击')
}
</script>
<template>
<button v-debounce:500="handleClick">防抖按钮</button>
</template>

http://www.kler.cn/news/363343.html

相关文章:

  • SL3160 dcdc150V降压5.1V/1A 车载GPS定位器供电芯片
  • 机器视觉相机自动对焦算法
  • DevEco Studio的使用 习题答案 HarmonyOS第一课
  • WRB Hidden Gap,WRB隐藏缺口,MetaTrader 免费公式!(指标教程)
  • Redis的6.0以上为啥又支持多线程
  • docker容器无法连接宿主机mysql排查
  • Python3入门--数据类型
  • 国内常见的 AI 工具,你都用过几个?
  • 【Android】自定义EditText
  • 交换基础简述
  • hive数据库,表操作
  • git 克隆并切换分支
  • 第九天 中间层异步编程
  • python 访问openai接口
  • 2024年软件设计师中级(软考中级)详细笔记【11】知识产权基础知识(分值2~3分)
  • 6、基于Python+爬虫+LDA+决策树的《富士山下》评论数据情感分析【开题+源程序+论文】
  • Spring Task介绍与基本使用
  • Konva框选移动
  • PPT自动化:掌握 python-pptx 的基础元素
  • 20240818 字节跳动 笔试
  • Python小游戏11——扑克牌消消看小游戏
  • Go入门指南-3.1Go 开发环境的基本要求
  • 哈夫曼树的定义?如何构造?
  • XJ03、消费金融|从场景实例看懂背后的系统架构
  • Xcode使用的一些问题记录
  • 图文深入介绍oracle资源管理(续)