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" />
// 在子组件中通过定义model
export 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">
<input
class="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父组件中引入并定义emitter
import 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);
});
}
})