您的位置:首页 > 房产 > 家装 > 学习vue3 七 v-model用于组件通信,自定义指令,自定义hooks

学习vue3 七 v-model用于组件通信,自定义指令,自定义hooks

2024/10/6 14:29:05 来源:https://blog.csdn.net/tian_liang_jia/article/details/141016930  浏览:    关键词:学习vue3 七 v-model用于组件通信,自定义指令,自定义hooks

v-model

在vue3中v-model是破环性更新

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

子组件要修改父组件的值

  • prop:value -> modelValue
  • 事件:input -> update:modelValue
  • v-bind 的 .sync 修饰符和组件的 model 选项已移除

v-model的更多用法

  • 新增 支持多个v-model
  • 新增 支持自定义 修饰符 Modifiers

单个v-model 

案例:

父组件

<script setup lang="ts">
// 通过v-model来实现父子组件之间的双向绑定
import A from './components/A.vue'
import {ref} from "vue";
const flag = ref(true)</script><template><div class="switch">{{flag}}<button @click="flag = !flag">切换</button></div><transitionenter-active-class="animate__animated animate__backInDown"leave-active-class="animate__animated animate__backOutDown"><A v-model="flag"></A></transition>
</template><style scoped>
.switch {width: 200px;margin: 5px auto;
}
</style>

子组件

<script setup lang="ts">
defineProps<{modelValue:boolean
}>()
const emit = defineEmits<{(e:'update:modelValue', value: boolean): void
}>()
function submit() {emit('update:modelValue', false)
}
</script><template><div class="container" v-if="modelValue"><div><span>请输入:</span><input type="text"></div><button @click="submit">提交</button></div>
</template><style scoped>
.container {width: 500px;height: 500px;background-color: #ccc;border: 1px solid grey;margin: 0 auto;
}
.header {height: 50px;background-color: #eee;border: 1px solid grey;
}
.body {height: 400px;background-color: #ddd;border: 1px solid grey;
}
.footer {height: 50px;background-color: #eee;border: 1px solid grey;
}
</style>

也可以给一个组件绑定多个v-model

案例:

父组件

<A v-model="flag" v-model:title="title"></A>

子组件

<script setup lang="ts">
defineProps<{modelValue:boolean,title:string
}>()
const emit = defineEmits<{(e:'update:modelValue', value: boolean): void,(e:'update:title', value: string): void
}>()
function submit() {emit('update:modelValue', false)
}
function inputChange(e:any) {let value = e.target.valueemit('update:title', value)
}
</script><template><div class="container" v-if="modelValue"><div><span>请输入:</span><input type="text" :value="title" @input="inputChange($event)"></div><button @click="submit">提交</button></div>
</template><style scoped>
.container {width: 500px;height: 500px;background-color: #ccc;border: 1px solid grey;margin: 0 auto;
}
.header {height: 50px;background-color: #eee;border: 1px solid grey;
}
.body {height: 400px;background-color: #ddd;border: 1px solid grey;
}
.footer {height: 50px;background-color: #eee;border: 1px solid grey;
}
</style>

自定义修饰符

可以通过 modelModifiers prop的方式自定义

案例:

自定义一个增加修饰符,有它就增加一个好

父组件

<A v-model="flag" v-model:title.add="title"></A>

子组件

<script setup lang="ts">
const props = defineProps<{modelValue:boolean,title:string,titleModifiers?: {[key: string]: boolean}
}>()
const emit = defineEmits<{(e:'update:modelValue', value: boolean): void,(e:'update:title', value: string): void
}>()
function submit() {emit('update:modelValue', false)if(props.titleModifiers?.add) {emit('update:title', props.title + '好')}
}
function inputChange(e:any) {let value = e.target.valueemit('update:title', value)
}
</script><template><div class="container" v-if="modelValue"><div><span>请输入:</span><input type="text" :value="title" @input="inputChange($event)"></div><button @click="submit">提交</button></div>
</template><style scoped></style>

directive-自定义指令

vue里面有一些系统定义好的指令,如v-model,v-show,v-if,v-bind,v-for等,它也可以支持自定义指令

1. 指令的生命周期钩子函数

和vue组件的生命周期函数很类似

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

2. 在setup语法糖模式下定义局部指令

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

定义:

import {ref,Directive,DirectiveBinding} from "vue";
const vMove:Directive = {mounted(el:HTMLElement,binding:DirectiveBinding){console.log(binding)el.style.backgroundColor = '#bfa'}
}

