packages/core/props-manager/old/PropsManager.back.ts (138 lines of code) (raw):
/**
* Copyright (C) 2021 Alibaba Group Holding Limited
* All rights reserved.
*/
import { default as isEqual } from 'lodash/isEqual'
import { default as partition } from 'lodash/partition'
import { default as isEmpty } from 'lodash/isEmpty'
export type EventCallBack = { (event: Event, done: () => void): void }
export interface Event {
type: string[]
trigger: string[]
[property: string]: any
}
class EventEmitter {
listeners: Map<string, EventCallBack> = new Map()
on(type: string, callback: EventCallBack) {
this.listeners.set(type, callback)
}
emit(event: Event): Promise<void> {
const callback = this.listeners.get(event.type)
return new Promise<void>((resolve) => {
callback?.apply(this, [event, resolve])
})
}
getCallback(type: string) {
return this.listeners.get(type)
}
}
/**
* get changed props keys
* @note using deep-diff algorithm, objects and arrays will be deep-diff
* @note only check added and changed keys, not deleted keys
*/
function deepDiffProps(newProps: Record<string, any>, oldProps: Record<string, any>): string[] {
const changedKeys: Array<string> = []
const keys = Object.keys(newProps)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
if (!isEqual(newProps[key], oldProps[key])) {
changedKeys.push(key)
}
}
return changedKeys
}
function needsUpdate(key: string, diffProps: Array<string>) {
for (let i = 0; i < diffProps.length; i++) {
if (key.indexOf(diffProps[i]) > -1) {
return diffProps[i]
}
}
return false
}
function includes(array: string[], item: string): boolean {
for (let i = 0; i < array.length; i++) {
const element = array[i]
}
}
/**
* 属性管理 - 协助 Layer 管理 props
* 1. 修改、监听、获取 props 值
* 2. 缓存 transform 后的数据
* 3. 将 'getXXX' 以 get 开头的 sacle 属性转换为 data mapping 回调函数
*/
export class PropsManager extends EventEmitter {
// 当前的 props 属性
props: any = {}
/**
* 初始化/更新属性,可以是增量更新
* @param {*} newProps
* @param {() => void} [callback]
* @memberof PropsManager
*/
set(newProps: any, callback?: () => void): Promise<void> {
// 获取新旧 Props 的差异
const diffProps = deepDiffProps(newProps, this.props)
const partitionDiffProps = partition(diffProps, (props) => props === 'data')
const diffDataProps = partitionDiffProps[0]
const diffOtherProps = partitionDiffProps[1]
// 更新旧 Props
this.props = {
...this.props,
...newProps,
}
return new Promise<void>((resolve) => {
if (!isEmpty(diffDataProps)) {
_getCacheData(this.props['data']).then((cacheData) => {
this.cacheData = cacheData
// 数据相关属性有变化
// 非数据相关属性有变化
this.trigger(diffOtherProps, diffDataProps).then(() => {
if (callback) callback()
resolve()
})
})
} else {
// 非数据相关属性有变化
this.trigger(diffOtherProps).then(() => {
if (callback) callback()
resolve()
})
}
})
}
/**
* @fixed 若dataProps和normalProps存在共享同一个callback, 则只触发一次
* @param normalProps
* @param dataProps
*/
trigger(normalProps: string[], dataProps?: string[]): Promise<void> {
const keyMap = new Map<string, string[]>()
// Find and set normalProps callback
for (const key of this.listeners.keys()) {
const trigger = needsUpdate(key, normalProps)
if (trigger !== false) {
const triggerProps = keyMap.get(key)
if (triggerProps === undefined) {
// Add normalProps keys into triggers arr
keyMap.set(key, Array.from(normalProps))
} else {
// Add normalProps keys into triggers arr
normalProps.forEach((prop) => {
triggerProps.push(prop)
})
}
}
}
// Find and set data callback
if (dataProps) {
for (const key of this.listeners.keys()) {
const trigger = needsUpdate(key, dataProps)
if (trigger !== false) {
const triggerProps = keyMap.get(key)
if (triggerProps === undefined) {
keyMap.set(key, [trigger])
} else {
triggerProps.push(trigger)
}
}
}
}
const emitPromises: Promise<any>[] = []
keyMap.forEach((triggerProps, key) => {
emitPromises.push(
this.emit({
type: key,
trigger: triggerProps,
})
)
})
return new Promise<void>((resolve) => {
Promise.all(emitPromises).then(() => resolve())
})
}
/**
* 获取属性对应的值,对 data 以及数据图形映射属性有特殊处理
* @fixed 将所有getter functions缓存起来,每次取值即可取到相同的function,避免diff时产生false positive。
* @param key
*/
get(key: string) {
const value = this.props[key]
// 处理 get 开头的数据图形映射属性,返回一个 mapping 函数,每个数据 item,可以映射为一个图形属性
if (key.startsWith('get')) {
if (typeof value === 'function') return value
const getter = () => value
return getter
}
return value
}
/**
* 注册属性变化监听器
* @param propsName
* @param callback
*/
listen(propsName: string | Array<string>, callback: EventCallBack) {
if (!Array.isArray(propsName)) {
propsName = [propsName]
}
const type = propsName.join(',')
this.on(type, callback)
// 注册完后,主动触发一次回调,方便 Layer 做初始化
this.emit({
type,
trigger: 'initialize',
})
}
}