Вы действительно знаете проверку формы vue? Проверка формы компонента (форма)

Vue.js
Вы действительно знаете проверку формы vue? Проверка формы компонента (форма)

предисловие

Давно не писал статьи, немного изучил webpack, некоторые базовые компоненты, а сегодня привожу туториал по компоненту валидации формы (element.iviewui) (как новичок, после всего опыта многих проекты, могу дать несколько советов новичкам

Узнайте о плавании фитнес:github 技术文档Техническая документация будет постоянно обновляться


Краткое содержание

  1. Объяснение принципа
  2. отправка и передача // взаимодействие родитель-потомок
  3. Представляем async-validator // плагин проверки формы
  4. дизайн формы / и API
  5. form-item
  6. input

визуализация

1. Объяснение принципа

原理

рассмотреть возможность

Давайте посмотрим, что мы можем использовать форму для запуска проверки в целом или один вход для запуска элемента формы для проверки. Детская обувь может все еще чувствовать, что она все еще не понимает, так что можно продолжать чтение.

2. Распространение и трансляция

Зачем использовать трансляцию и распространение? Обычно мы стараемся не использовать vuex и bus (event bus) для компонентов, не имеющих никакого отношения к делу. Ниже присылаю код для вещания и рассылки. Привязываем компонент, который нужно вызватьthis.$on('event',res=>()), Посредством распределения и широковещательных вызовов$emit.

  1. Диспетчер ищет и звонит только 1
  2. Вещание смотрит вниз, чтобы вызвать несколько
  3. Примечание⚠️Все компоненты должны быть прописаныname
  4. миксеромmixinsиспользовать
emitter.js
/**
 * 递归使用 call 方式this指向
 * @param componentName // 需要找的组件的名称
 * @param eventName // 事件名称
 * @param params // 需要传递的参数
 */
function broadcast(componentName, eventName, params) {
    // 循环子节点找到名称一样的子节点 否则 递归 当前子节点
    this.$children.map(child=>{
        if (componentName===child.$options.name) {
            child.$emit.apply(child,[eventName].concat(params))
        }else {
            broadcast.apply(child,[componentName,eventName].concat(params))
        }
    })
}
export default {
    methods: {
        /**
         * 派发 (向上查找) (一个)
         * @param componentName // 需要找的组件的名称
         * @param eventName // 事件名称
         * @param params // 需要传递的参数
         */
        dispatch(componentName, eventName, params) {
            let parent = this.$parent || this.$root;//$parent 找到最近的父节点 $root 根节点
            let name = parent.$options.name; // 获取当前组件实例的name
            // 如果当前有节点 && 当前没名称 且 当前名称等于需要传进来的名称的时候就去查找当前的节点
            // 循环出当前名称的一样的组件实例
            while (parent && (!name||name!==componentName)) {
                parent = parent.$parent;
                if (parent) {
                    name = parent.$options.name;
                }
            }
            // 有节点表示当前找到了name一样的实例
            if (parent) {
                parent.$emit.apply(parent,[eventName].concat(params))
            }
        },
        /**
         * 广播 (向下查找) (广播多个)
         * @param componentName // 需要找的组件的名称
         * @param eventName // 事件名称
         * @param params // 需要传递的参数
         */
        broadcast(componentName, eventName, params) {
            broadcast.call(this,componentName, eventName, params)
        }
    }
}

3.async-validator

не знаюasync-validatorВы можете перейти на официальный сайт, чтобы увидетьgithub

yarn add async-validator // 因为当前这个插件是需要打包到项目里的所以不能加-D

4. Дизайн API

Давайте посмотрим нижеelementИзображение официального сайта`

  1. formЕсть 2 введенных поля:rulesправила и:modelЗначение текущей формы будет переданоmodelстоимость иrulesсовпадение для проверки.
  2. form-itemЕсть 2 введенных поляlableа такжеprop(prop) должен прийти иformсопоставьте, чтобы получить текущийform-itemзначение
  3. inputНа самом деле существуют текущие@inputМетоды.v-modelя не буду объяснять

form

  1. мы вformСначала начните вводить все текущиеform-itemэкземпляр (получить)
  2. createdМетоды, которые связывают и удаляют текущий экземпляр в начале жизненного цикла. Обычно привязки вызываются перед запуском домена страницы и должны бытьdomЗавершена загрузка
  3. provideИспользуйте с inject, чтобы дочерние компоненты могли вызывать методы и данные текущего родительского компонента.
  4. Замечания написаны ниже и их можно есть с уверенностью (можно проверить после тестирования)
form.vue
<template>
    <form>
        <slot></slot>
    </form>
</template>

<script>
    export default {
        name: "aiForm",
        provide(){ //  [不懂的可以看看](https://cn.vuejs.org/v2/api/#provide-inject)
            return {
                form: this
            }
        },
        props: {
            // 当前 form 的model
            model: {
                type: Object
            },
            // 验证
            rules: {
                type: Object
            }
        },
        data(){
            return{
                fields: [] // 储存当前的 form-item的实例
            }
        },
        created(){
            // 存当前实例
            let that =this;
            this.$on('on-form-item-add',item=>{
                if (item) {
                    that.fields.push(item)
                }
            });
            // 删除当前有的实例
            this.$on('on-form-item-remove',item=>{
                if (item.prop) {// 如果当前没有prop的话表示当前不要进行删除(因为没有注入)
                    that.fields.splice(that.fields.indexOf(item),1)
                }
            })
        },
        methods:{
            /**
             * 清空
             */
            resetFields(){//添加resetFields方法使用的时候调用即可
                /**
                 * 当前所有当form-item 进行赋值
                 */
                this.fields.forEach(field => {
                    field.resetField();
                });
            },
            /**
             * 校验 公开方法:全部校验数据,支持 Promise
             */
            validate(callback){
                return new Promise(resolve=>{
                    /**
                     * 当前所有当form-item 进行校验
                     */
                    let valid = true; // 默认是通过
                    let count = 0; // 来匹配当前是否是全部检查完
                    this.fields.forEach(field => {
                        // 每个实例都会有 validation 的校验的方法
                        field.validation('',error=>{
                            // 只要有一个不符合那么当前的校验就是未通过的
                            if (error) { 
                                valid = false;
                            }
                            // 通过当前检查完所有的form-item的时候才会调用
                            if (++count === this.fields.length) {
                                resolve(valid);// 方法使用then
                                if (typeof callback === 'function') {
                                    callback(valid);// 直接调用注入的回调方法
                                }
                            }
                        });
                    });
                })
            }
        }
    }
</script>

5.form-item

  1. form-itemЭто сложнее, давайте поговорим об этом по порядку
  2. isRequiredопределить, требуется ли это в настоящее время
  3. validateStateопределить текущий статус проверки
  4. validateMessageтекущее значение ошибки
  5. inject: ['form']мы можем пройтиthis.from.xxxдля вызова события и значения родительского компонента
  6. computedвнизfieldValueМожет постоянно меняться, поэтому мы используем вычисляемые свойства
  7. initialValueЗначение по умолчанию, которое мы имеем вmountedкогда и когда в настоящее время требуется проверка (propиногда) будет назначать
  8. mixins: [Emitter]Микшер — это метод внутри, а дата может часто использоваться в текущем вызове и может быть помещена в микшер.
  9. насform-itemбудет передан вinputдва методаblurа такжеchange(ввод используется изначально@input)пройти черезformвходящая контрольная суммаrulesвнутриtriggerсудить
form-item.vue
<template>
    <div>
        <label :class="isRequired?'ai-form-item-label-required':''">{{label}}</label>
        <div>
            <slot></slot>
            <div class="ai-form-item-message" v-if="validateState==='error'">{{validateMessage}}</div>
        </div>
    </div>
</template>

<script>
    import Emitter from '../../mixins/emitter';
    import schema from 'async-validator';
    export default {
        name: "aiFormItem",
        mixins: [Emitter],
        inject: ['form'],
        props: {
            label: {
                type: String,
                default: ''
            },
            prop:{
                type: String
            },
        },
        computed:{
            fieldValue () {
                return this.form.model[this.prop];
            },
        },
        data(){
            return {
                initialValue: '', // 储存默认值
                isRequired: false, // 当前的是否有问题
                validateState: '', // 是否校验成功
                validateMessage: '', // 校验失败文案
            }
        },
        methods:{
            /**
             * 绑定事件 进行是否 required 校验
             */
            setRules(){
                let that = this;
                let rules = this.getRules();//拿到父组件过滤后当前需要使用的规则
                if (rules.length) {
                    // every 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)
                    // some 只要有一个符合就返回true
                    this.isRequired = rules.some(rule=>{
                        // 如果当前校验规则中有必填项,则标记出来
                        return rule.required;
                    })
                }
                /**
                 * blur 事件
                 */
                this.$on('on-form-blur',that.onFieldBlur);
                /**
                 * change 事件
                 */
                this.$on('on-form-change',that.onFieldChange)
            },
            /**
             * 从 Form 的 rules 属性中,获取当前 FormItem 的校验规则
             */
            getRules () {
                let that = this;
                let rules = that.form.rules;
                rules = rules?rules[that.prop]:[];
                return [].concat(rules||[])//这种写法可以让规则肯定是一个数组的形式
            },
            /**
             * Blur 进行表单验证
             */
            onFieldBlur(){
                this.validation('blur')
            },
            /**
             * change 进行表单验证
             */
            onFieldChange(){
                this.validation('change')
            },
            /**
             * 只支持 blur 和 change,所以过滤出符合要求的 rule 规则
             */
            getFilteredRule (trigger) {
                let rules = this.getRules();
                // !res.trigger 没有调用方式的时候默认就校验的
                // filter 过滤出当前需要的规则
                return rules.filter(res=>!res.trigger || res.trigger.indexOf(trigger)!==-1)
            },
            /**
             * 校验数据
             * @param trigger 校验类型
             * @param callback 回调函数
             */
            validation(trigger,callback=function () {}){
                // blur 和 change 是否有当前方式的规则
                let rules = this.getFilteredRule(trigger);
                // 判断当前是否有规则
                if (!rules || rules.length === 0) {
                    return
                }
                // 设置状态为校验中
                // async-validator的使用形式
                this.validateState = 'validating';
                var validator = new schema({[this.prop]: rules});
                // firstFields: true 只会校验一个
                validator.validate({[this.prop]: this.fieldValue}, { firstFields: true },(errors, fields) => {
                    this.validateState = !errors ? 'success' : 'error';
                    this.validateMessage = errors ? errors[0].message : '';
                    callback(this.validateMessage);
                });
            },
            /**
             * 清空当前的 form-item
             */
            resetField(){
                this.form.model[this.prop] = this.initialValue;
            }
        },
        // 组件渲染时,将实例缓存在 Form 中
        mounted(){
            // 如果没有传入 prop,则无需校验,也就无需缓存
            if (this.prop) {
                this.dispatch('aiForm','on-form-item-add', this);
                // 设置初始值,以便在重置时恢复默认值
                this.initialValue = this.fieldValue;
                // 添加表单校验
                this.setRules()
            }
        },
        // 组件销毁前,将实例从 Form 的缓存中移除
        beforeDestroy(){
            this.dispatch('iForm', 'on-form-item-remove', this);
        },
    }
</script>

<style scoped>
    <!--当前css-->
    .ai-form-item-label-required:before{
        content: '*';
        color: red;
    }
    .ai-form-item-message {
        color: red;
    }
</style>

5.input

  1. valueподдержка одного входа
  2. потому что в настоящее времяinputВведенные параметры не могут быть размещены напрямуюinputОн используется в нем, поэтому сначала назначаетсяdefaultValueзатем используйтеwatchприходи и уходиdefaultValueНазначение достигает привязки, измененной родительским компонентом
<template>
   <input type="text"
          @input="handleInput" // change
          @blur="handleBlur"
          :value="defaultValue"
   >
</template>

<script>
   import Emitter from '../../mixins/emitter.js'
   export default {
       name: "aiInput",
       mixins: [Emitter],
       props: {
           value: {
               type: String,
               default: ''
           }
       },
       data(){
           return {
               defaultValue: this.value
           }  
       },
       watch:{
           value (val) {
               this.defaultValue = val;
           }
       },
       methods:{
           /**
            * change 事件
            * @param event
            */
           handleInput(event){
               // 当前model 赋值
               this.defaultValue = event.target.value;
               // vue 原生的方法 return 出去
               this.$emit('input',event.target.value);
               // 将当前的值发送到 aiFormItem 进行校验
               this.dispatch('aiFormItem','on-form-change',event.target.value)
           },
           /**
            * blur 事件
            * @param event
            */
           handleBlur(event){
               // vue 原生的方法 return 出去
               this.$emit('blur',event.target.value);
               // 将当前的值发送到 aiFormItem 进行校验
               this.dispatch('aiFormItem','on-form-blur',event.target.value)
           }
       }
   }
</script>

наконец

Наконец, дайте последний доступный метод использования

<template>
 <div class="home">
   <button @click="changeButton">测试</button>
   <ai-form ref="formItems" :model="formValidate" :rules="ruleValidate">
     <ai-form-item label="用户名" prop="name">
       <ai-input  v-model="formValidate.name"/>
     </ai-form-item>
   </ai-form>
 </div>
</template>

<script>
 import AiForm from "../components/form/form";
 import AiFormItem from "../components/form/form-item";
 import AiInput from "../components/input/ai-input";
export default {
   name: 'home',
   components: {AiInput, AiFormItem, AiForm},],
   data(){
       return{
           formValidate: {
               name: '123z',
               mail: ''
           },
           ruleValidate: {
               name: [
                   { required: true, message: '用户名不能为空', trigger: 'blur' },
               ],
           }
       }
   },
   methods:{
       changeButton(){
           this.$refs.formItems.resetFields() // 清空方法
           this.$refs.formItems.validate() // 验证方法
               .then(res=>{
                   console.log(res)
               })
       }
   },
}
</script>

резюме

Может быть, мои друзья до сих пор не понимают. . Как говорится, мастер ведет дверь, а практика в человеке. Замечаний по коду достаточно. Если вы все еще не понимаете, вы можете присоединиться к группе и спросить своих друзей,

Если вы ищете надежный внутренний толчок (район Пекина), вы можете оставить сообщение +. знак равно