Vue3中父子表单组件数据同步问题
前言:
有段时间没有更新文章了,最近工作中遇到某个Vue表单文件复杂度代码行数高达5K+,为此页面更新时有些许卡顿,当时决定将这个Vue表单文件抽离成几个小表单的结构,便于今后的项目维护和功能迭代。
所以今天给大家带来的是Vue3中对于大型表单组件如何抽离小表单组件以及父子组件数据同步问题。
以下提供三种方案,由上到下逐步优化。
先来看页面的效果(以下案例均按照如下页面效果实现):
方案一:
- 子组件自行维护各自表单数据,更新后同步到父组件。
- 父组件表单数据变更时,设置子组件的表单回显。
父组件如何实现?:
由此可见父组件定义了ruleForm
对象,用于整合整个大型表单文件的数据存储。 changeSonForm
方法用于接受子组件表单数据变更时传递的最新数据,然后做覆盖ruleForm对象的操作。实现子组件表单数据同步到父组件中。
再看子组件的实现?:
子组件自行维护sonForm
对象,并且利用watch
深度监听该对象,对象属性变更时同步给父组件,更新父组件的ruleForm
对象,这样就父子组件表单的值就能够同步。
那要是页面挂载完成时需要初始化表单值怎么做呢?未抽离时直接覆盖ruleForm
对象就好了,但是现在部分表单的数据已经抽离到子组件了,那么就需要去初始化子组件的表单了。
/** 子组件 */
/** 提供一个方法,供父组件刷新子组件的值 */
const initSonForm = (form: FormType) => {
Object.assign(sonForm, form);
};
/** 子组件暴露出去的变量|方法 */
defineExpose({
initSonForm,
});
/** 父组件 */
const sonRef = ref();
onMounted(() => {
/**模拟更改ruleForm的数据,初始化子组件的表单数据 */
ruleForm.name = "我是父组件修改的值";
ruleForm.open = true;
/** 触发子组件提供的方法 */
sonRef.value?.initSonForm({
name: ruleForm.name,
type: ruleForm.type,
open: ruleForm.open,
citys: ruleForm.citys,
});
});
缺点:
- 父组件需要维护一套子组件的表单对象。
- 数据流动混乱,子父组件各自调动初始化数据,打破数据单向流动性规则。
- 项目维护难度大。
方案二:
- 父组件维护数据,传递整个表单对象。
父组件实现?:
所有的数据均由父组件维护,表单对象通过属性传递给子组件,子组件通过props接收并直接绑定到自己的表单中。
此时数据时互通的,不需要子组件表单项更新时同步到父组件,父组件的表单变更也会响应式更新到子组件。
看起来方案二已经完美无瑕了,但是忽略了一点:子组件收到父组件的表单对象是直接绑定到v-model中的,相当于直接更改了父组件传递过来的值,这是官方不建议的,且引入了eslint也会报出警告⚠️。
缺点:
- 子组件直接更改了父组件的值,打破数据单向流动性规则。
方案三:
- 父组件维护数据,通过
v-model
传递表单对象。 - 子组件收到的对象不直接绑定到表单,利用
computed
做一层中转。
直接看父组件:
值得注意的是这里的ruleForm
对象改为了ref
声明,且在子组件传递处改为了v-model=“ruleForm”
。
原因:使用reactive覆盖表单对象会使ruleForm失去响应式。
子组件实现:
表单变更时控制台输出:
考虑到此表单组件抽离是常见的场景,特写成通用的hooks
函数。代码如下:
你可能会疑惑:
什么时候会触发外层的set
函数?
- 答:只有当
props[propName]
整个对象被重新赋值时会触发。
以上就是此次分享的全部内容啦。
结语:
表单组件抽离在开发中很常见,切不可打破数据单向流动性数规则,否则,打破地越多,代码离屎山就越近!