UNPKG

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
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; }