当前位置:首页 > 系统运维

如何手写El-Form表单组件

本文转载自微信公众号「前端有道」,何手作者星野。单组转载本文请联系前端有道公众号。何手

前言

在刚入行时候,单组只会知道如何使用表单组件,何手在后面一两年工作中也没有什么技术积累成为一个工具人,单组操作最多的何手就是ctrl+c和ctrl+v,在去年进了一家新公司,单组这家公司以前旧项目代码经过太多人的何手手,代码已经快不成人样了,单组难以维护,何手技术人员已经跑的单组差不多了,我进去好在让我们负责新的何手项目开发,要不然可能第二天就看不到我了,单组哈哈。何手项目主要面向于小程序和H5端,网上的UI库很难满足产品后续规划需求开发,只好开始研究组件原理及封装组件。

最近又个项目让我有开始接触element-ui,想到当初对el-form表单有一些困惑,查看一下源码和一些技术文章,对el-form有一些新的认识。

Form 表单

下面是一份el-form示例代码

<template>   <el-form :model="ruleForm" :rules="rules" ref="ruleForm">     <el-form-item label="名字" prop="pass">       <el-input type="password" v-model="ruleForm.pass"></el-input>     </el-form-item>     <el-form-item label="年龄" prop="age">       <el-input v-model.number="ruleForm.age"></el-input>     </el-form-item>     <el-form-item>       <el-button type="primary" @click="submitForm(ruleForm)">提交</el-button>       <el-button @click="resetForm(ruleForm)">重置</el-button>     </el-form-item>   </el-form> </template> <script> export default {    data() {      return {        ruleForm: {          pass: ,         checkPass: ,         age: ,       },       rules: {          pass: [{  required: true, message: 请输入名字, trigger: blur }],         age: [{  required: true, message: 请输入年龄, trigger: blur }],       },     }   },   methods: {      submitForm(formName) {        this.$refs[formName].validate(valid => {          if (valid) {            alert(submit!)         } else {            console.log(error submit!!)           return false         }       })     },     resetForm(formName) {        this.$refs[formName].resetFields()     },   }, } </script> 

首先要清楚一下组件的使用方式

1.el-form接收model和rule两个prop

model表示表单绑定的数据对象 rule表示验证规则策略,表单验证

2.el-form-item接收的服务器租用prop属性,对应form组件的model数据中某个key值,如果rule刚好有key,给定的条件下去如失去焦点验证规则匹不匹配。

最终得到类似这样代码结构

<template>   <div>     <form @submit.prevent>       <div>         姓名<input v-model="form.name" />       </div>       <div>         年龄<input v-model="form.age" />       </div>       <div>         <button @click="submit">提交</button>       </div>     </form>   </div> </template> 

手写表单组件

组件中嵌套组件,主要是通过slot插槽,可以将组件拼接成上面代码结构。代码如下

el-form

<template>     <form>         <slot></slot>     </form> </template> <script> export default {      name:elForm } </script> 

el-form-item

<template>     <div>         <slot></slot>     </div> </template> <script> export default {      name:elFormItem } </script> 

el-input

<template>     <input type="text"> </template> <script> export default {      name:elInput } </script> 

 接下来就要考虑到组件中的通讯。由于组件中有可能嵌套很多的组件,如果单纯通过$parent和$children查找出来的父级组件,不一定是el-form组件。

两个问题:

el-form-item组件如何得到el-form的数据 el-form组件如何和el-form-item进行交互

解决第一问题,可以通过provide与inject实现。解决第二问题,就要讲到dispatch派发和broadcast广播

provide与inject

通过provide将当前表单实例传递到所有后代组件中,后代通过inject接受传递的值。

el-form

<template>     <form><slot></slot></form> </template> <script> export default {      name:elForm,     provide(){          return {              elForm: this         }       },     props:{          model:{              type:Object,             default:()=>({ })         },         rules:Object     } } </script> 

el-form-item

<template>     <div><slot></slot></div> </template> <script> export default {      name:elFormItem,     inject:[elForm],     props:{          label:{               type:String,             default:         },         prop:String      },     mounted(){         console.log(this.elForm)     } } </script> 

provide中this指el-form组件,this.elForm就能得到el-form组件中的数据和方法。

dispatch和broadcast广播

$dispatch与$broadcast是一种有历史的组件通信方式,因为他们是Vue1.0提供的高防服务器一种方式,在Vue2.0中废弃了。

$dispatch: $dispatch会向上触发一个事件,同时传递要触发的祖先组件的名称与参数,当事件向上传递到对应的组件上时会触发组件上的事件侦听器,同时传播会停止。

$broadcast: $broadcast会向所有的后代组件传播一个事件,同时传递要触发的后代组件的名称与参数,当事件传递到对应的后代组件时,会触发组件上的事件侦听器,同时传播会停止(因为向下传递是树形的,所以只会停止其中一个叶子分支的传递)

$dispatch

/**  * 派发 (向上查找) (一个)  * @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))         }     }, 

$broadcast

/**  * 派发 (向上查找) (一个)  * @param componentName // 需要找的组件的名称  * @param eventName // 事件名称  * @param params // 需要传递的参数  */ 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))     } }) 

验证表单

async-validator是一个表单的异步验证的第三方库,也是element-ui 中的form组件所使用的验证方式。 

el-form-item

<template>   <div>     <label v-if="label">{ { label}}</label>     <slot></slot>     { { errorMessage}}   </div> </template> <script> import Schema from "async-validator"; export default {    name: "elFormItem",   inject: ["elForm"],   props: {      label: {        type: String,       default: ""     },     prop: String   },   data(){        return { errorMessage:}   },   mounted() {      this.$on("validate", () => {        if (this.prop) {          let rule = this.elForm.rules[this.prop];         let newValue = this.elForm.model[this.prop];         let descriptor = {            [this.prop]: rule         };         let schema = new Schema(descriptor);         return schema.validate({ [this.prop]:newValue},(err,res)=>{              if(err){                  this.errorMessage = err[0].message;             }else{                  this.errorMessage =              }         })       }     });   } }; </script> 

分享到:

滇ICP备2023006006号-16