请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

我的mitt() 的使用方法是否出错了?funcArr.value.map(func => func()) 遍历的结果有错误

老师你好,我是用setup语法糖和组合式API编写的。想知道是不是mitt() 的使用方法是否出错了?
仓库地址: https://git.imooc.com/Ak17/Vue3zheye_Ak17 
具体问题表现如下:

  1. 我定义的存放校验函数数组 funcArr ,它重复传入了一次校验函数,本来是想应该就传入一次就好。具体表现为,添加验证函数到数组的方法 register ,页面在一加载时就执行了4次,我的预期时执行2次
    https://img1.sycdn.imooc.com//szimg/6476d84f0970a76603920563.jpg
    https://img1.sycdn.imooc.com//szimg/6476d98f09ad34ec02170178.jpg

  2. 定义了 const results = funcArr.value.map(func => func()) ,console.log("Validation results:", results)来验证,但在输入框内容正确与否的情况下,results的值为 undefined 的数组,而不是布尔值
    https://img1.sycdn.imooc.com//szimg/6476dc6c0960f0f704930116.jpg

下面附上两个组件的代码

// ValidateForm.vue
<template>
    <form class="validate-form-container">
        <slot name="default"></slot>
        <div class="submit-area">
            <slot name="submit">
                <button type="submit" class="btn btn-primary" @click.prevent="submitForm">
                    提交
                </button>
            </slot>
        </div>
        <div class="reset-area">
            <slot name="reset">
                <button type="submit" class="btn btn-danger" @click.prevent="clearInputs">
                    重置
                </button>
            </slot>
        </div>
    </form>
</template>

<script setup>
import { emitter } from "./ValidateInput.vue"
import { defineEmits, onUnmounted, ref } from "vue"

// 自定义 hook,useValidateForm用于管理校验函数
type ValidateFunc = () => boolean
const useValidateForm = (emit: (event: string, ...args: any[]) => void) => {
    // 校验函数数组
    const funcArr = ref<ValidateFunc[]>([])
    // 提交表单时校验所有函数,并发出相应事件
    const submitForm = () => {
        // const result = funcArr.value.map(func => func()).every(result => result)
        // console.log("Form is submitting! reslut:" + result)
        // emit("form-submit", result)
        const results = funcArr.value.map(func => func())
        console.log(funcArr.value)
        console.log("Validation results:", results)
        const result = results.every(result => result)
        console.log("Form is submitting!", result)
        emit("form-submit", result)
    }
    // 注册校验函数
    const register = (func?: ValidateFunc) => {
        if (func) {
            funcArr.value.push(func)
        }
        console.log("register执行了")
        console.log("funcArr:", funcArr.value)
    }
    // 取消注册校验函数
    const unregister = (func?: ValidateFunc) => {
        if (func) {
            const index = funcArr.value.indexOf(func)
            if (index !== -1) {
                funcArr.value.splice(index, 1)
            }
        }
    }
    // 清空校验函数数组
    onUnmounted(() => {
        funcArr.value = []
        emitter.off("form-item-created")
    })

    return {
        submitForm,
        register,
        unregister,
    }
}

//自定义hook,clearForm用于清空输入框
type clearFunc = () => void
const clearForm = (emit: (event: string, ...args: any[]) => void) => {
    const clearFuncArr = ref<clearFunc[]>([])

    const clearInputs = () => {
        const result = clearFuncArr.value.map(func => func())
        console.log("Form is cleared", result)
        emit("clear-inputs", result)
    }
    const clearReg = (func?: clearFunc) => {
        clearFuncArr.value.push(func)
    }
    onUnmounted(() => {
        clearFuncArr.value = []
        emitter.off("form-item-cleared")
    })
    return {
        clearInputs,
        clearReg,
    }
}

// 定义组件的自定义事件
const emits = defineEmits(["form-submit", "clear-inputs"])
// 创建自定义 hook 实例,用于维护校验函数数组并在组件销毁时释放资源
const { submitForm, register } = useValidateForm(emits)
// 创建自定义 hook 实例,用于清空输入框
const { clearInputs, clearReg } = clearForm(emits)

//监听传过来的验证方法
// onMounted(() => {
emitter.on("form-item-created", (func?: ValidateFunc) => {
    if (func) {
        register(func)
    }
})
emitter.on("form-item-cleared", (func?: clearFunc) => {
    if (func) {
        clearReg(func)
    }
})
// })
</script>
//ValidateInput.vue
<template>
    <div>
        <div class="validate-input-container pb-3">
            <input
                type="text"
                class="form-control"
                :class="{ 'is-invalid': inputRef.error, 'is-valid': inputRef.allPassed }"
                v-model="inputRef.val"
                @blur="toValidate"
                @input="updateValue"
            />
            <span v-if="inputRef.error" class="invalid-feedback">{{ inputRef.message }}</span>
        </div>
    </div>
</template>
<script>
import mitt from "mitt"
export const emitter = mitt()
</script>

<script setup>
import { ref, reactive, inject } from "vue"

