使用组件就像流水线上的工人;设计组件就像设计装配线的人,是为工人使用而设计的。
完整的项目地址:通过模仿元素实现一个表单
一.目标
模仿ElementUI实现一个简单的Form表单,主要实现以下四点:
表单表单项目输入表单验证我们先来看看表单表单表单在ElementUI中的基本用法
El-Form : model=' rule Form ' : rules=' rules ' ref=' log in Form ' El-Form-item label=' user name ' prop=' name ' El-input v-model=' rule Form . name '/El-input/El-Form-item El-Button type=' primary ' @ click='
二.创建项目
我们通过vuecli 3.x创建了这个项目。
使用vue create e-form创建目录。
使用npm运行服务启动项目。
三.表单组件设计
ElementUI中的表单叫做el-form,我们设计的表单叫做e-form。
为了实现电子表单表单,我们参考了ElementUI的表单用法,总结了以下需要设计的功能。
E-form负责全局验证并提供槽位;E-form-item负责检查单个项目,显示错误消息并提供插槽;E-input负责数据双向绑定;1.输入的设计
让我们首先看看ElementUI中的输入组件:
El-input v-model=' ruleform。name'/El-input在上面的代码中,我们发现输入标签可以实现一个双向数据绑定,而要实现双向数据绑定,我们需要在输入标签上做两件事。
为了绑定值并响应输入事件,当我们完成这两件事时,我们可以完成v模型的语法糖。
我们创建一个输入文件:
模板div!-1.绑定值2。响应输入事件-input type=' text ' : value=' value in input ' @ input=' handleinput '/div/Templatescriptexportdefault { name : ' einput ',prop : { value : {//解释一个type:string,default3360 ' ',}},data(){ return { value in put 3360 this。值//解释两个};},methods : { handleInput(event){ this . valuein input=event . target . value;//解释三个这个。$ emit ('input ',这个。valuein input);//解释四}},};/script让我们稍微解释一下上面的代码:
* *解释1: *由于我们希望成为一个Input组件,接收到的值必须由父组件传入,当父组件没有传入值时,我们可以有一个默认值' '。
* *说明2: *设计组件时,要遵循单向数据流的原则:只能使用父组件传入的值,不能更改。然后将从父组件传入的值赋给输入组件中的值Input。如果该值发生变化,我们将修改输入中的内部值。这样,我们不仅可以处理数据更改,还可以直接修改父组件传入的值。
* *解释3: *当输入中的值发生变化时,会触发@input事件。此时,我们通过event.target.value获取已更改的值,并将其重新分配给input中的内部值。
* *说明4: *完成内部分配后,我们需要做的是将更改的值通知父组件。这里,我们用这个。$emit向上调度事件。第一个参数是事件名称,第二个参数是更改后的值。
完成以上四个步骤后,设计了一个简单的双向数据绑定的Input组件。此时,我们可以将Input组件引入App.vue来观察结果。
模板div id=' app ' e-input v-model=' init value '/e-input div { { init value } }/div/div/templatescriptimport EInput from '。/components/Input . vue ';导出默认{ name: 'app ',components: { EInput },data(){ return { initvalue : ' 223 ',};},};/脚本
2.FormItem的设计
El-form-item label=' user name ' prop=' name ' El-input v-model=' rule form。名称'/El-input/El-form-item在ElementUI的formItem中,我们可以看到:
标签是显示名称所必需的;需要Prop来验证当前项目;需要为输入或按钮预留插槽;根据上面的要求,我们可以创建自己的formItem,并创建一个新的FormItem.vue文件。
模板div!-解释一-标签v-if='标签“{label}}/labeldiv!-解释双槽/槽!-解释三-p v-if=' validate state==' error ' ' class=' error ' { { validate message } }/p/div/div/template脚本导出默认值{ name: ' eformatitem,prop : { label : { type : String,default: '' },prop: { type: String,default 3: ' ' },data(){ return { validate state : ' ',validate}/style与上面相同,我们接下来将解释上面的代码:
* *说明1: *根据ElementUI中的用法,我们知道标签是从父组件传入的,传入的时候会显示,传不进去的时候不会显示。
说明2:插槽是预留的插槽,我们可以在里面放输入或者其他的组件和元素。
说明3:p标签用于显示错误信息,如果验证状态为错误,则会显示。
此时,我们的FormItem组件也可以使用。同样,我们在App.vue中引入了这个组件
模板div id=' app' e-form-item标签=' user name ' prop=' name ' E-input v-model=' rule form . name '/E-input/E-form-item E-form-item标签=' password ' prop=' pwd ' E-input v-model=' rule form . pwd '/E-input/E-form-item div { { rule form } }/div/div/templatest import E from '。/components从“”导入EFormItem。/components/formitem . vue ';导出默认{ name: 'app ',components: { EInput,EFormItem },data(){ return { Ruleform : { name : ' ',pwd: ' ',},};},};/脚本
3.形式设计
到目前为止,我们已经完成了最里面的输入和中间层FormItem的设计,现在我们开始设计最外面的Form组件。
当层次太多,组件之间需要数据传输时,Vue为我们提供了提供和注入API,方便我们跨层次传输数据。
让我们举一个简单实现提供和注入的例子。在App.vue中,我们提供数据。
导出默认的{name:' app ',提供(){return {msg: '是最外层的数据' } } };/script接下来,我们将数据注入最里面的Input.vue并观察结果。
模板div!-1.绑定值2并响应输入事件-input type=' text ' : value=' valuein input ' @ input=' handleinput ' div { { msg } }/div/div/templatescript export default { name : ' EInput ',input :[' msg '],prop : { value : { type 3: String,default: ' ',},data(){ return { valuein put : this . value };},methods : { handleInput(event){ this . valuein input=event . target . value;这个。$emit('input ',this . valueinput);} },};/脚本
根据上图,我们可以看到,无论跨越多少层,提供和注入都可以非常方便地传输数据。
了解以上知识点后,我们就可以开始设计表单组件了。
El-form : model=' rule form ' : rules=' rules ' ref=' loginform '/El-form根据表单在ElementUI中的用法,我们知道表单组件需要实现以下功能:
提供数据模型模型;提供检查规则;提供放置FormItem和其他组件的插槽;根据上述要求,我们创建了一个表单组件:
模板表单插槽/插槽/表单/模板脚本导出默认{name:' eform ',prop : {//解释一个模型: {type: Object,Required : True},规则: {Type: Object}},Provide(){//解释2返回{eForm: this //解释3 } }/脚本解释1:此组件要求用户传入一个Object类型的数据模型。规则是可转移的项目。
说明2:为了使各级能够使用表单中的数据,需要依靠提供功能来提供数据。
解释3:直接传递组件的实例。
完成了表单组件的设计,我们在App.vue中使用了它:
模板div id=' app ' E-form : model=' rule form ' : rules=' rules ' E-form-item标签=' user name ' prop=' name ' E-input v-model=' rule form . name '/E-input/E-form-item E-form-item E-form-item标签=' password ' prop=' pwd ' E-input . pwd '/E-input/E-form-item E-form-item按钮提交/按钮/E-form-item/item/components/Input . vue ';从“”导入EFormItem。/components/formitem . vue ';从“”导入from。/组件/表单';导出默认的{ name: 'app ',组件: { EInput,EFormItem,EForm },data(){ return { Ruleform : { name : ' ',pwd: ' ',},rule 3360 { name :[{ required : true }],pwd: [{ required: true }],};},};/脚本
到目前为止,我们的基本功能已经实现。除了提交和验证规则之外,所有组件几乎都与ElementUI中的表单相同。接下来,我们开始实现验证功能。
4.设计检查规则
在上面设计的组件中,我们知道验证当前项目和显示错误信息的工作在FormItem组件中,但是数据的变化在Input组件中,所以FormItem和Input组件有数据传输。当输入中的数据发生变化时,告诉FormItem,让FormItem验证,并显示错误。
首先,让我们修改输入组件:
methods : { handlerInput(event){ this . valueinput=event . target . value;这个。$emit('input ',this . valueinput);//数据已更改,直接通知FormItem检查this.dispatch ('eformatitem ',' validate ',this . value input);},//查找具有指定名称的组件,dispatch(组件名、事件名、params) {var parent=this。$ parent | |这个。$ rootvar name=parent。$ options.namewhile (parent(!名字||名字!==component name)){ parent=parent。$ parentif (parent) { name=parent。$ options.name} } if (parent) { parent。$emit.apply(父级,[eventName]。concat(params));}}}这里,我们不能使用这个。$emit直接调度事件,因为Input组件在FormItem组件中的位置只是一个槽,所以不能做事件监控,所以让FormItem调度事件,自己监控。修改FormItem组件,并侦听创建的事件。
创建了(){this。$on('validate ',this . validate);}当Input组件中的数据发生变化时,FormItem组件会在监听到validate事件后执行validate函数。
接下来,我们将处理我们的验证函数。在ElementUI中,底层库异步验证器用于验证,我们可以通过npm安装这个包。
NPM I异步验证器异步验证器是一个可以对数据执行异步验证的库。具体用法请参考以上链接。我们通过这个库完成验证功能。继续查看FormItem.vue文件:
template div label v-if=' label " { label } }/label div slot/slot p v-if=' validate state=' error ' ' class=' error ' { validate message } }/p/div/div/template scription来自' async-validator '的异步验证程序;导出默认值{ name: 'EFormItem ',prop : { label : { type : String,default: '' },prop: { type: String,default: '' },inject :[' eFOrmitem '],//解释一创建了(){这个.$on('validate ',此。验证);},已安装(){ //解释二if (this.prop) { //解释三这个。派单(' eFrOm ',' addFiled ',this);} },data(){返回{验证消息: ' ',验证状态: ' ' };},methods: { validate() { //解释四返回新的承诺(解析={ //解释五const descriptor={//name : this。形式。规则。name=//name :[{要求:为真},{ 0.} ] };描述符[这个。prop]=这个。eFrOm。规则[这个。道具];//校验器const validator=新的AsyncValidator(描述符);const model={ };模型[这个。prop]=这个。eFrOm。模型[这个。道具];//异步校验validator.validate(模型,错误={ if(errors){ this。验证状态=“错误”;this.validateMessage=errors[0]。消息;解决(假);} else { this . validate state=this . validate message=决心(真);} });});}, //查找上级指定名称的组件调度(组件名、事件名、参数){ var parent=this .$parent ||这个$ rootvar name=parent .$ options.namewhile (parent(!名字||名字!==组件名)){ parent=parent .$ parentif (parent) { name=parent .$ options . name } } if(parent){ parent .$emit.apply(父级,[事件名称].concat(params));} } }};/script样式作用域。错误{ color : red}/样式我们对上面的代码做一个解释。
解释一:注入形式组件提供的数据表示"具有…形状的“:十字形组件的实例,下面就可以使用这个。EcHo。XXX来使用形式中的数据了。
解释二:因为我们需要在形式组件中校验所有的FormItem,所以当表单项挂载完成后,需要派发一个事件告诉表单:你可以校验我了。
解释三:当表单项中有支柱属性的时候才校验,没有的时候不校验。比如提交按钮就不需要校验。
e-form-item输入类型=' submit ' @ click=' submit form()' value='提交/电子表格-项目**解释四:**返回一个承诺对象,批量处理所有异步校验的结果。
解释五:描述符对象是异步验证器的用法,采用键值对的形式,用来检查当前项。比如:
//检查当前项//异步验证器给出的例子name: {type: 'string ',required: true,validator:(规则,值)=value==='muji ',}FormItem中检查当前项完成了,现在我们需要处理一下形式组件中的全局校验。表单提交时,需要对形式进行一个全局校验。大致的思路是:循环遍历表单中的所有派发上来的FormItem,让每一个表单项执行自己的校验函数,如果有一个为假的,则校验不通过;否则,校验通过。我们通过代码实现一下:
模板表单插槽/插槽/表单/模板脚本导出默认值{ prop : { model : { type : Object,required: true },Rule: {type:object}},提供(){return {eform:this,//提供此组件的实例}},data () {return {fileds3360 [],},created(){//explain this . fileds=[]this。$ on(' addfield ',field=this . fileds . push(field));},methods : { async validate(CB){//解释2//解释3 const eachfiledersultry=this . fileds . map(field=field . validate());//解释四个常量结果=等待承诺。all(each filedersultry);让ret=trueresults.forEach(有效={ if(!有效){ ret=false} });CB(ret);}},}/script style lang=' SCS '作用域/样式说明1:使用fileds缓存要验证的表单项,因为我们在FormItem中调度了事件。只有需要验证的FormItem才会被调度到这里,并保存在数组中。
if(this . prop){ this . dispatch(' EcHo ',' addFiled ',this);}说明2:点击提交按钮会触发此事件。
说明3:遍历所有添加到文件中的FormItem项,让每一项单独验证,返回Promise的真或假。将所有结果放在一个数组中。
说明4:获取所有结果,统一处理。如果一个结果为假,验证将不会通过。
至此,模仿ElementUI的表单的简化版本已经实现。
四.摘要
当然,上面的代码还有很多需要优化的地方,比如dispatch函数,我们可以写一次,使用的时候用mixin导入。因为篇幅的关系,这里就不处理了。
通过这个实现,我们首先总结一下涉及到的知识点。
父组件被传递给子组件。事件由props子组件调度,跨级数据交互由$emit执行,slot可以通过提供和注入来预留,然后给出了一些思路:
单数据流:从父组件传递给子组件的值只能在子组件内部使用,不能修改。组件内部的名称属性可以通过这个找到。$家长。如果您想批处理许多异步结果,可以使用promise对象。最后文章会发表在我的Github和微信官方账号上。欢迎关注,欢迎明星。
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。