关于组件封装
vue组件系统提供一种抽象,让我们可以使用独立可复用的组件来构建大型应用,任意类型的应用界面可以抽象为一个组件树。组件化可以提高开发效率,方便重复使用,简化调试步骤,提升项目可维护性,便于多人协同开发。所以更多的大型项目中会封装一些组件来使用。
这里仿造element的form表单来写一个,更多的是学习组件封装的技巧,最重要的还是在具体项目中的刻意练习。
对于组件的功能分析,并分成多个组件
首先看这个组件的具体要实现怎么样的功能,比如element的form表单(后面直接写form表单,具体的功能也可以参照element),实现数据的收集,校验并且提交。但是将功能详细的切分:1.首先我们输入的或者通过获取来显示的数据我们要维护,这里需要一个Input的输入和数据显示的双向数据绑定的这里我们命名KInput,2.要执行校验,显示错误信息,添加label标签这里命名该组件为KformItem,3.指定数据校验规则,我们命名该组件为Kform
KInput组件的实现
<template>
<div>
<!-- 自定义组件双向绑定: :value @input -->
<!-- $attrs是将在父组件中绑定的元素并且在props中没有被生命的直接显示在子组件上 -->
<input :type="type" :value="value" @input="onInput" v-bind="$attrs">
</div>
</template>
<script>
export default {
inheritAttrs:false, //设置为false将属性的继承关闭掉
props:{
value:{
type:String,
default:''
},
type:{
type:String,
default:'text'
}
} ,
methods:{
onInput(e){
//派发一个input事件
this.$emit('input',e.target.value)
//值发生变化的时候,就是需要校验的时候
this.$parent.$emit('validate')
// 对于$emit来说就是谁派发,谁监听。这里可以通过父辈的派发,再由父辈监听
}
}
}
</script>
这里要实现的就是将传入的数据显示,并在页面修改数据的时候通过$emit将数据派发出去。
注意事项
这里采用了$attrs ,attrs就是将绑定在父组件上面的元素并且是没有通过props传递过来的直接显示。
同时也设置了inheritAttrs:false。将通过attrs传递的属性的继承性关闭。这个在使用的时候:
<KForm :model='userInfo' :rules='rules' >
<!-- 用户名 -->
<KFormltem label='用户名' prop="username">
<KInput placeholder="请输入用户名" v-model="userInfo.username"></KInput>
</KFormltem>
placeholder="请输入用户名"是没有通过props,而是$attrs传递的,如果没有设置inheritAttrs:false会出现在在父组件也有这个属性,但是当设置为false时,只会出现input组件上:
在没有设置为false的时候:
KformItem组件的实现
<template>
<div>
<!-- label -->
<label v-if="label">{{label}}</label>
<!-- 容器,放插槽 -->
<slot></slot>
<p v-if="error" class="setcolor">{{error}}</p>
<!-- 这里的校验信息不是传入的,而是自己的状态,所以要放在自己的data里面维护起来 -->
<!-- <p>{{form.rules}}</p> -->
</div>
</template>
<script>
import Schema from 'async-validator'
export default {
inject: ["form"],
props:{
label:{
type:String,
default:''
}
},
data() {
return {
error: ' ',
prop:{
type:String,
}
}
},
mounted(){
//监听校验事件
this.$on("validate", () => {
this.validate();
});
},
methods:{
validate(){
//执行校验
//1.获取值和校验规则
const rules = this.form.rules[this.prop]
const value = this.form.model[this.prop]
//2.执行校验,使用官方也是用的:使用官方也使用的async-validator
const descriptor = {[this.prop]:rules}
// 创建校验器
const schema = new Schema(descriptor)
// 执行校验
return schema.validate({[this.prop]:value},errors=>{
//如果error存在,则说明校验失败
if(errors){
this.error = errors[0].message
}else{
this.error = ''
}
})
}
}
}
</script>
这里实现的是label的输入,并且留一个容器将KInput插入,还有就是表单校验的错误提示。
注意事项
这里的监听校验事件,在KInput里面的数据发生变化的时候就是在KformItem数据要进行监听的时候,但是,怎么来监听该事件呢,$emit时谁派发谁监听,因此在KInput中采用父组件派发的形式
Kform组件的实现
<template>
<div>
<!-- 容器:存放所有表单项 -->
<!-- 存储值载体:保存大家数据和校验规则 -->
<slot></slot>
</div>
</template>
<script>
export default {
provide(){
return{
//这里传递的数据不是响应式的
//这里采用的就是直接把当前组件实例传递下去
//传递下去的对象是响应式的则还是可以响应式
form:this
}
},
props:{
//数据模型
model:{
type:Object,
required:true
},
rules:Object
},
methods:{
validate(cb){
//遍历肚子里面的所有FormItem,执行他们的validate方法
//全部通过才算校验
//tasks是校验结果的Promise组成的数据
const tasks = this.$children.filter(item=>item.prop).map(item=>item.validate())
console.log(this.$children)
//统一处理所有的Promise结果
Promise.all(tasks).then(()=>cb(true)).catch(()=>cb(false))
}
}
}
</script>
这里的Kform只是一个容器,保存所有的表单,并且保存所有的规则和存储的数据。
注意事项
这里用到provide 隔代传值,但是传过去的数据并不是响应式的,这里采用传过去的值是组件本身,这样解决了传值不响应的问题
写在最后
关于组件开发看别人的是学习他人组件开发的技巧方法和精髓,更多的时候是自己在项目中的实践,哪些需要提取,哪些是输入,哪些是输出,哪些是组件内部的状态维护。都需要在实践中自己学习和把握(个人觉得是一种开发思维模式的转换,还是那句话“纸上得来终觉浅,绝知此事要躬行”)。