UNPKG

vite-uni-dev-tool

Version:

vite-uni-dev-tool, debug, uni-app, 一处编写,到处调试

307 lines (256 loc) 8.02 kB
import { isBoolean, isNil, isObject, isString, isFunction } from './language'; /** * 自定义set函数 - 安全地设置嵌套对象属性 * @param obj 目标对象 * @param path 属性路径数组 * @param value 要设置的值 */ export function setValueByPath(obj: any, path: string, value: any) { if (obj == null || typeof path !== 'string') { return; } // 支持两种路径格式: "a.b.c" 或 "a[0].b" const segments = path .replace(/\[(\d+)\]/g, '.$1') // 将 a[0] 转换为 a.0 .split('.') .filter((segment) => segment !== ''); // 过滤空段 if (segments.length === 0) { return; } let current = obj; const lastIndex = segments.length - 1; for (let i = 0; i < lastIndex; i++) { const key = segments[i]; // 如果路径不存在且不是最后一个属性,创建对象或数组 if (!current[key]) { // 判断下一级是否为数字索引(数组) const nextKey = segments[i + 1]; current[key] = /^\d+$/.test(nextKey) ? [] : {}; } current = current[key]; } // 设置最终值 current[segments[lastIndex]] = value; } export function getValueByPath(obj: any, path: string, defaultValue?: any) { if (obj == null || typeof path !== 'string') { return defaultValue; } // 支持两种路径格式: "a.b.c" 或 "a[0].b" const segments = path .replace(/\[(\d+)\]/g, '.$1') // 将 a[0] 转换为 a.0 .split('.') .filter((segment) => segment !== ''); // 过滤空段 let current = obj; for (const segment of segments) { if (current == null) { return defaultValue; } current = current[segment]; } return current !== undefined ? current : defaultValue; } /** * 计算对象在内存中的近似大小 * @param obj 要计算大小的对象 * @returns 对象的近似大小(以字节为单位) */ export function calculateObjectSize(obj: any): number { obj = parseValue(obj); // 处理基本类型 if (obj === null || obj === undefined) { return 0; } // 处理原始类型 const type = typeof obj; if (type === 'boolean') { return 4; // 布尔值通常占4字节 } if (type === 'number') { return 8; // JavaScript 数字是双精度浮点数(8字节) } if (type === 'string') { // 假设每个 UTF-16 字符占2字节 return obj.length * 2; } if (type === 'symbol') { // 符号在内存中的大小通常与引用大小相同 return 8; } // 处理日期对象 if (obj instanceof Date) { return 8; // 日期内部表示为时间戳(8字节) } // 处理正则表达式 if (obj instanceof RegExp) { return calculateObjectSize(obj.source) + calculateObjectSize(obj.flags); } // 处理数组和普通对象 let totalSize = 0; // 对象头部信息(简化估算) totalSize += 24; // 假设对象头部占24字节 if (Array.isArray(obj)) { // 数组的长度属性 totalSize += 4; // 计算每个元素的大小 for (const item of obj) { // 数组元素引用(假设每个引用占8字节) totalSize += 8; // 元素本身的大小 totalSize += calculateObjectSize(item); } } else { // 计算对象每个属性的大小 for (const key in obj) { if (obj?.hasOwnProperty?.(key)) { // 属性名的大小(假设每个字符占2字节) totalSize += key.length * 2; // 属性引用(假设每个引用占8字节) totalSize += 8; // 属性值的大小 totalSize += calculateObjectSize(obj[key]); } } } return totalSize; } /** * 存储单位转换 * @param bytes 字节数 * @param options 转换选项 * @returns 转换后的存储大小字符串 */ interface FormatStorageOptions { precision?: number; // 精度,默认为2 useBinary?: boolean; // 是否使用二进制单位,默认为true includeUnit?: boolean; // 是否包含单位,默认为true } export function formatStorageSize( bytes: number, options: FormatStorageOptions = {}, ): string { const { precision = 2, useBinary = true, includeUnit = true } = options; if (bytes === 0) { return includeUnit ? `0 Bytes` : `0`; } const units = useBinary ? ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'] : ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; const base = useBinary ? 1024 : 1000; let unitIndex = 0; while (bytes >= base && unitIndex < units.length - 1) { bytes /= base; unitIndex++; } const formattedSize = bytes.toFixed(precision); return includeUnit ? `${formattedSize} ${units[unitIndex]}` : formattedSize; } /** * 序列化包含循环引用的对象 * @param {Object} obj - 要序列化的对象 * @param {WeakMap} [visited] - 用于记录已处理的对象(内部递归使用) * @returns {string} - 序列化后的字符串 */ export function serializeCircular( obj: any, options: { maxDepth: number; visited: WeakMap<any, true> } = { maxDepth: 10, visited: new WeakMap(), }, ): string { const { maxDepth, visited } = options; // 处理 null 和基本类型 if (obj === null || typeof obj !== 'object') { return JSON.stringify(obj); } // 检测循环引用 if (visited.has(obj)) { return '[Circular]'; } // 记录当前对象已处理 visited.set(obj, true); // 处理日期对象 if (obj instanceof Date) { return `"${obj.toISOString()}"`; } // 处理正则表达式 if (obj instanceof RegExp) { return `/^${obj.source}$/${obj.flags}`; } // 检查是否达到最大深度 if (maxDepth <= 0) { return typeof obj === 'object' ? '{}' : '[]'; } // 递归处理数组 if (Array.isArray(obj)) { const serializedItems = obj.map((item) => serializeCircular(item, { maxDepth: maxDepth - 1, visited }), ); return `[${serializedItems.join(', ')}]`; } // 递归处理普通对象 const keys = Object.keys(obj); const serializedProps = keys.map((key) => { const value = serializeCircular(obj[key], { maxDepth: maxDepth - 1, visited, }); return `"${key}": ${value}`; }); return `{${serializedProps.join(', ')}}`; } /** * 处理value * 基础类型数据直接返回value * 引用类型数据判断 value 是否存在循环引用的情况,存在则将循环引用的第二次赋值为 [Circular] * 最大处理 deep 层数 * * @export * @param {*} value * @param {number} [deep=6] */ export function parseValue(value: any, deep: number = 6) { const seen = new WeakMap<object, string[]>(); // 存储对象及其路径 function process(value: any, currentDeep: number, path: string[] = []): any { if (isString(value)) { return `${value}`; } if (isBoolean(value)) { return `${value}`; } if (isFunction(value)) { return `f(...) { ... }`; } if (isNil(value)) { return `${value}`; } // 处理基本类型 if (!isObject(value)) { return value; } // 检查是否达到最大深度 if (currentDeep <= 0) { return '[MaxDepth]'; } // 检查循环引用 if (seen.has(value)) { const circularPath = seen.get(value)!.join('.'); return `[Circular: ${circularPath}]`; } // 标记当前对象为已访问,并记录路径 seen.set(value, [...path]); // 处理数组 if (Array.isArray(value)) { return value.map((item, index) => process(item, currentDeep - 1, [...path, String(index)]), ); } // 处理对象 const result: Record<string, any> = {}; for (const [key, val] of Object.entries(value)) { result[key] = process(val, currentDeep - 1, [...path, key]); } return result; } return process(value, deep); }