使用:

<A v-move:aaa.stop="title"></A>

钩子函数的参数:

 第一个为,el即绑定的元素,第二个一个DirectiveBinding类型的对象,第一个属性arg为指令名字modifiers里放的就是修饰符

3. 权限按钮案例

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

这个案例将会采用函数模式

<script setup lang="ts">
import { DirectiveBinding } from 'vue'
import type { Directive } from 'vue'
localStorage.setItem("userId",'taotao')
let permission = ['taotao:shop:edit','taotao:shop:delete','taotao:shop:manage'
]
let userId = localStorage.getItem('userId')
const vFocus: Directive = (el:HTMLElement,binding:DirectiveBinding) => {if (!permission.includes(userId + ':' + binding.value)) {el.style.display = 'none'}
}
</script><template><div class="context"><button v-focus="`shop:edit`">编辑</button><button v-focus="`shop:manage`">管理</button><button v-focus="`shop:delete`">删除</button></div>
</template><style scoped>
.context {width: 500px;height: 300px;background-color: #ccc;border: 1px solid grey;margin: 0 auto;
}button {width: 100px;height: 50px;margin: 10px;background-color: #fff;border: 1px solid grey;}
</style>

4.图片懒加载案例

想实现图片懒加载,首先需要将图片引入

可以采用vite提供的import.meta.glob("路劲")的形式

注意需要配置,这样才能直接全部导入

{ eager: true }

指定返回值的key为string类型,值必须是一个对象,且对象内部具有default属性 

  • Record 类型是TS中其众多强大特性之一
  • 它为我们提供了创建键值对映射的强大能力
  • 极大地增强了代码的灵活性与类型安全性
Record<string, { default:string }>
<script setup lang="ts">
// 实现图片懒加载
import {Directive,DirectiveBinding} from "vue";
let images: Record<string, { default:string }> = import.meta.glob("./assets/*.*",{ eager: true });
let imagesList = Object.values(images).map((item) => item.default);</script><template><div class="image"v-for="item in imagesList":key="item"><img :src="item" alt="图片"></div>
</template><style scoped>
.image img{width: 300px;
}
</style>

2. 需要观察视口的变化,图片有无在可显示的区域

<script setup lang="ts">
// 实现图片懒加载
import {Directive,DirectiveBinding} from "vue";
let images: Record<string, { default:string }> = import.meta.glob("./assets/*.*",{ eager: true });
let imagesList = Object.values(images).map((item) => item.default);
const vLazy:Directive = (el:HTMLImageElement,binding:DirectiveBinding)=> {let observer = new IntersectionObserver(async (entries) => {let img = await import ("./assets/image/vue.svg")el.src = img.defaultif (entries[0].isIntersecting && entries[0].intersectionRatio > 0) {setTimeout(()=> {el.src = binding.valueobserver.unobserve(el);},2000)}});observer.observe(el);
}</script><template><div class="image"v-for="item in imagesList":key="item"><img :src="item" alt="图片" v-lazy="item"></div>
</template><style scoped>
.image img{width: 300px;
}
</style>

自定义Hooks

Vue3 的自定义的hook

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

案例:将图片转为base64格式

app.vue

<script setup lang="ts">
// 自定义hook
let images: Record<string, { default:string }> = import.meta.glob("./assets/*.*",{ eager: true });
let imagesList = Object.values(images).map((item) => item.default);
import useHooks from './hooks/index.ts'
const base64 = useHooks({el: '#image'})
base64.then(res => {console.log(res.value)
})
</script><template><div class="image"><img :src="imagesList[0]" id="image"></div>
</template><style scoped>
.image img{width: 300px;
}
</style>

hooks/index.ts

type Options = {el: string
}
import {onMounted} from "vue";export default function (option:Options) {return new Promise((resolve) => {onMounted(()=> {const file:HTMLImageElement = document.querySelector(option.el) as HTMLImageElementfile.onload = () => {resolve({value:toBase64(file)})}})function toBase64(file:HTMLImageElement) {const canvas = document.createElement('canvas')canvas.width = file.widthcanvas.height = file.heightconst ctx = canvas.getContext('2d')if (ctx) {ctx.drawImage(file, 0, 0, file.width, file.height)return canvas.toDataURL('image/png')}}})
}

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com