vue组件化设计思想

480 阅读2分钟

现在很多UI库都有很多的好用的组件库,那这写组件的设计原理也必须知道,今天实现一下自定义form表单组件

组件设计思想 :3个点

  1. 属性 :设计任何组件都需要传递一个属性
  2. 事件:组件内部对外通知的机制
  3. 组件可以扩展:组件内部不能写死,比如弹窗,不能只放按钮和文本,应该是用户想放什么放什么 ,slot插槽,需要分层 。

组件化设计思想实现

  1. 采坑 :
    • v-model 双向绑定表单元素的数据:实质是绑定了value、checked、selected属性

    • 父组件向子组件传递数据 , 可以使用 :name 进行传递 ,也可以直接绑定属性 target="yuhior",那么这样传递的就是yuhior的值 不是变量

      <Cart :name="name" :cart="cart" target="yuhior" ></Cart> 
      <!--使用:name这种形式进行数据的-->
      <script>
      import Cart from './components/Cart'
      export default {
          name:'app', //组件的名字
          components:{
            Cart
          },
          data(){
              return{
                  name:'开课吧',
                  cart:[]
              }
          },
      }
      </script>
      
    • 父组件是使用这个子组件的组件 ,而不是template里嵌套子元素的是父组件,比如说这里KInput、FormItem、KForm的父组件都是App.vue

      <!--App.vue-->
      <template>
        <div>
          <k-form :model="ruleForm" :rules=" rules">
            <form-item label="用户名" prop="name">   //父组件这是   *****理解错误 
              <k-input v-model="ruleForm.name" name="name"></k-input>  //开始我以为子组件
            </form-item>
            <form-item label="年龄" prop="age">
              <k-input v-model="ruleForm.age" name="age"></k-input>
            </form-item>
          </k-form>
        </div>
      </template>
      
      <script>
        import KForm from './components/Form';
        import FormItem from './components/FormItem';
        import KInput from './components/KInput';
        export default {
          name: 'app',
          components:{
            KForm,
            FormItem,
            KInput
          },
          data(){
            return{
              }
            }
          }
        }
      </script>
      
    • v-model 是一个特殊的属性绑定,相当于绑定了:value和@input两件事情

          <custom-input v-model="searchText"></custom-input>
          <custom-input 
                  :value="searchText"
                  @input="searchTex=$event"
          ></custom-input>
      
  2. form表单验证的设计实现

    表单组件进行分层:

    ​ KForm组件:负责接收自定义的规则

    ​ KFormItem:负责显示错误信息也就是规则的验证

    ​ KFromInput:负责数据的双向绑定

    • 子组件向父组件传递数据,通知父组件组件内部变化

      //子组件
      <template>
          <button @click="handleClick">
              开课吧
          </button>
      </template>
      <script>
          export default {
              methods: {
                  handleClick(event){
                      this.$emit('toMessage',event);
                  },   
              }
          }
      </script>
      
      
      // 父组件接收
      <template>
       	<k-input v-on:toMessage="toFuzhi"></k-input>  //toMessage 是子组件那个出发事件
      </template>
      <script>
          export default {
              methods: {
                   toFuzhi(msg){
      				 this.msg = msg  //进行父组件的复制
                  },  
              }
          }
      </script>
      
    • 先看看平常使用: 和lelement类似

      <template>
          <k-form :model="ruleForm" :rules="rules">
              <k-form-item label="用户名" prop="name">
                  <k-input v-model="ruleForm.name" name="name"></k-input>
              </k-form-item>
              <k-form-item label="年龄" prop="age">
                  <k-input v-model="ruleForm.age" name="age"></k-input>
              </k-form-item>
          </k-form>
      </template>
      <script>
          import KForm from './components/KForm';
          import KFormItem from './components/KFromItem';
          import KInput from './components/KInput'
          export default {
              name: 'app',
              components:{
                  KForm,
                  KFormItem,
                  KInput
              },
              data() {
                  return {
                      ruleForm:{
                          name:'',
                          age:''
                      },
                      rules: {
                          name:{required: true, message: '用户名不能为空!'},
                          age: {required: true, message: '年龄不能为空!'}
                      }
                  }
              }
          }
      </script>
      
    • KFormInput 实现: 需要注意 理解 this.$emit 传值的时候,因为 父组件 v-model 绑定了:value和@input两件事情 ,所以 使用的时候直接 v-model =“ruleForm.age” 就相当于 @input = ‘’ ’‘ 所以也就接收到了

      <template>
          <!--实现v-model-->
          <input
                  type="text"
                  :value="inputText"
                  @input="handleInput"
                  @blur="handleBlur"
          />
      </template>
      <script>
          export default {
              name: "KInput",
              props:{
                  value:{
                      type:String,
                      default:'',
                      require: true
                  },
                  name:{
                      type:String
                  }
              },
              data(){
                  return{
                      inputText:this.value  // * 双向输入的输入->父元素传递过来的
                  }
              },
              methods: {
                  handleInput(e) {
                      let value = e.target.value;  // props 是单向数据流传递数据,所以修改值之后,props的值不变,父元素的值不改变
                      this.inputText = value;   // 当输入框输入值的时候,获取输入框的值
                      // * 双向数据的输出-> 通知父元素进行回流绑定
                      this.$emit('input',value);  // 子组件向父组件传递数据 使用this.$emit('input') input事件触发  父组件使用@input接收
                      this.$bus.$emit('KFormItem', value); //将值通过总线机制,实现KInput和KFormItem不相关的组件的数据传递,传递给KFromItem值,它进行验证
                  },
                  handleBlur(){ //失去焦点是进行验证
                      let value = this.inputText;
                      this.$bus.$emit('KFormItem',value,);
                  }
              }
          }
      </script>
      
      
    • KFormItem 实现 :

      1. 我们接收到KInput发送过来的数据 , 然后进行 验证

         //----------KFromItem接收
        created() {
              this.$bus.$on('KFormItem', (value) => {  //接收KInput组件传递过来的值 进行调用validate方法进行验证
                  this.validate(value);  
              })
          },
        
      2. 需要验证 就需要 获取验证的规则

        //-----------KForm发送数据实例  
        provide(){  
             return {
             	kform:this
             }
         },
        //-----------KFormItem接收
        inject:['kform'],  //  2.接收KForm车通过  provide传递过来的 组件实例 这样才能获取到规则
        //-----------获取规则
        methods:{
            getRules(){ //获取规则的方法
                let formRules = this.kform.rules[this.prop];  //找到对应的 rules.prop的值
                return formRules;
            },
                
        }
        
      3. 获取了规则 就需要写 校验方法

        validate(obj){
            //1.验证我需要获取验证规则 ,也就是使用组件使用时传递进来的规则
            const rule = this.getRules(); //3.调用获取rules
            const value = this.kform.model[this.prop]; //4.由于在KInput组件中我们已经将输入框的变化值使用this.$bus.$emit通知给父组件了,所以这样获取输入框的值
            if (rule.required&&!value){  //5.进行验证 ,require为true ,!value为真(value的值为空)就说明验证未通过
                this.validateStatus = 'error';
                this.errorMessage = rule.message
            }else{                       //6.验证通过
                this.validateStatus = 'stating'  //验证状态为通过状态
            }
        
        }
        
    • KForm实现就简单了:给一个槽口 让KFormItem来占用就行了

      <template>
          <form>
              <!--子元素的插口-->
              <slot></slot>
          </form>
      </template>
      
      <script>
          export default {
              name: "KForm",
              provide(){   //给KFromItem传递规则 数据
                  return {
                      kform:this
                  }
              },
              // provide:{
              //     kform:this ;   也可以使用这种方式进行数据传递  ,但是我们现在需要返回this实例 所以需要使用provide:function(){}形式 但是如果返回变量就可以直接返回
              // },
              props:{
                  model:{
                      type:Object
                  },
                  rules:{
                      type:Object
                  }
              }
      
          }
      </script>
      
  3. 完成之后发现 不管输入哪一个输入框 都验证所有的验证框都响应,进行验证

    这是由于 我们再KInput.vue中使用 的emitemit 和.on都是全局的,所以父组件接收也就是@input =“ ”的时候都接收到了变化,也就是多个KForm监听@input事件 ,所以多个验证都响应 ,

    那么我们就判断一下

    // --------在KFormInput的$bus.$emit 发送值的时候不仅仅发送一个value ,我们发送一个对象  
    	this.$bus.$emit('KFormItem',{
            value,
            name:this.name
        });
    // --------在KFormItem验证中判断一下,如果发送过来的name和这个prop不相等,说明不是当前输入的input
        if (obj.name!==this.prop) {  
            return    //别的prop的 如果不是这个name则直接返回
        }
    
  4. 规则扩展

    • 但是如果我们想有多个验证规则怎么办 ?如下

      rules: {
           name: [  //如果有多个校验项 ,则验证需要修改
               {required: true, message: '用户名不能为空!'},
               {minLength: 3, message: "用户名长度要大于3"},
               {maxLength: 10, message: "用户名长度要小于10"},
           ],
               age: {required: true, message: '年龄不能为空!'}
       }
      
    • 那就在验证里边加判断被!!!

      //7.如果验证规则有多个,那就需要判断
      if (Array.isArray(value)){
          value.forEach(v=>{
              if(rule.required && !value){
      
              }
              if(rule.mixLength && value.length<rule.mixLength){
      
              }
          })
      }
      
  5. [源码]: