缓存组件
缓存分为了普通数据缓存与特殊UI交互缓存,其中普通数据缓存使对象来维护
注册时用type来区分不同的数据表单类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
export enum SourceDataType { CLM_INDEX_ANTD_FORM = 'clm_index_antd_form', CLM_COLLECTIONTERMS_ANTD_FORM = 'clm_collectionTerms_antd_form', CLM_REPORTEDINCOMES_ANTD_FORM = 'clm_reportedIncomes_antd_form', CLM_FINANCIALSETTLEMENT_ANTD_FORM = 'clm_financialSettlement_antd_form', CLM_CASHDEPOSIT_ANTD_FORM = 'clm_cashDeposit_antd_form', CLM_PROJECTINFO_ANTD_FORM = 'clm_projectInfo_antd_form', CLM_INDEX_META_FORM = 'clm_index_meta_form', CLM_RISK_META_FORM = 'clm_risk_meta_form', CLM_BID_RISK_META_FORM = 'clm_bid_risk_meta_form', CLM_INTERNAL_META_FORM = 'clm_internal_meta_form' }
|
数据跟新时,有可能多个地方同时使用缓存的对象进行了更新。直接修改缓存对象引用类型的值,不要深拷贝,不然可能同时更新时,数据出现覆盖的现象
1 2 3 4 5 6 7 8 9 10 11 12
| const updateSourceData = (sourceData: SourceData[]) => { if (!cacheLock) { return } const tempData = cacheData _.forEach(sourceData, item => { tempData.formValues[item.type] = item.data }) setCacheData({ ...tempData }) }
|
如果把 const tempData = cacheData 换成 const tempData = _.deepClone(cacheData),
则多个地方的数据修改无法同步,只能保持了最新的一个值。使用引用类型更新时,也要做一个浅拷贝,
防止无法使cacheData触发重新渲染
- 暴露出如下方法
- addCacheLoader 提供给数据加载器的添加方法
- updateSourceData 用于数据改变时,将数据同步给缓存组件的方法
- updateSourceActionFn 用于特殊的ui操作添加方法
- updateAction 用于ui操作改变时,将ui操作同步给缓存组件的方法
页面流程
1、页面加载阶段
a、页面通过 addCacheLoader 载入当前缓存表单的加载器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| useEffect(() => { addCacheLoader([ { type: SourceDataType.CLM_INDEX_ANTD_FORM, loader: form, }, { type: SourceDataType.CLM_CASHDEPOSIT_ANTD_FORM, loader: incomeReportAndSettlementRef?.current?.cashDepositForm, }, { type: SourceDataType.CLM_COLLECTIONTERMS_ANTD_FORM, loader: incomeReportAndSettlementRef?.current?.collectionTermsForm, }, { type: SourceDataType.CLM_FINANCIALSETTLEMENT_ANTD_FORM, loader: incomeReportAndSettlementRef?.current?.financialSettlementForm, }, { type: SourceDataType.CLM_REPORTEDINCOMES_ANTD_FORM, loader: incomeReportAndSettlementRef?.current?.reportedIncomesForm, }, { type: SourceDataType.CLM_PROJECTINFO_ANTD_FORM, loader: projectInfoRef?.current?.customerForm, }, { type: SourceDataType.CLM_INDEX_META_FORM, loader: setValues, }, ]) }, [form?.setFieldsValue, incomeReportAndSettlementRef, projectInfoRef, setValues])
|
b、同时注册数据导入监听,当页面相应表单数据变化时,及时更新对应表单缓存。注意,这里的表单信息可能分散在代码各个地方,在根级容器分发注册方法(updateSourceData)即可。
例如,根级元素注册了以下监听
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| useEffect(() => { updateSourceData([{ type: SourceDataType.CLM_INDEX_ANTD_FORM, data: form?.getFieldsValue(), }, { type: SourceDataType.CLM_INDEX_META_FORM, data: values, }, { type: SourceDataType.CLM_PROJECTINFO_ANTD_FORM, data: projectInfoRef?.current?.customerForm?.getFieldsValue(), }, ]) }, [ JSON.stringify(form?.getFieldsValue()), values, JSON.stringify(projectInfoRef?.current?.customerForm?.getFieldsValue()), ])
|
子元素表单注册:
1 2 3 4 5 6 7 8
| useEffect(() => { updateSourceData([ { type: SourceDataType.CLM_COLLECTIONTERMS_ANTD_FORM, data: form?.getFieldsValue(), }, ]) }, [JSON.stringify(form?.getFieldsValue())])
|
缓存组件权限判断
父元素的权限获取可能有延迟,loading判断主要接口的加载完成时间,但是审批接口(可能有可能没有)无法判断。有权限的人才开始拉取缓存数据,收集外部缓存数据
- 创建阶段任何人都有缓存权限
- 创建者在草稿阶段
- 审批人在自己的审批节点
数据上报
当用户具备缓存权限,且第一次进行了鼠标操作才开始监听操作
1 2 3 4 5 6 7 8
| const mouseHandler = () => { if (!cacheLock && currentCacheVersion) { getCache() } !cacheLock && setCacheLock(true) }
|
检测cacheData的变化,最后一次输入停止的时候开始计时30s,提交数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| useEffect(() => { console.log('1111', timer) if (!cacheLock) { return } if (timer) { clearTimer(timer) } timer = setTimeout(() => { setCache() }, REPORT_TIME) }, [cacheLock, setCache, cacheKey])
|
数据消费
初始化之后通过getCache拉取到数据后,先把数据存储到waitConsumedCacheData中。同时将模块加载器和待消费数据作为依赖添加个处理函数,当任意一个模块同时具备2者时,对数据进行消费,然后清空该数据。完全结束时,该模块的数据应该被清空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| useEffect(() => { if (waitConsumedCacheData?.formValues && !_.isEmpty(cacheLoader)) { try { if (_.isEmpty(waitConsumedCacheData?.formValues)) { setWaitConsumedCacheData({ ...waitConsumedCacheData, formValues: undefined, }) return }
if (waitConsumedCacheData?.actions?.length > 0) { _.forEach(waitConsumedCacheData?.actions, i => { if (actionFn?.[i] && typeof actionFn[i] === 'function') { actionFn[i]() setActionFn({ ...actionFn, [i]: undefined, }) } }) setWaitConsumedCacheData({ ...waitConsumedCacheData, actions: [], }) return }
_.forOwn(waitConsumedCacheData?.formValues, (value, key) => { if (!cacheLoader[key] || !value) return
formatDate(waitConsumedCacheData?.formValues, value, key)
if (_.includes([SourceDataType.CLM_INDEX_META_FORM, SourceDataType.CLM_INDEX_ANTD_FORM, SourceDataType.CLM_INTERNAL_META_FORM], key)) { value.isCached = true }
if (key?.includes('antd_form') && typeof cacheLoader[key]?.getFieldDecorator === 'function') { console.log(key, value) bindFieldsDecorator(value, cacheLoader[key]) cacheLoader[key]?.setFieldsValue(value) } else { cacheLoader[key](value) } delete waitConsumedCacheData.formValues[key] setWaitConsumedCacheData(waitConsumedCacheData) }) } catch (e) { console.log(e) } } }, [waitConsumedCacheData, cacheLoader, actionFn])
|
问题:
1、使用引用类型更新时,也要做一个浅拷贝,防止无法使cacheData触发重新渲染,为何?
function 类型更新时,会做一个对比,引用不变,会放弃更新
2、性能如何优化?