共计 3630 个字符,预计需要花费 10 分钟才能阅读完成。
设计指标
配置化
咱们心愿把表格的内容,验证规定, 甚至于表单的款式,格局都能更规则化,配置化,这样后续咱们能够通过结构 json 去实现一个表单,甚至可用实现拖拽式的结构表单。
参数简略
尽量减少 json 的层级,缩小 json 的参数,字段更加语义化。
自由度
json 其实是一套自由度的很少的规定,然而 vue 则咱们提供更多的自由度,比方 h 函数,比方动静组件,利用这些办法咱们能够实现更高的自由度。
我的实现过程
表单项的格局设计
首先第一步,咱们先设计一个根底的格局, 在这个 JSON 里,字段名都是很简略的英文单词,我专门把验证的规定 rule 放到每个子项里来,这也比拟合乎直观。
const oneItem = {
key: 'title',
title: '小说名',
component: 'el-input',
props: {placeholder: '请输出姓名'},
rule: [{required: true, trigger: 'blur', message: '必填项'}],
}
复制代码
在这个格局外面,比拟重要的次要是 2 个,key,component。key 其实就是你表单里数据的字段名,而 component 则是你指定的编辑组件,在这里咱们能够间接应用字符串,但其实这里能够通过 vue 的动静组件实现更灵便的利用,比方咱们换一个组件库的 input 组件
import {Input} from ‘@varlet/ui’
import ‘@varlet/ui/es/input/style/index.js’
const oneItem = {component: Input}
复制代码
这时候,咱们就须要动静组件去渲染它,因而咱们能够这样写去渲染,当 component 是一个字符串,比方 el-input 的时候,咱们渲染 element 的 input 组件,至于 v -model 这些我就省略了
<el-form-item v-for=”item in items” :key=”item.key”>
<el-input v-if=”item.component === ‘el-input'” />
<component v-else :is=”item.component” />
</el-form-item>
复制代码
v-bind 的妙用
每个组件库的组件参数都不一样,而且有些属性咱们可能并不应用,比方 el-input 有这个属性 prefix-icon,是一个前缀图标, 别的组件库不肯定有啊,那到咱们须要把所有组件库的所有属性都写在 json?
我在之前的 json 中设计了以个 props 字段,这外面就是寄存的是组件库的属性,或者是咱们须要给组件传的值.
这时候,vue 给咱们提供了一个很不便的性能,间接应用 v -bind 传入一个对象,他就主动会帮咱们把属性绑定。
比方这样写
const props = {a:1,b:2}
<el-input v-if=”item.component === ‘el-input'” v-bind=”props” />
复制代码
vue 就会主动解决为上面这种, 这就是 v -bind 的妙用。当然使用 renderFunction 也能够实现这个成果,诸君能够本人尝试一下
<el-input v-if=”item.component === ‘el-input'” v-bind:a=”props.a” v-bind:a=”props.b”/>
复制代码
computed 的妙用:实现 v -model
上面咱们来看一下数据的问题,vue 中提供了不便 v -model,不便咱们批改的值能实时响应, 并且咱们能够本人实现一自定义 v -model。
它的基本原理是这样,咱们先父传子,而后子再通过事件通知父组件批改这个值。大略实现就是这样
<script>
<button>+1</button>
</script>
export default{
props:[
'modelValue', //v-model
'a' //v-model:a
],
emits:['update:modelValue','update:a'],
methods:{add(){this.$emit('update:modelValue',this.modelValue++)
this.$emit('update:a',this.a++)
}
}
}
复制代码
然而这个代码里有一个问题,在 vue 中咱们其实是无奈批改 props 的,也就是说 this.modelValue++ 会报错,那么如何解决这个问题呢,答案就是 computed,computed 其实也能够批改的,咱们能够指定它的 set 办法, 这样就规避了批改 props 的问题,从而实现了 v -model
{
computed:{
num:{get(){return this.modelValue},
set(val){this.$emit('update:modelValue',val)
}
}
}
}
复制代码
useAttrs 的妙用
在我的组件中有这样一个性能,上传。这就波及到了回调函数的问题,也就是说我上传完,甚至包含办法的名字,这样才更灵便,比方咱们在 json 中新增一个字段,
{
uploader: {
emits: 'handleUploadCover',
}
}
复制代码
而后我在渲染的时候会给它绑上这个事件, 那么咱们如何获取到这个事件的函数,并调用呢?
<zForm @handleUploadCover=”xxx” />
复制代码
在 vue3 中,我应用了 useAttrs, 须要留神的是 vue3 这里仿佛与 vue2 有些不同。vue3 中,attrs 获取到的是没有注册的值,比方你如果在 emits 里申明了,在这里就取不到了,不过这也正合我意,咱们能够随便指定事件名。
const attrs = useAttrs()
/*
返回值
{
onHandleUploadCover:function(){xxx}
}
*/
复制代码
能够看到这里能获取事件,只是名字略有不同,这里大家解决一下就行了
表单验证
表单里最重要的就是验证. 首先在我之前的设计中,表单验证的规定是散布在每一个子项中,因而咱们须要整合一下,这一块我就不赘述了,也很简略。
验证办法我是间接应用的 el-form 的验证, 只是封装了一下罢了。
须要留神的是,如果你用的是 script setup,须要应用 defineExpose 导出这个办法
const validate = ()=> new Promise((resolve) => {
this.$refs.form.validate((isValid) => resolve(isValid));
})
defineExpose({
validate
})
复制代码
上传文件
上传文件这里我其实截取了一下 element 的上传,只应用了它抉择的文件的性能,这块其实能够本人实现的。
因为我上传两头还要加很多参数,还有验证,因而我应用了 before-upload 办法,并被动 reject.
<el-upload
v-if="item.uploader"
style="margin-top: 10px"
:before-upload="(file) => beforeUpload(file, item)"
:show-file-list="false"
v-bind="item.uploader.props"
>
<el-button type="primary"> 点击上传 </el-button>
</el-upload>
const beforeUpload = (rawFile, { key, uploader}) => {
/* 执行逻辑,其实就是调起 uploader.emits 里的办法 */
return Promise.reject()
}
复制代码
代码总结
我把 demo 放到了这里,后续有工夫我整顿一下发个 npm 包。
stackblitz.com/edit/vue-m8…
这次封装这个组件,我学到了很多货色,一些比拟轻微的 vue3 知识点,比方 v -bind。但我也晓得这也封装也有一些问题或者叫争执。
到底应不应该应用 json
之前看过一篇封装 el-table 的文章,外面就拥护应用 json,起因无非 2 点:json 构造过于宏大,json 构造不利于接手代码的人应用。
先说第二点,我感觉通过一个好的构造定义是能够缓解这个问题的, 然而难道你函数式封装就没有学习老本了?我感觉 json 封装其实每次就是复制黏贴,反而学习老本更低,然而开发成本会更高,你须要解决各种谬误的值,谬误的构造,因而构造越简略越好,甚至能够拍平。
json 并不宏大,宏大的是咱们的表单,如果你表单里几百个条目,你怎么样写都只会宏大,因而还是倡议宰割表单,及时上报。
需不需要 v -model
在我这次封装中,我把数据通过 v -model 实时返回了,然而当我写到结尾的时候,我感觉表单的数据并不需要实时,因为咱们须要的不是实时的数据,而是验证后的正确数据。因而我感觉咱们能够暴露出一个 getData 办法,返回验证正确的数据。
性能问题
理论应用中,我发现这样封装仿佛有点卡,目前临时不晓得是哪里的问题,有待钻研