Match-id-6aa4ff44738c3e17ce4256de708591d0f487e7bb

This commit is contained in:
* 2022-10-31 17:30:36 +08:00 committed by *
commit 4359db22f2
13 changed files with 185 additions and 174 deletions

View File

@ -1,67 +0,0 @@
/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
export function isObject(obj) {
const type = typeof obj;
return obj != null && (type === 'object' || type === 'function');
}
export function isSet(obj) {
return obj != null && (Object.prototype.toString.call(obj) === '[object Set]' || obj.constructor === Set);
}
export function isWeakSet(obj) {
return obj != null && (Object.prototype.toString.call(obj) === '[object WeakSet]' || obj.constructor === WeakSet);
}
export function isMap(obj) {
return obj != null && (Object.prototype.toString.call(obj) === '[object Map]' || obj.constructor === Map);
}
export function isWeakMap(obj) {
return obj != null && (Object.prototype.toString.call(obj) === '[object WeakMap]' || obj.constructor === WeakMap);
}
export function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
export function isCollection(obj) {
return isSet(obj) || isWeakSet(obj) || isMap(obj) || isWeakMap(obj);
}
export function isString(obj) {
return typeof obj === 'string';
}
export function isValidIntegerKey(key) {
return isString(key) && key !== 'NaN' && key[0] !== '-' && String(parseInt(key, 10)) === key;
}
export const noop = () => {};
export function isSame(x, y) {
if (!(typeof Object.is === 'function')) {
if (x === y) {
// +0 != -0
return x !== 0 || 1 / x === 1 / y;
} else {
// NaN == NaN
return x !== x && y !== y;
}
} else {
return Object.is(x, y);
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
export function isObject(obj: any): boolean {
const type = typeof obj;
return (obj !== null || obj !== undefined) && (type === 'object' || type === 'function');
}
export function isSet(obj: any): boolean {
return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object Set]' || obj.constructor === Set);
}
export function isWeakSet(obj: any): boolean {
return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object WeakSet]' || obj.constructor === WeakSet);
}
export function isMap(obj: any): boolean {
return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object Map]' || obj.constructor === Map);
}
export function isWeakMap(obj: any): boolean {
return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object WeakMap]' || obj.constructor === WeakMap);
}
export function isArray(obj: any): boolean {
return Object.prototype.toString.call(obj) === '[object Array]';
}
export function isCollection(obj: any): boolean {
return isSet(obj) || isWeakSet(obj) || isMap(obj) || isWeakMap(obj);
}
export function isString(obj: any): boolean {
return typeof obj === 'string';
}
// key是有效的正整数字的字符串
export function isValidIntegerKey(key: any): boolean {
return isString(key) && key !== 'NaN' && key[0] !== '-' && String(parseInt(key, 10)) === key;
}
export function isPromise(obj: any): boolean {
return isObject(obj) && typeof obj.then === 'function';
}
export function isSame(x, y) {
if (!(typeof Object.is === 'function')) {
if (x === y) {
// +0 != -0
return x !== 0 || 1 / x === 1 / y;
} else {
// NaN == NaN
return x !== x && y !== y;
}
} else {
return Object.is(x, y);
}
}

View File

@ -13,8 +13,4 @@
* See the Mulan PSL v2 for more details.
*/
// The two constants must be the same as those in horizon.
export const FunctionComponent = 'FunctionComponent';
export const ClassComponent = 'ClassComponent';
export const OBSERVER_KEY = '_horizonObserver';

View File

