hooks
useClickOutside
点击下拉框外部时,触发开关事件。
使用html的node.contains进行判断
import { Ref, ref, onMounted, onUnmounted } from 'vue';// element为绑定的下拉框的元素function useClickOutside(element: Ref<HTMLElement | null>): Ref<boolean> {// 定义一个响应数据,处理是否点击的是下拉框内。const isClickOutside = ref<boolean>(false);const handler = (e: MouseEvent) => {if (element.value) {if (element.value.contains(e.target as HTMLElement)) {isClickOutside.value = false;} else {isClickOutside.value = true;}}};onMounted(() => {document.addEventListener('mousedown', handler);});onUnmounted(() => {document.removeEventListener('mousedown', handler);});return isClickOutside;}export default useClickOutside;
Form表单开发
v-model语法
vue2中的model用法
存在不足:
- 无法绑定多个model
- 处理checkbox等非input事件时,需要设定model来覆盖默认的value/input。有些鸡肋。
为了解决第二个问题,处理非input事件,vue2.2加入了model属性,可以自定义事件和绑定的值<Child v-model = "title />// 等价于 >><Child :value = "title" @input = "title = $event" />
// 父组件中引用自组件<Child v-model="gender" />
// 在子组件中通过定义modelexport default {model: {prop: 'gender', // v-model绑定的属性名称event: 'change' // v-model绑定的事件},props: {value: String, // 此时value跟v-model无关gender: { // gender是v-model绑定的属性type: String,default: 'M'}}}
但是,仍然无法解决子组件中有多个model值的绑定。
vue3中的model用法
vue3为了解决在vue2中存在的问题,把默认value/input类型绑定给去除掉。而是使用了modelValue/update:modelValue作为默认绑定字段
<Child v-model = "email">// 相当于<Child :modelValue="email" @update:modelValue = "handleUpdateValue">
// 在子组件中定义export default defineComponent({props:{modelValue:String, // v-model绑定的属性值},setup(){const updateValue = (e: KeyboardEvent) => {context.emit("update:modelValue",targetValue); //传递targetValue给父组件的handleUpdateValue}}}
设置自定义参数
在vue3中可以给v-model:defineName,定义一个语意化的字段。并且可以定义多个model。
<Child v-model:useName="useName" v-model:email = "email" />//等价于<ChildComponent:useName="useName" @update:useName = "handleUseName":email="email" @update:email = "handleEmail"/>
在子组件定义属性props和事件event。
export default defineComponent({props:{useName:String,email:String,},setup(){const updateUseName = (e: KeyboardEvent) => {context.emit("update:useName", e.target.value);}const updateEmail = (e: KeyboardEvent) => {context.emit("update:email", e.target.value);}}}
组件属性的绑定
inheritAttrs
组件进行属性传值时,如果没有props定义,会把属性值给定义到子组件的根节点上。为了组织把属性绑定到跟节点,可以给子组件添加属性inheritAttrs:false
export default defineComponent({props:{ // ... },inheritAttrs: false,})
v-bind=”$attrs”
去除了绑定到跟节点后,可以给需要绑定属性的标签添加v-bind=”$attrs”
<template><div class="form"><inputclass="input":class="{ 'is-danger': inputRef.error }"v-bind="$attrs"/></div></template>
插槽slot
插槽模版值
子组件中设置插槽命名和位置。
<div>// 具名插槽<slot name="header"></slot>// 默认插槽<slot></slot>// 具名插槽<slot name="footer"></slot></div>
在父组件中传递slot的值,v-slot:header或者简写#header
<template v-slot:header><button class="button is-danger">submit</button></template>
slot插槽元素上的事件,父子组件通讯
在父组件上通过定义ref获取子组件的属性或方法
<ValidateInput ref="demoRef"></ValidateInput><script lang="ts">setup(){const demoRef = ref<any>(null);onMounted(()=>{console.log('demoRef', demoRef.value.validateInput());})}</script>
使用插件mitt,传递数据或事件
如果子组件被放置在父组件的slot元素上,就无法使用ref标签。如何获取子组件的事件和属性?
通过公用的事件监听,引入外部第三方模块mitt。
// 在App组件中引入ValidateForm和ValidateInput,共用组件<ValidateForm @form-submit="handleFormSubmit"><ValidateInput :rules="emailRules"v-model="emailVal" type="email"placeholder="请输入邮箱地址" ref="demoRef"><label class="label">邮箱地址:</label></ValidateInput><ValidateInput :rules="passwordRules"v-model="passwordVal"type="password"placeholder="请输入密码"><label class="label">密 码:</label></ValidateInput><template #submit="slotProps"><button class="button is-danger">submit{{ slotProps }}</button></template></ValidateForm><script lang="ts">export default defineComponent({setup(){const handleFormSubmit = (val: boolean) => {console.log('handleFormSubmit', val);// console.log('demoRef', demoRef.value.validateInput());};}})</script>
// form父组件中引入并定义emitterimport mitt from 'mitt';type ValidateFunc = () => boolean;type Events = {'form-item-created': ValidateFunc;};export const emitter = mitt<Events>();export default defineComponent({emits: ['form-submit'],setup(props, context) {let inputFuncArr: ValidateFunc[] = [];const callback = (func: ValidateFunc): void => {inputFuncArr.push(func);};emitter.on('form-item-created', callback);onUnmounted(() => {emitter.off('form-item-created', callback);inputFuncArr = [];});return { submitForm, item };}});
// 在input子组件中使用。首先引入form中导出的emitter对象import { emitter } from './ValidateForm.vue';export default defineComponent({setup(props, context) {onMounted(() => {// 放在生命周期,测试emitter.emit('form-item-created', validateInput);});}})
