vue3学习(十二)--- 自定义指令

文章目录

Vue3 directive的钩子函数

  1. created 元素初始化的时候
  2. beforeMount 指令绑定到元素后调用 只调用一次
  3. mounted 元素插入父级dom调用
  4. beforeUpdate 元素被更新之前调用
  5. update 这个周期方法被移除 改用updated
  6. beforeUnmount 在元素被移除前调用
  7. unmounted 指令被移除后调用 只调用一次

Vue2 指令 bind inserted update componentUpdated unbind

在setup内定义局部指令

必须以 vNameOfDirective 的形式来命名本地自定义指令,以使得它们可以直接在模板中使用。

javascript 复制代码
<template>
  <button @click="show = !show">开关{{show}} ----- {{title}}</button>
  <Dialog  v-move-directive="{background:'green',flag:show}"></Dialog>
</template>

 
const vMoveDirective: Directive = {
  created: () => {
    console.log("初始化====>");
  },
  beforeMount(...args: Array<any>) {
    // 在元素上做些操作
    console.log("初始化一次=======>");
  },
  mounted(el: any, dir: DirectiveBinding<Value>) {
    el.style.background = dir.value.background;
    console.log("初始化========>");
  },
  beforeUpdate() {
    console.log("更新之前");
  },
  updated() {
    console.log("更新结束");
  },
  beforeUnmount(...args: Array<any>) {
    console.log(args);
    console.log("======>卸载之前");
  },
  unmounted(...args: Array<any>) {
    console.log(args);
    console.log("======>卸载完成");
  },
};

生命周期钩子参数详解

  1. 第一个 el 当前绑定的DOM 元素

  2. 第二个 binding
    instance:使用指令的组件实例。
    value:传递给指令的值。例如,在 v-my-directive="1 + 1" 中,该值为 2。
    oldValue:先前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否有更改都可用。
    arg:传递给指令的参数(如果有的话)。例如在 v-my-directive:foo 中,arg 为 "foo"。
    modifiers:包含修饰符(如果有的话) 的对象。例如在 v-my-directive.foo.bar 中,修饰符对象为 {foo: true,bar: true}。
    dir:一个对象,在注册指令时作为参数传递。例如,在以下指令中

  3. 第三个 当前元素的虚拟DOM 也就是Vnode

  4. 第四个 prevNode 上一个虚拟节点,仅在 beforeUpdate 和 updated 钩子中可用

函数形式简写

你可能想在 mounted 和 updated 时触发相同行为,而不关心其他的钩子函数。那么你可以通过将这个函数模式实现

javascript 复制代码
<template>
   <div>
      <input v-model="value" type="text" />
      <A v-move="{ background: value }"></A>
   </div>
</template>
   
<script setup lang='ts'>
import A from './components/A.vue'
import { ref, Directive, DirectiveBinding } from 'vue'
let value = ref<string>('')
type Dir = {
   background: string
}
const vMove: Directive = (el, binding: DirectiveBinding<Dir>) => {
   el.style.background = binding.value.background
}
</script>
 
 
 
<style>
</style>

案例1 --- 自定义拖拽指令

javascript 复制代码
<!--
 * @Author: qfuli
 * @Date: 2023-10-19 10:04:21
 * @LastEditors: qfuli
 * @LastEditTime: 2023-10-26 15:04:29
 * @Description: Do not edit
 * @FilePath: /ts-learning/vite-project/src/components/docPage/11-directive/拖拽案例.vue
-->
<template>
  <div v-move class="box">
    <div class="header"></div>
    <div>
      内容
    </div>
  </div>

</template>

<script lang='ts' setup>
  
import { Directive} from 'vue'