@ -15,8 +15,6 @@
import { createStore as createStoreX } from '../store/StoreHandler';
import { ReduxStoreHandler } from '../store/StoreHandler';
export { thunk } from './reduxThunk';
export {
@ -29,6 +27,14 @@ export {
createDispatchHook,
} from './reduxReact';
export type ReduxStoreHandler = {
reducer: (state: any, action: { type: string }) => any;
dispatch: (action: { type: string }) => void;
getState: () => any;
subscribe: (listener: () => void) => () => void;
replaceReducer: (reducer: (state: any, action: { type: string }) => any) => void;
};
export type ReduxAction = {
type: string;
[key: string]: any;

View File

@ -17,9 +17,7 @@
import { useState, useContext, useEffect, useRef } from '../../renderer/hooks/HookExternal';
import { createContext } from '../../renderer/components/context/CreateContext';
import { createElement } from '../../external/JSXElement';
import { BoundActionCreator } from './redux';
import { ReduxAction } from './redux';
import { ReduxStoreHandler } from '../store/StoreHandler';
import type { ReduxStoreHandler, ReduxAction, BoundActionCreator } from './redux';
const DefaultContext = createContext(null);
type Context = typeof DefaultContext;

View File

@ -13,8 +13,7 @@
* See the Mulan PSL v2 for more details.
*/
import { ReduxAction, ReduxMiddleware } from './redux';
import { ReduxStoreHandler } from '../store/StoreHandler';
import { ReduxStoreHandler, ReduxAction, ReduxMiddleware } from './redux';
function createThunkMiddleware(extraArgument?: any): ReduxMiddleware {
return (store: ReduxStoreHandler) =>

View File

@ -17,7 +17,6 @@ import { IObserver } from './Observer';
/**
* Observer
*
*/
export class HooklessObserver implements IObserver {
listeners: (() => void)[] = [];

View File

@ -13,14 +13,10 @@
* See the Mulan PSL v2 for more details.
*/
/**
* Observer
*/
//@ts-ignore
import { launchUpdateFromVNode } from '../../renderer/TreeBuilder';
import { getProcessingVNode } from '../../renderer/GlobalVar';
import { VNode } from '../../renderer/vnode/VNode';
export interface IObserver {
useProp: (key: string) => void;
@ -39,6 +35,9 @@ export interface IObserver {
clearByVNode: (vNode: any) => void;
}
/**
* Observer
*/
export class Observer implements IObserver {
vNodeKeys = new WeakMap();
@ -48,16 +47,18 @@ export class Observer implements IObserver {
watchers = {} as { [key: string]: ((key: string, oldValue: any, newValue: any) => void)[] };
// 对象的属性被使用时调用
useProp(key: string | symbol): void {
const processingVNode = getProcessingVNode();
if (processingVNode === null || !processingVNode.observers) {
// 异常场景
return;
}
// vNode -> Observers
processingVNode.observers.add(this);
// key -> vNodes
// key -> vNodes记录这个prop被哪些VNode使用了
let vNodes = this.keyVNodes.get(key);
if (!vNodes) {
vNodes = new Set();
@ -65,7 +66,7 @@ export class Observer implements IObserver {
}
vNodes.add(processingVNode);
// vNode -> keys
// vNode -> keys记录这个VNode使用了哪些props
let keys = this.vNodeKeys.get(processingVNode);
if (!keys) {
keys = new Set();
@ -74,6 +75,32 @@ export class Observer implements IObserver {
keys.add(key);
}
// 对象的属性被赋值时调用
setProp(key: string | symbol): void {
const vNodes = this.keyVNodes.get(key);
vNodes?.forEach((vNode: VNode) => {
if (vNode.isStoreChange) {
// VNode已经被触发过不再重复触发
return;
}
vNode.isStoreChange = true;
// 触发vNode更新
this.triggerUpdate(vNode);
});
this.triggerChangeListeners();
}
triggerUpdate(vNode: VNode): void {
if (!vNode) {
return;
}
// 触发VNode更新
launchUpdateFromVNode(vNode);
}
addListener(listener: () => void): void {
this.listeners.push(listener);
}
@ -82,32 +109,11 @@ export class Observer implements IObserver {
this.listeners = this.listeners.filter(item => item != listener);
}
setProp(key: string | symbol): void {
const vNodes = this.keyVNodes.get(key);
vNodes?.forEach((vNode: VNode) => {
if (vNode.isStoreChange) {
// update already triggered
return;
}
vNode.isStoreChange = true;
// 触发vNode更新
this.triggerUpdate(vNode);
});
this.triggerChangeListeners();
}
triggerChangeListeners(): void {
this.listeners.forEach(listener => listener());
}
triggerUpdate(vNode: VNode): void {
if (!vNode) {
return;
}
launchUpdateFromVNode(vNode);
}
// 触发所有使用的props的VNode更新
allChange(): void {
const keyIt = this.keyVNodes.keys();
let keyItem = keyIt.next();
@ -117,6 +123,7 @@ export class Observer implements IObserver {
}
}
// 删除keyVNodes中保存的这个VNode的关系数据
clearByVNode(vNode: VNode): void {
const keys = this.vNodeKeys.get(vNode);
if (keys) {

View File

@ -22,16 +22,18 @@ import { createCollectionProxy } from './handlers/CollectionProxyHandler';
import { IObserver } from '../types';
import { OBSERVER_KEY } from '../Constants';
// 保存rawObj -> Proxy
const proxyMap = new WeakMap();
export const hookObserverMap = new WeakMap();
export function createProxy(rawObj: any, hookObserver = true): any {
export function createProxy(rawObj: any, isHookObserver = true): any {
// 不是对象(是原始数据类型)不用代理
if (!isObject(rawObj)) {
return rawObj;
}
// 已代理过
const existProxy = proxyMap.get(rawObj);
if (existProxy) {
return existProxy;
@ -45,15 +47,15 @@ export function createProxy(rawObj: any, hookObserver = true): any {
// 创建Observer
let observer: IObserver = getObserver(rawObj);
if (!observer) {
observer = hookObserver ? new Observer() : new HooklessObserver();
observer = isHookObserver ? new Observer() : new HooklessObserver();
rawObj[OBSERVER_KEY] = observer;
}
hookObserverMap.set(rawObj, hookObserver);
hookObserverMap.set(rawObj, isHookObserver);
// 创建Proxy
let proxyObj;
if (!hookObserver) {
if (!isHookObserver) {
proxyObj = createObjectProxy(rawObj, true);
} else if (isArray(rawObj)) {
// 数组

View File

@ -44,6 +44,7 @@ function get(rawObj: any[], key: string, receiver: any) {
if (isValidIntegerKey(key) || key === 'length') {
return objectGet(rawObj, key, receiver);
}
return Reflect.get(rawObj, key, receiver);
}
@ -58,16 +59,19 @@ function set(rawObj: any[], key: string, value: any, receiver: any) {
const observer = getObserver(rawObj);
if (!isSame(newValue, oldValue)) {
// 值不一样,触发监听器
if (observer.watchers?.[key]) {
observer.watchers[key].forEach(cb => {
cb(key, oldValue, newValue);
});
}
// 触发属性变化
observer.setProp(key);
}
if (oldLength !== newLength) {
// 触发数组的大小变化
observer.setProp('length');
}

View File

@ -15,10 +15,11 @@
import { isObject } from '../CommonUtils';
export function readonlyProxy<T extends object>(target: T): ProxyHandler<T> {
return new Proxy(target, {
get(target, property, receiver) {
const result = Reflect.get(target, property, receiver);
export function readonlyProxy<T extends object>(rawObj: T): ProxyHandler<T> {
return new Proxy(rawObj, {
get(rawObj, property, receiver) {
const result = Reflect.get(rawObj, property, receiver);
try {
if (isObject(result)) {
return readonlyProxy(result);

View File

@ -21,35 +21,33 @@ import readonlyProxy from '../proxy/readonlyProxy';
import { Observer } from '../proxy/Observer';
import { FunctionComponent, ClassComponent } from '../../renderer/vnode/VNodeTags';
import { VNode } from '../../renderer/Types';
import { isPromise } from '../CommonUtils';
const storeMap = new Map<string, StoreHandler<any, any, any>>();
function isPromise(obj: any): boolean {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}
type StoreConfig<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>> = {
id?: string;
state?: S;
actions?: A;
id?: string;
computed?: C;
options?: any;
};
export type ReduxStoreHandler = {
reducer: (state: any, action: { type: string }) => any;
dispatch: (action: { type: string }) => void;
getState: () => any;
subscribe: (listener: () => void) => () => void;
replaceReducer: (reducer: (state: any, action: { type: string }) => any) => void;
};
type UserActions<S extends object> = { [K: string]: ActionFunction<S> };
type ActionFunction<S extends object> = (this: StoreHandler<S, any, any>, state: S, ...args: any[]) => any;
type StoreActions<S extends object, A extends UserActions<S>> = { [K in keyof A]: Action<A[K], S> };
type Action<T extends ActionFunction<any>, S extends object> = (
this: StoreHandler<S, any, any>,
...args: RemoveFirstFromTuple<Parameters<T>>
) => ReturnType<T>;
type StoreHandler<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>> = {
$subscribe: (listener: () => void) => void;
$unsubscribe: (listener: () => void) => void;
$s: S;
$queue: QueuedStoreActions<S, A>;
$a: StoreActions<S, A>;
$c: UserComputedValues<S>;
$queue: QueuedStoreActions<S, A>;
$subscribe: (listener: () => void) => void;
$unsubscribe: (listener: () => void) => void;
} & { [K in keyof S]: S[K] } & { [K in keyof A]: Action<A[K], S> } & { [K in keyof C]: ReturnType<C[K]> };
type PlannedAction<S extends object, F extends ActionFunction<S>> = {
@ -63,41 +61,35 @@ type RemoveFirstFromTuple<T extends any[]> = T['length'] extends 0
? I
: [];
type UserActions<S extends object> = { [K: string]: ActionFunction<S> };
type UserComputedValues<S extends object> = { [K: string]: ComputedFunction<S> };
type ActionFunction<S extends object> = (this: StoreHandler<S, any, any>, state: S, ...args: any[]) => any;
type ComputedFunction<S extends object> = (state: S) => any;
type Action<T extends ActionFunction<any>, S extends object> = (
this: StoreHandler<S, any, any>,
...args: RemoveFirstFromTuple<Parameters<T>>
) => ReturnType<T>;
type AsyncAction<T extends ActionFunction<any>, S extends object> = (
this: StoreHandler<S, any, any>,
...args: RemoveFirstFromTuple<Parameters<T>>
) => Promise<ReturnType<T>>;
type StoreActions<S extends object, A extends UserActions<S>> = { [K in keyof A]: Action<A[K], S> };
type QueuedStoreActions<S extends object, A extends UserActions<S>> = { [K in keyof A]: AsyncAction<A[K], S> };
type ComputedValues<S extends object, C extends UserComputedValues<S>> = { [K in keyof C]: ReturnType<C[K]> };
export function createStore<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>>(
config: StoreConfig<S, A, C>
storeConfig: StoreConfig<S, A, C>
): () => StoreHandler<S, A, C> {
// create a local shalow copy to ensure consistency (if user would change the config object after store creation)
config = {
id: config.id,
options: config.options,
state: config.state,
actions: config.actions ? { ...config.actions } : undefined,
computed: config.computed ? { ...config.computed } : undefined,
};
// 校验
if (Object.prototype.toString.call(config) !== '[object Object]') {
if (Object.prototype.toString.call(storeConfig) !== '[object Object]') {
throw new Error('store obj must be pure object');
}
// 创建本地浅拷贝以确保一致性(避免用户在创建存储后更改配置对象)
const config = {
id: storeConfig.id,
state: storeConfig.state,
actions: storeConfig.actions ? { ...storeConfig.actions } : undefined,
computed: storeConfig.computed ? { ...storeConfig.computed } : undefined,
options: storeConfig.options
};
const proxyObj = createProxy(config.state, !config.options?.reduxAdapter);
proxyObj.$pending = false;
@ -114,14 +106,14 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
const $a: Partial<StoreActions<S, A>> = {};
const $queue: Partial<StoreActions<S, A>> = {};
const $c: Partial<ComputedValues<S, C>> = {};
const handler = {
const storeHandler = {
$s: proxyObj,
$a: $a as StoreActions<S, A>,
$c: $c as ComputedValues<S, C>,
$queue: $queue as QueuedStoreActions<S, A>,
$config: config,
$subscribe,
$unsubscribe,
$a: $a as StoreActions<S, A>,
$s: proxyObj,
$c: $c as ComputedValues<S, C>,
$config: config,
$queue: $queue as QueuedStoreActions<S, A>,
} as unknown as StoreHandler<S, A, C>;
function tryNextAction() {
@ -132,7 +124,7 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
const nextAction = plannedActions.shift()!;
const result = config.actions
? config.actions[nextAction.action].bind(handler, proxyObj)(...nextAction.payload)
? config.actions[nextAction.action].bind(storeHandler, proxyObj)(...nextAction.payload)
: undefined;
if (isPromise(result)) {
@ -153,7 +145,7 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
return new Promise(resolve => {
if (!proxyObj.$pending) {
proxyObj.$pending = true;
const result = config.actions![action].bind(handler, proxyObj)(...payload);
const result = config.actions![action].bind(storeHandler, proxyObj)(...payload);
if (isPromise(result)) {
result.then(value => {
@ -174,45 +166,50 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
});
};
// 让store.$a[action]可以访问到action方法
($a as any)[action] = function Wrapped(...payload) {
return config.actions![action].bind(handler, proxyObj)(...payload);
return config.actions![action].bind(storeHandler, proxyObj)(...payload);
};
// direct store access
Object.defineProperty(handler, action, {
// 让store[action]可以访问到action方法
Object.defineProperty(storeHandler, action, {
writable: false,
value: (...payload) => {
return config.actions![action].bind(handler, proxyObj)(...payload);
return config.actions![action].bind(storeHandler, proxyObj)(...payload);
},
});
});
}
if (config.computed) {
Object.keys(config.computed).forEach(key => {
($c as any)[key] = config.computed![key].bind(handler, readonlyProxy(proxyObj));
Object.keys(config.computed).forEach(computeKey => {
// 让store.$c[computeKey]可以访问到computed方法
($c as any)[computeKey] = config.computed![computeKey].bind(storeHandler, readonlyProxy(proxyObj));
// direct store access
Object.defineProperty(handler, key, {
get: $c[key] as () => any,
// 让store[computeKey]可以访问到computed的值
Object.defineProperty(storeHandler, computeKey, {
get: $c[computeKey] as () => any,
});
});
}
// direct state access
// 让store[key]可以访问到state的值
if (config.state) {
Object.keys(config.state).forEach(key => {
Object.defineProperty(handler, key, {
get: () => proxyObj[key],
Object.defineProperty(storeHandler, key, {
get: () => {
// 从Proxy对象获取值会触发代理
return proxyObj[key];
},
});
});
}
if (config.id) {
storeMap.set(config.id, handler);
storeMap.set(config.id, storeHandler);
}
return createStoreHook(handler);
return createStoreHook(storeHandler);
}
export function clearVNodeObservers(vNode) {
@ -238,8 +235,7 @@ function hookStore() {
if (processingVNode.tag === FunctionComponent) {
// from FunctionComponent
const vNodeRef = useRef(null) as unknown as { current: VNode };
vNodeRef.current = processingVNode;
const vNodeRef = useRef(processingVNode);
useEffect(() => {
return () => {

View File

@ -54,7 +54,7 @@ export type CallBack<F> = {
};
export type Ref<V> = {
current: V | null;
current: V;
};
export type Trigger<A> = (A) => void;