export interface RuleProps {
    type: "required" | "email" | "pwdRange"
    message?: string
    //以下要两个属性配合range使用
    min?: { message: string; minlength: number }
    max?: { message: string; maxlength: number }
}
export type RulePropsArr = RuleProps[]

const props = defineProps<{
    rules: RulePropsArr
    modelValue?: string
    validateType?: RuleProps["type"] //设置输入框验证类型
}>()

const inputRef = reactive({
    val: props.modelValue || "",
    error: false,
    message: "", //可编辑的提示信息
    allPassed: false,
})

//邮箱正则
const emailReg =
    /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
//邮箱验证通过的逻辑
const validateInput = () => {
    if (props.rules) {
        const allPassed = props.rules.every(rule => {
            let passed = true
            inputRef.message = rule.message
            switch (rule.type) {
                case "required":
                    passed = inputRef.val.trim() !== ""
                    break
                case "email":
                    passed = emailReg.test(inputRef.val)
                    break
                default:
                    break
            }
            return passed
        })
        inputRef.error = !allPassed
        console.log("inputRef.error = " + inputRef.error)
        inputRef.allPassed = allPassed
    }
}

//密码验证通过的逻辑
const passwordInput = () => {
    if (props.rules) {
        const allPassed = props.rules.every(rule => {
            let passed = true
            switch (rule.type) {
                case "required":
                    passed = inputRef.val.trim() !== ""
                    inputRef.message = rule.message
                    break
                case "pwdRange":
                    if (inputRef.val.trim().length >= 6 && inputRef.val.trim().length <= 12) {
                        passed = true
                        inputRef.message = "密码长度正确"
                    } else {
                        passed = false
                        inputRef.message = "密码长度需在六位至十二位"
                    }
                    break
                default:
                    break
            }
            return passed
        })
        inputRef.error = !allPassed
    }
    console.log("密码验证执行了")
}
const emit = defineEmits(["update:modelValue", "form-item-created", "form-item-cleared"]) // defineEmits格式是约定的, 1、默认 v-model 对应:'update:modelValue', 2、自定义v-model对应:'update:自定义变量名'
const updateValue = (e: KeyboardEvent) => {
    const targetValue = (e.target as HTMLInputElement).value
    inputRef.val = targetValue
    emit("update:modelValue", targetValue)
}
//验证何种类型
const toValidate = computed(() => {
    if (props.validateType == "email") {
        return validateInput
    } else if (props.validateType == "pwdRange") {
        return passwordInput
    }
})

const clearInputs = () => {
    inputRef.val = ""
    inputRef.message = ""
    console.log("重置执行了" + inputRef.error)
}

onMounted(() => {
    emitter.emit("form-item-created", validateInput)
    emitter.emit("form-item-created", passwordInput)
    emitter.emit("form-item-cleared", clearInputs)
})
</script>
<style scoped></style>
//App.vue
<template>
    <el-container direction="vertical">
        <GlobalHeader :user="currentUser"></GlobalHeader>
        <!-- <ColumnList :list="testData" /> -->
        <ValidateForm>
            <div class="mb-3">
                <label class="form-label">邮箱地址</label>
                <ValidateInput :rules="currentInput" :validate-type="`email`"></ValidateInput>
                <label class="form-label">密码</label>
                <ValidateInput :rules="passwordInput" :validate-type="`pwdRange`"></ValidateInput>
            </div>
        </ValidateForm>
        <template #submit></template>
        <template #reset></template>
    </el-container>
</template>
<script setup>
import ValidateInput, { RulePropsArr } from "./components/ValidateInput.vue"
import ValidateForm from "./components/ValidateForm.vue"
import "bootstrap/dist/css/bootstrap.min.css"

//邮箱验证--传入ValidateInput组件的props.rules
const currentInput: RulePropsArr = [
    { type: "required", message: "邮箱地址不能为空" },
    { type: "email", message: "请输入正确的邮箱地址" },
]

//密码验证--传入ValidateInput组件的props.rules
const passwordInput: RulePropsArr = [
    { type: "required", message: "密码不能为空" },
    { type: "pwdRange", min: { message: "你的密码至少包含六位且不含空格", minlength: 6 } },
    { type: "pwdRange", max: { message: "你的密码至多为十二位且不含空格", maxlength: 12 } },
]
</script>

清空内容的功能正常


正在回答

1回答

同学你好

原因是在这里:

你在 input 中发射了两次创建的事件啊~
emitter.emit("form-item-created", validateInput)
emitter.emit("form-item-created", passwordInput)
// 所以自然,每个 input 会有两个事件

所以你这里不应该是发送两个函数,而是要将两个函数整合为一个 validateInput

这个 validateInput 中应该包含所有验证的逻辑
参看:
https://git.imooc.com/coding-449/zheye/src/develop/src/components/ValidateInput.vue#L66
// 当然上面的链接是 develop 的实现,要复杂一些,包含最后的一系列功能,你酌情参考就行


1 回复 有任何疑惑可以回复我~
  • 提问者 阿康喜欢蓝色 #1
    钻了几天牛角尖,chatGPT都问了很多次。谢谢老师的准确解答!
    回复 有任何疑惑可以回复我~ 2023-06-01 13:19:38
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信