Fork me on GitHub

vue3(六)(深入v-model,自定义指令directive,组合式函数(自定义Hooks))

深入v-model

TIps 在Vue3 v-model 是破坏性更新的

v-model在组件里面也是很重要的

v-model 其实是一个语法糖 通过props 和 emit组合而成的

1.默认值的改变

prop:value -> modelValue;
事件:input -> update:modelValue;
v-bind 的 .sync 修饰符和组件的 model 选项已移除
新增 支持多个v-model
新增 支持自定义 修饰符 Modifiers
案例 子组件

<template>
     <div v-if='propData.modelValue ' class="dialog">
         <div class="dialog-header">
             <div>标题</div><div @click="close">x</div>
         </div>
         <div class="dialog-content">
            内容
         </div>
         
     </div>
</template>
 
<script setup lang='ts'>
 
type Props = {
   modelValue:boolean
}
 
const propData = defineProps<Props>()
 
const emit = defineEmits(['update:modelValue'])
 
const close = () => {
     emit('update:modelValue',false)
}
 
</script>
 
<style lang='less'>
.dialog{
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    position: fixed;
    left:50%;
    top:50%;
    transform: translate(-50%,-50%);
    &-header{
        border-bottom: 1px solid #ccc;
        display: flex;
        justify-content: space-between;
        padding: 10px;
    }
    &-content{
        padding: 10px;
    }
}
</style>

  

父组件

<template>
  <button @click="show = !show">开关{{show}}</button>
  <HelloWorld v-model="show"></HelloWorld>
</template>
 
<script setup lang='ts'>
import HelloWorld from "../components/HelloWorld.vue";
import {ref} from 'vue'
const show = ref(false)
</script>
 
<style>
</style>

  

绑定多个案例

 子组件

<template>
     <div v-if='modelValue ' class="dialog">
         <div class="dialog-header">
             <div>标题---{{title}}</div><div @click="close">x</div>
         </div>
         <div class="dialog-content">
            内容
         </div>
         
     </div>
</template>
 
<script setup lang='ts'>
 
type Props = {
   modelValue:boolean,
   title:string
}
 
const propData = defineProps<Props>()
 
const emit = defineEmits(['update:modelValue','update:title'])
 
const close = () => {
     emit('update:modelValue',false)
     emit('update:title','我要改变')
}
 
</script>
 
<style lang='less'>
.dialog{
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    position: fixed;
    left:50%;
    top:50%;
    transform: translate(-50%,-50%);
    &-header{
        border-bottom: 1px solid #ccc;
        display: flex;
        justify-content: space-between;
        padding: 10px;
    }
    &-content{
        padding: 10px;
    }
}
</style>

 

父组件

<template>
  <button @click="show = !show">开关{{show}} ----- {{title}}</button>
  <HelloWorld v-model:title='title' v-model="show"></HelloWorld>
</template>
 
<script setup lang='ts'>
import HelloWorld from "../components/HelloWorld.vue";
import {ref} from 'vue'
const show = ref(false)
const title = ref('我是标题')
</script>
 
<style>
</style>

  

自定义指令directive

directive-自定义指令(属于破坏性更新)
Vue中有v-if,v-for,v-bind,v-show,v-model 等等一系列方便快捷的指令 今天一起来了解一下vue里提供的自定义指令

1.Vue3指令的钩子函数
created 元素初始化的时候
beforeMount 指令绑定到元素后调用 只调用一次
mounted 元素插入父级dom调用
beforeUpdate 元素被更新之前调用
update 这个周期方法被移除 改用updated
beforeUnmount 在元素被移除前调用
unmounted 指令被移除后调用 只调用一次
Vue2 指令 bind inserted update componentUpdated unbind

2.在setup内定义局部指令
但这里有一个需要注意的限制:必须以 vNameOfDirective 的形式来命名本地自定义指令,以使得它们可以直接在模板中使用。

<template>

  <HelloWorld  v-move-directive="{background:'green'}"></HelloWorld>