// 指令名称必须以 vxxx 的形式来命名本地自定义指令,以使得它们可以直接在模板中使用。
const vMove: Directive = {
  mounted(el:HTMLElement){
    let moveEl = el.firstElementChild as HTMLElement;
    let parentEl = el as HTMLElement;
    const mouseDown = (e:MouseEvent)=>{
      //鼠标点击物体那一刻相对于物体左侧边框的距离=点击时的位置相对于浏览器最左边的距离-物体左边框相对于浏览器最左边的距离
      console.log(e.clientX, e.clientY, "-----起始", el.offsetLeft);
      let X = e.clientX - el.offsetLeft;
      let Y = e.clientY - el.offsetTop;
      const move = (e:MouseEvent)=>{
        let left =  e.clientX - X;
        let top =  e.clientY - Y;
        console.log(parentEl.offsetHeight);
        if(left + moveEl.offsetWidth/2 >= document.body.offsetWidth){
          left = document.body.offsetWidth - moveEl.offsetWidth/2;
        }else if(left - moveEl.offsetWidth/2 <= 0){
          left = moveEl.offsetWidth/2 
        }
        if(top - parentEl.offsetWidth/2 <=0){
          top = parentEl.offsetWidth/2
        }else if(top + parentEl.offsetHeight/2 >= document.body.offsetHeight){
          top = document.body.offsetHeight - parentEl.offsetHeight/2
        }
        
        el.style.left =left + "px";
        el.style.top = top + "px";

      }
      document.addEventListener("mousemove", move);
      document.addEventListener("mouseup", () => {
        document.removeEventListener("mousemove", move);
      });
    }
    moveEl.addEventListener("mousedown", mouseDown);
  }
}
  
</script>
 <style>
html,body{
  margin: 0;
  padding: 0;
  height: 100%;
}</style> 
<style scoped lang="scss">

.box {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 200px;
  height: 200px;
  border: 1px solid #ccc;
  .header {
    height: 20px;
    background: black;
    cursor: move;
  }
}
</style>

案例2 --- 图片懒加载指令

javascript 复制代码
<!--
 * @Author: qfuli
 * @Date: 2023-10-26 15:16:10
 * @LastEditors: qfuli
 * @LastEditTime: 2023-10-26 15:49:09
 * @Description: Do not edit
 * @FilePath: /ts-learning/vite-project/src/components/docPage/11-directive/图片懒加载.vue
-->
<template>
  <div>
        <div v-for="item in arr">
            <img height="500" :data-index="item" v-lazy="item" width="360" alt="">
        </div>
    </div>
</template>

<script lang='ts' setup>
  
import { Directive } from 'vue'
// import.meta.glob 是vite提供的

// glob是懒加载模块
// let module = {
//    'xx':import('xxxx')
// }

// glob {eager:true} 静态加载模块
// import xx from 'xx'

//这样方便我们批量加载静态资源
let imageList:Record<string,{default:string}> = import.meta.glob('./images/*.*',{eager:true})  
console.log('imageList',imageList)
let arr = Object.values(imageList).map(v=>v.default)

// 指令
let vLazy:Directive<HTMLImageElement,string> = async (el,binding)=>{
  let url = await import('@/assets/vue.svg');
  console.log('url',url)
  el.src = url.default

  //观察元素是否在可视区域
  let observer = new IntersectionObserver((entries)=>{
    console.log('entries',entries[0],el);
    // intersectionRatio 大于0表示可见的比例 1是全可见
    if (entries[0].intersectionRatio > 0) {
            setTimeout(() => {
                el.src = binding.value;
                observer.unobserve(el)
            }, 1000)
        }
  })
  observer.observe(el);
}
</script>
  
<style scoped></style>
相关推荐
我叫黑大帅21 分钟前
前端总说的防抖与节流到底是什么?
前端·javascript·面试
掘金安东尼24 分钟前
从平面到空间:用 React Three Fiber 构建 3D 产品网格
前端·javascript·面试
swipe26 分钟前
#用这 9 个浏览器 API,我把页面从“卡成 PPT”救回到 90+(每个都有能直接抄的例子)
前端·javascript·面试
摸鱼的春哥3 小时前
Agent教程15:认识LangChain,Agent框架的王(上)
前端·javascript·后端
明月_清风4 小时前
自定义右键菜单:在项目里实现“选中文字即刻生成新提示”
前端·javascript
明月_清风4 小时前
告别后端转换:高质量批量导出实战
前端·javascript
刘发财8 小时前
弃用html2pdf.js,这个html转pdf方案能力是它的几十倍
前端·javascript·github
ssshooter15 小时前
看完就懂 useSyncExternalStore
前端·javascript·react.js
Live0000016 小时前
在鸿蒙中使用 Repeat 渲染嵌套列表,修改内层列表的一个元素,页面不会更新
前端·javascript·react native
柳杉16 小时前
使用Ai从零开发智慧水利态势感知大屏(开源)
前端·javascript·数据可视化