limu
Version:
A fast js lib of immutable data, based on shallow copy on read and mark modified on write mechanism
177 lines (176 loc) • 5.41 kB
JavaScript
import { META_KEY, META_VER } from '../support/consts';
import { verWrap } from '../support/inner-data';
import { getDataType, injectMetaProto, noop } from '../support/util';
export const ROOT_CTX = new Map();
export function markModified(meta) {
meta.rootMeta.modified = true;
const doMark = (meta) => {
if (meta && !meta.modified) {
meta.modified = true;
doMark(meta.parentMeta);
}
};
doMark(meta);
}
export function attachMeta(dataNode, meta, options) {
if (options.apiCtx.debug) {
const { fast } = options;
// speed up read performance when debug is true, especially for array forEach scene
if (fast) {
dataNode[META_KEY] = meta;
}
else {
injectMetaProto(dataNode);
dataNode.__proto__[META_KEY] = meta;
}
}
return dataNode;
}
export function getKeyPath(draftNode, curKey, apiCtx) {
const pathArr = [curKey];
const meta = getSafeDraftMeta(draftNode, apiCtx);
if (meta && meta.level > 0) {
const { keyPath } = meta;
return [...keyPath, curKey];
}
return pathArr;
}
export function newMeta(key, baseData, options) {
const { ver, parentMeta = null, immutBase, compareVer, apiCtx, hasOnOperate } = options;
const dataType = getDataType(baseData);
let keyPath = [];
let level = 0;
let copy = null;
if (parentMeta) {
copy = parentMeta.copy;
level = getNextMetaLevel(copy, apiCtx);
keyPath = getKeyPath(copy, key, apiCtx);
}
const meta = {
// @ts-ignore add later
rootMeta: null,
parentMeta,
parent: copy,
selfType: dataType,
self: baseData,
// @ts-ignore add later
copy: null,
key,
keyPath,
level,
// @ts-ignore add later
/** @type any */
proxyVal: null,
proxyItems: null,
modified: false,
scopes: [],
isImmutBase: immutBase,
isDel: false,
isFast: false,
isArrOrderChanged: false,
newNodeStats: {},
newNodeMap: new Map(),
newNodes: [],
ver,
compareVer,
revoke: noop,
hasOnOperate,
execOnOperate: noop,
};
if (level === 0) {
meta.rootMeta = meta;
}
else {
meta.rootMeta = parentMeta.rootMeta;
}
return meta;
}
/**
* 是否是一个草稿对象代理节点
*/
export function isDraft(mayDraft) {
const meta = getDraftProxyMeta(mayDraft);
if (!meta) {
return false;
}
return !meta.isImmutBase;
}
export function genMetaVer() {
if (verWrap.value >= Number.MAX_SAFE_INTEGER) {
verWrap.value = 1;
verWrap.usablePrefix += 1;
}
else {
verWrap.value += 1;
}
const { value, usablePrefix } = verWrap;
const metaVer = `${usablePrefix}_${value}`;
return metaVer;
}
export function getNextMetaLevel(mayContainMetaObj, apiCtx) {
const meta = getDraftMeta(mayContainMetaObj, apiCtx);
return meta ? meta.level + 1 : 1;
}
export function getSafeDraftMeta(proxyDraft, apiCtx) {
// @ts-ignore
return apiCtx.metaMap.get(proxyDraft);
}
export function getDraftMeta(proxyDraft, apiCtx) {
let apiCtxVar = apiCtx || getApiCtx(proxyDraft);
return (apiCtxVar === null || apiCtxVar === void 0 ? void 0 : apiCtxVar.metaMap.get(proxyDraft)) || null;
}
export function getMetaVer(mayDraftProxy) {
return mayDraftProxy ? mayDraftProxy[META_VER] || '' : '';
}
export function getApiCtx(mayDraftProxy) {
const ver = getMetaVer(mayDraftProxy);
return ROOT_CTX.get(ver) || null;
}
export function getDraftProxyMeta(mayDraftProxy) {
const apiCtx = getApiCtx(mayDraftProxy);
if (!apiCtx) {
return null;
}
return apiCtx.metaMap.get(mayDraftProxy) || null;
}
/**
* 判断两个值是否相同,true 表示不相等,false 表示相等
*/
export function isDiff(val1, val2) {
const meta1 = getDraftProxyMeta(val1);
const meta2 = getDraftProxyMeta(val2);
if (!meta1 && !meta2) {
return !Object.is(val1, val2);
}
const { self: self1, modified: modified1, compareVer: cv1, ver: ver1, level: level1, } = meta1 || { self: val1, modified: false, compareVer: false, ver: '0', level: 0 };
const { self: self2, modified: modified2, compareVer: cv2, ver: ver2, level: level2, } = meta2 || { self: val2, modified: false, compareVer: false, ver: '0', level: 0 };
if (self1 !== self2) {
// self 是内部维护的值,可不用 Object.is 判断
return true;
}
if ((cv1 || cv2) && (level1 === 0 || level2 === 0) && ver1 !== ver2) {
return true;
}
return modified1 || modified2;
}
/**
* 浅比较两个对象,除了专用于比较 helux 生成的代理对象,此函数还可以比较普通对象
* ```txt
* true:两个对象一样
* false:两个对象不一样
* ```
*/
export function shallowCompare(prevObj, nextObj, compareLimuProxyRaw = true) {
const diffFn = compareLimuProxyRaw ? isDiff : Object.is;
const isObjDiff = (a, b) => {
for (let i in a)
if (!(i in b))
return true;
for (let i in b)
if (diffFn(a[i], b[i]))
return true;
return false;
};
const isEqual = !isObjDiff(prevObj, nextObj);
return isEqual;
}