</template>
 
<script setup lang='ts'>
import HelloWorld from "../components/HelloWorld.vue";
import {ref,Directive,DirectiveBinding} from 'vue'
 
const vMoveDirective: Directive = {
  created: () => {
    console.log("初始化====>");
  },
  beforeMount(...args: Array<any>) {
    // 在元素上做些操作
    console.log("初始化一次=======>");
  },
  mounted(el: any, dir: DirectiveBinding<any>) {
    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("======>卸载完成");
  },
};
</script>
 
<style>
</style>

  

3.生命周期钩子参数详解
第一个 el 当前绑定的DOM 元素

第二个 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:一个对象,在注册指令时作为参数传递。例如,在以下指令中

 

 


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

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

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

<template>
   <div>
      <input v-model="value" type="text" />
      <HelloWorld v-move="{ background: value }"></HelloWorld>
   </div>
</template>
   
<script setup lang='ts'>
import HelloWorld from '../components/HelloWorld.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>

案例自定义拖拽指令   

<template>
  <div v-move class="box">
    <div class="header"></div>
    <div>
      内容
    </div>
  </div>
</template>
 
<script setup lang='ts'>
import { Directive } from "vue";
const vMove: Directive = {
  mounted(el: HTMLElement) {
    let moveEl = el.firstElementChild 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) => {
        el.style.left = e.clientX - X + "px";
        el.style.top = e.clientY - Y + "px";
        console.log(e.clientX, e.clientY, "---改变");
      };
      document.addEventListener("mousemove", move);
      document.addEventListener("mouseup", () => {
        document.removeEventListener("mousemove", move);
      });
    };
    moveEl.addEventListener("mousedown", mouseDown);
  },
};
</script>
 
<style lang='less'>
.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>

  

组合式函数(自定义Hooks)

主要用来处理复用代码逻辑的一些封装

这个在vue2 就已经有一个东西是Mixins

mixins就是将这些多个相同的逻辑抽离出来,各个组件只需要引入mixins,就能实现一次写代码,多组件受益的效果。

弊端就是 会涉及到覆盖的问题,变量来源不明确(隐式传入),不利于阅读,使代码变得难以维护

Vue3 的自定义的hook

Vue3 的 hook函数 相当于 vue2 的 mixin, 不同在与 hooks 是函数
Vue3 的 hook函数 可以帮助我们提高代码的复用性, 让我们能在不同的组件中都利用 hooks 函数
Vue3 hook 库Get Started | VueUse

案例
新建ts结尾文件

import { onMounted } from 'vue'
 
 
type Options = {
    el: string
}
 
type Return = {
    Baseurl: string | null
}
export default function (option: Options): Promise<Return> {
    
    return new Promise((resolve) => {
        onMounted(() => {
            const file: HTMLImageElement = document.querySelector(option.el) as HTMLImageElement;
            file.onload = ():void => {
                resolve({
                    Baseurl: toBase64(file)
                })
            }
 
        })
 
 
        const toBase64 = (el: HTMLImageElement): string => {
            const canvas: HTMLCanvasElement = document.createElement('canvas')
            const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
            canvas.width = el.width
            canvas.height = el.height
            ctx.drawImage(el, 0, 0, canvas.width,canvas.height)
            
            return canvas.toDataURL('image/png')
 
        }
    })
 
 
}

  

引用自定义hook

<template>
  <div>
    <img id="img" width="200" height="200" src="../assets/logo.png" alt="">
  </div>
</template>
 
<script setup lang='ts'>
import useCanvas from '../hook/useCanvas'
import { ref } from "vue";
useCanvas({el:'#img'}).then((res)=>{
  console.log(res)
})
</script>
 
<style lang='less'>

</style>

 


————————————————
版权声明:本文为CSDN博主「小满zs」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq1195566313/article/details/123187523

posted @ 2022-11-03 14:53  小白不白10  阅读(1103)  评论(1编辑  收藏  举报