优化
This commit is contained in:
@@ -81,8 +81,13 @@ export class ObservableImpl<T extends TNonFunctionProperties<T>> implements IObs
|
||||
/** 全量订阅函数集合 */
|
||||
private listeners: Set<TObservableListener<T>> = new Set()
|
||||
|
||||
/** 字段订阅函数集合 */
|
||||
private keyListeners: Map<keyof T, Set<TObservableKeyListener<T, keyof T>>> = new Map()
|
||||
/**
|
||||
* 字段订阅函数集合
|
||||
* 新结构:
|
||||
* Map<TObservableKeyListener, Array<keyof T>>
|
||||
* 记录每个回调订阅的字段数组,保证多字段订阅 always 返回所有订阅字段值
|
||||
*/
|
||||
private keyListeners: Map<TObservableKeyListener<T, keyof T>, Array<keyof T>> = new Map()
|
||||
|
||||
/** 待通知的字段集合 */
|
||||
private pendingKeys: Set<keyof T> = new Set()
|
||||
@@ -161,7 +166,7 @@ export class ObservableImpl<T extends TNonFunctionProperties<T>> implements IObs
|
||||
|
||||
/** 安排下一次通知(微任务合并) */
|
||||
private scheduleNotify(): void {
|
||||
if (!this.notifyScheduled && !this.disposed) {
|
||||
if (!this.notifyScheduled && !this.disposed && this.pendingKeys.size > 0) {
|
||||
this.notifyScheduled = true
|
||||
Promise.resolve().then(() => this.flushNotify())
|
||||
}
|
||||
@@ -171,7 +176,6 @@ export class ObservableImpl<T extends TNonFunctionProperties<T>> implements IObs
|
||||
private flushNotify(): void {
|
||||
if (this.disposed) return
|
||||
|
||||
const keys = Array.from(this.pendingKeys)
|
||||
this.pendingKeys.clear()
|
||||
this.notifyScheduled = false
|
||||
|
||||
@@ -180,30 +184,13 @@ export class ObservableImpl<T extends TNonFunctionProperties<T>> implements IObs
|
||||
try {
|
||||
fn(this.state as unknown as T)
|
||||
} catch (err) {
|
||||
// 可以根据需要把错误上报或自定义处理
|
||||
// 这里简单打印以便调试
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Observable listener error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 字段订阅:把同一个回调的多个 key 聚合到一次调用里
|
||||
const fnMap: Map<TObservableKeyListener<T, keyof T>, Array<keyof T>> = new Map()
|
||||
for (const key of keys) {
|
||||
const set = this.keyListeners.get(key)
|
||||
if (!set) continue
|
||||
for (const fn of set) {
|
||||
const existing = fnMap.get(fn)
|
||||
if (existing === undefined) {
|
||||
fnMap.set(fn, [key])
|
||||
} else {
|
||||
existing.push(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 调用每个字段订阅回调
|
||||
fnMap.forEach((subKeys, fn) => {
|
||||
// ================== 字段订阅 ==================
|
||||
// 遍历所有回调,每个回调都返回它订阅的字段(即使只有部分字段变化)
|
||||
this.keyListeners.forEach((subKeys, fn) => {
|
||||
try {
|
||||
// 构造 Pick<T, K> 风格的结果对象:结果类型为 Pick<T, (typeof subKeys)[number]>
|
||||
const result = {} as Pick<T, (typeof subKeys)[number]>
|
||||
@@ -214,7 +201,6 @@ export class ObservableImpl<T extends TNonFunctionProperties<T>> implements IObs
|
||||
// 调用时类型上兼容 TObservableKeyListener<T, K>,因为我们传的是对应 key 的 Pick
|
||||
fn(result as Pick<T, (typeof subKeys)[number]>)
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Observable keyListener error:", err)
|
||||
}
|
||||
})
|
||||
@@ -227,7 +213,6 @@ export class ObservableImpl<T extends TNonFunctionProperties<T>> implements IObs
|
||||
try {
|
||||
fn(this.state as unknown as T)
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Observable subscribe immediate error:", err)
|
||||
}
|
||||
}
|
||||
@@ -236,19 +221,18 @@ export class ObservableImpl<T extends TNonFunctionProperties<T>> implements IObs
|
||||
}
|
||||
}
|
||||
|
||||
/** 订阅指定字段变化 */
|
||||
/** 订阅指定字段变化(多字段订阅 always 返回所有字段值) */
|
||||
public subscribeKey<K extends keyof T>(
|
||||
keys: K | K[],
|
||||
fn: TObservableKeyListener<T, K>,
|
||||
options: { immediate?: boolean } = {}
|
||||
): () => void {
|
||||
const keyArray = Array.isArray(keys) ? keys : [keys]
|
||||
for (const key of keyArray) {
|
||||
if (!this.keyListeners.has(key)) this.keyListeners.set(key, new Set())
|
||||
// 存储为 Set<TObservableKeyListener<T, keyof T>>
|
||||
this.keyListeners.get(key)!.add(fn as TObservableKeyListener<T, keyof T>)
|
||||
}
|
||||
|
||||
// ================== 存储回调和它订阅的字段数组 ==================
|
||||
this.keyListeners.set(fn as TObservableKeyListener<T, keyof T>, keyArray as (keyof T)[])
|
||||
|
||||
// ================== 立即调用 ==================
|
||||
if (options.immediate) {
|
||||
const result = {} as Pick<T, K>
|
||||
keyArray.forEach(k => {
|
||||
@@ -257,30 +241,26 @@ export class ObservableImpl<T extends TNonFunctionProperties<T>> implements IObs
|
||||
try {
|
||||
fn(result)
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Observable subscribeKey immediate error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ================== 返回取消订阅函数 ==================
|
||||
return () => {
|
||||
for (const key of keyArray) {
|
||||
this.keyListeners.get(key)?.delete(fn as TObservableKeyListener<T, keyof T>)
|
||||
}
|
||||
this.keyListeners.delete(fn as TObservableKeyListener<T, keyof T>)
|
||||
}
|
||||
}
|
||||
|
||||
/** 批量更新状态(避免重复 schedule) */
|
||||
public patch(values: Partial<T>): void {
|
||||
let changed = false
|
||||
// 用 for..in 保持和对象字面量兼容(跳过原型链)
|
||||
for (const key in values) {
|
||||
if (Object.prototype.hasOwnProperty.call(values, key)) {
|
||||
const typedKey = key as keyof T
|
||||
const oldValue = (this.state as Record<keyof T, unknown>)[typedKey]
|
||||
const newValue = values[typedKey] as unknown
|
||||
if (oldValue !== newValue) {
|
||||
// 直接写入 state,会被 Proxy 的 set 捕获并安排通知
|
||||
;(this.state as Record<keyof T, unknown>)[typedKey] = newValue
|
||||
(this.state as Record<keyof T, unknown>)[typedKey] = newValue
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
@@ -322,3 +302,4 @@ export class ObservableImpl<T extends TNonFunctionProperties<T>> implements IObs
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user