UNPKG

d-render

Version:

数据渲染库 - form、table、search-form

188 lines (183 loc) 7.98 kB
import { computed, ComputedRef, ShallowRef, shallowRef } from 'vue' // cloneDeep, import { getFieldValue, IAnyObject, IRenderConfig, TFormConfig } from '@d-render/shared' import { EffectExecutor, IEffectExecutorConfigs, IExecutor, IEffects } from '../effect-executor' import { useFieldChange } from './use-field-change' import { secureNewFn, cloneDeepConfig, IKey, IObjectKey, getValuesByKeys } from '../util' import type { FormItemProps } from '../index' export const useWatchFieldDepend = ( props: FormItemProps, { updateModelValue, updateOtherValue, clearValues }: { updateModelValue: (val:unknown) => Promise<void> updateOtherValue: (val:unknown) => Promise<void> clearValues: () => void } ) => { const securityConfig: ComputedRef<TFormConfig> = computed(() => props.config ?? {}) // 运行中的config // [PERF]: 6.1.x 使用shallowRef优化对config的相应 const runningConfig: ShallowRef<TFormConfig|undefined> = shallowRef() // 运行时的config // 各类型自己的执行器 // [PERF]: 6.1.x初步优化cloneDeep的性能不在对一些特殊的属性进行深度复制 // [FEAT]: 6.0.x changeConfig、changeValue支持流式处理 // [REFACTOR]: 执行条件提升到执行器配置中 const changeConfigExecutor = async ( values: Parameters<IExecutor>['0'], outValues: Parameters<IExecutor>['1'], effects: Parameters<IExecutor>['2'] ) => { // [PERF]: cloneDeep较为消耗性能如何优化 let config = cloneDeepConfig(securityConfig.value) for (let [changeConfigCb] of effects) { if (typeof changeConfigCb === 'string') { changeConfigCb = secureNewFn('config', 'values', 'outValues', changeConfigCb) as ()=> Promise<TFormConfig> } if (typeof changeConfigCb === 'boolean') { continue } config = await changeConfigCb(config, values, outValues) as TFormConfig } runningConfig.value = config } const changeValueExecutor = async ( values: Parameters<IExecutor>['0'], outValues: Parameters<IExecutor>['1'], effects: Parameters<IExecutor>['2'] ) => { const otherKey = ([] as string[]).concat(props.config?.otherKey as string) let data = { value: getFieldValue(props.model, props.fieldKey), otherValue: getValuesByKeys(props.model, otherKey) } for (let [changeValueCb] of effects) { if (typeof changeValueCb === 'string') { changeValueCb = secureNewFn('values', 'outValues', changeValueCb) as ()=> Promise<{ value: unknown, otherValue?: unknown } | undefined> } if (typeof changeValueCb === 'boolean') { continue } data = await changeValueCb(values, outValues, data) as { value: unknown, otherValue: {} } } if (typeof data === 'object') { const { value, otherValue } = data updateModelValue(value) updateOtherValue(otherValue) } } const resetValueExecutor = ( values: Parameters<IExecutor>['0'], outValues: Parameters<IExecutor>['1'], effects: Parameters<IExecutor>['2'] ) => { // 获取判断结果 for (let [resetValueCb, { oldValues, keys }] of effects) { keys = ([] as Array<string>).concat(keys) oldValues = ([]as Array<unknown>).concat(oldValues) if ( !(keys.length === 1 && keys[0] === props.fieldKey) && // 不能只有自己变了变了 oldValues.some(val => val !== undefined) // 存在变更的依赖原始值不为undefined [1,undefined] true [undefined,undefined] false // executeChangeValueEffect // 此条件提升至执行器配置中 ) { if ((resetValueCb === true) || (typeof resetValueCb === 'function' && resetValueCb(getFieldValue(props.model, props.fieldKey!), values, outValues) ) ) { // 清空值只需要执行一次, 由于异步执行的关系,其执行顺序有可能在changeValue后导致他们同时运行时可能出现不一致的问题 clearValues() return } } } } const changeValueByOldExecutor = async ( values: Parameters<IExecutor>['0'], outValues: Parameters<IExecutor>['1'], effects: Parameters<IExecutor>['2'] ) => { // 与changeValue一样,首次不触发 let data for (let [changeValueByOldCb, { keys, oldValues }] of effects) { keys = ([] as Array<string>).concat(keys) oldValues = ([]as Array<unknown>).concat(oldValues) if (typeof changeValueByOldCb === 'boolean') { continue } for (const [idx, key] of keys.entries()) { // 全局多源监听时会执行多次,不建议设置为全局副作用 [兼容老代码] data = await changeValueByOldCb({ key, oldValue: oldValues[idx] }, values, outValues, data) as {value: unknown, otherValue: unknown} | undefined } } if (data !== undefined) { const { value, otherValue } = data updateModelValue(value) updateOtherValue(otherValue) } } const effectExecutorConfig: IEffectExecutorConfigs = { // 允许effect为字符串,配置转换函数 changeConfig: { executor: changeConfigExecutor, transformStrEffect: (effectStr: string) => secureNewFn('config', 'dependOnValues', 'outDependOnValues', effectStr) }, changeValue: { condition: (effects: IEffects, executeChangeValueEffect: boolean) => !props.readonly && executeChangeValueEffect, executor: changeValueExecutor, transformStrEffect: (effectStr: string) => secureNewFn('dependOnValues', 'outDependOnValues', 'data', effectStr) }, resetValue: { condition: (effects: IEffects, executeChangeValueEffect: boolean) => !props.readonly && executeChangeValueEffect, executor: resetValueExecutor }, // 此方法全局和局部只要执行一个就好了, 存在effect.resetValue不需要执行global.resetValue changeValueByOld: { // 此方法不存在global condition: (effects: IEffects, executeChangeValueEffect: boolean) => !props.readonly && executeChangeValueEffect, executor: changeValueByOldExecutor } } const effectExecutor = new EffectExecutor(effectExecutorConfig) const dependOnWatchCb = ( { changeKeys, changeOldValues }: {changeKeys: Array<IKey>, changeOldValues: Array<unknown> }, { values, outValues, executeChangeValueEffect }: {values: IAnyObject, outValues: IAnyObject, executeChangeValueEffect: boolean} ) => { // 获取局部effect的key const privateEffectKeys = changeKeys .map((key, index) => { if (typeof key === 'object') return { ...key, _index: index } return key }) // 塞入初始的index .filter(key => typeof key === 'object') as Array<IObjectKey & {_index: number }> // 获取全局effect的key const hasGlobalEffectKey = changeKeys.find(key => typeof key === 'string') const cbParams = [] // 执行全局effect的回调 if (hasGlobalEffectKey) { cbParams.push({}) // 只要有个无effect的标记即可,analysisEffects会使用globalParam中的配置 } // 执行局部effect的回调 privateEffectKeys.forEach(object => { const privateEffect = object.effect || {} // @ts-ignore cbParams.push({ keys: object.key, oldValues: changeOldValues[object._index], effect: privateEffect }) }) // [BROKEN]: 6.0.x 除resetValue外,其他函数在执行私有的副作用时会优先执行全局的副作用 effectExecutor.analysisEffects(cbParams, securityConfig.value, { values, outValues, keys: changeKeys, oldValues: changeOldValues, executeChangeValueEffect }) effectExecutor.executeAll(props.fieldKey as string) } // 收集变化的依赖 const { changeCount, dependOnValues, outDependOnValues } = useFieldChange(props, securityConfig, dependOnWatchCb) return { changeCount, // model变化总计 dependOnValues, outDependOnValues, runningConfig } }