资源管理 - 知蛛网1. 内存管理
垃圾回收机制
// 1. 引用计数(早期)
let obj1 = { name: "obj1" };
let obj2 = obj1; // obj1 引用计数: 2
obj1 = null; // obj1 引用计数: 1
obj2 = null; // obj1 引用计数: 0 → 可回收
// 2. 标记清除(现代)
function createObjects() {
let objA = { data: "A" };
let objB = { data: "B" };
objA.ref = objB;
objB.ref = objA;
// 形成循环引用,但标记清除算法能处理
}
createObjects(); // 函数执行后,objA 和 objB 被标记为不可达
// 3. 手动释放资源
class ResourceHandler {
constructor() {
this.resources = new Set();
}
add(resource) {
this.resources.add(resource);
}
dispose() {
for (const resource of this.resources) {
// 手动清理
if (resource.close) resource.close();
if (resource.disconnect) resource.disconnect();
if (resource.removeAllListeners) resource.removeAllListeners();
}
this.resources.clear();
}
}
内存泄漏检测和预防
// 常见内存泄漏场景及解决方案
// 1. 意外的全局变量
function leak1() {
leakVar = "I am leaked"; // 没有 var/let/const
this.leaked = true; // this 指向全局
}
// 解决方案:使用严格模式
("use strict");
function safe1() {
// leakVar = 'error'; // 报错
}
// 2. 闭包未清理
function createClosure() {
const largeArray = new Array(1000000).fill("data");
return function () {
console.log("闭包引用 largeArray"); // largeArray 不会被释放
};
}
// 解决方案:及时解除引用
let closure = createClosure();
closure = null; // 解除引用
// 3. DOM 引用未清理
class ImageGallery {
constructor() {
this.images = [];
this.elements = new WeakMap(); // 使用 WeakMap 避免内存泄漏
}
addImage(src) {
const img = new Image();
img.src = src;
this.images.push(img);
this.elements.set(img, document.createElement("div"));
}
clear() {
this.images.length = 0; // 清空数组
// WeakMap 中的引用会自动清理
}
}
// 4. 定时器未清理
class TimerManager {
constructor() {
this.timers = new Map();
}
setTimer(id, callback, interval) {
const timerId = setInterval(callback, interval);
this.timers.set(id, timerId);
return id;
}
clearTimer(id) {
if (this.timers.has(id)) {
clearInterval(this.timers.get(id));
this.timers.delete(id);
}
}
clearAll() {
for (const timerId of this.timers.values()) {
clearInterval(timerId);
}
this.timers.clear();
}
}
// 5. 事件监听器未移除
class EventManager {
constructor() {
this.handlers = new Map();
}
addListener(element, event, handler, options) {
element.addEventListener(event, handler, options);
if (!this.handlers.has(element)) {
this.handlers.set(element, []);
}
this.handlers.get(element).push({ event, handler });
// 自动移除(当元素从 DOM 移除时)
const observer = new MutationObserver(() => {
if (!document.contains(element)) {
this.removeAllListeners(element);
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
removeAllListeners(element) {
if (this.handlers.has(element)) {
this.handlers.get(element).forEach(({ event, handler }) => {
element.removeEventListener(event, handler);
});
this.handlers.delete(element);
}
}
}
内存监控
// 性能监控
class MemoryMonitor {
constructor() {
this.leaks = new Set();
this.snapshots = new Map();
}
// 记录对象引用
track(obj, label) {
const tracked = {
label,
timestamp: Date.now(),
stack: new Error().stack,
};
this.leaks.add(tracked);
// 返回代理对象
return new Proxy(obj, {
get(target, prop) {
return target[prop];
},
set(target, prop, value) {
target[prop] = value;
return true;
},
});
}
// 内存快照
takeSnapshot(label) {
if (performance.memory) {
const memory = performance.memory;
this.snapshots.set(label, {
usedJSHeapSize: memory.usedJSHeapSize,
totalJSHeapSize: memory.totalJSHeapSize,
jsHeapSizeLimit: memory.jsHeapSizeLimit,
timestamp: Date.now(),
});
}
}
// 报告内存使用情况
report() {
const report = {
timestamp: Date.now(),
leaks: Array.from(this.leaks),
snapshots: Array.from(this.snapshots.entries()),
};
if (performance.memory) {
report.currentMemory = performance.memory;
}
return report;
}
}
// 使用示例
const monitor = new MemoryMonitor();
monitor.takeSnapshot("初始状态");
const largeObject = monitor.track({ data: new Array(10000).fill("test") }, "大数据对象");
setTimeout(() => {
monitor.takeSnapshot("执行后");
console.log(monitor.report());
}, 1000);
2. 网络资源管理
请求并发控制
class RequestScheduler {
constructor(maxConcurrent = 6) {
this.maxConcurrent = maxConcurrent;
this.queue = [];
this.active = new Set();
this.counter = 0;
}
async request(url, options = {}) {
return new Promise((resolve, reject) => {
const request = {
id: ++this.counter,
url,
options,
resolve,
reject,
status: "pending",
};
this.queue.push(request);
this.processQueue();
return request.id;
});
}
async processQueue() {
while (this.active.size < this.maxConcurrent && this.queue.length > 0) {
const request = this.queue.shift();
this.active.add(request.id);
request.status = "active";
this.executeRequest(request).finally(() => {
this.active.delete(request.id);
this.processQueue();
});
}
}
async executeRequest(request) {
try {
const startTime = Date.now();
const response = await fetch(request.url, request.options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
const duration = Date.now() - startTime;
request.resolve({
data,
duration,
status: response.status,
});
request.status = "completed";
} catch (error) {
request.reject(error);
request.status = "failed";
}
}
cancelRequest(requestId) {
const index = this.queue.findIndex(r => r.id === requestId);
if (index !== -1) {
const [request] = this.queue.splice(index, 1);
request.reject(new Error("请求被取消"));
}
}
getStats() {
return {
queueLength: this.queue.length,
activeCount: this.active.size,
totalProcessed: this.counter,
};
}
}
// 使用示例
const scheduler = new RequestScheduler(3);
const urls = [
"https://api.example.com/data1",
"https://api.example.com/data2",
// ...更多URL
];
// 批量请求
const promises = urls.map(url => scheduler.request(url));
Promise.allSettled(promises).then(results => {
console.log("所有请求完成", results);
});
资源预加载和懒加载
class ResourceManager {
constructor() {
this.cache = new Map();
this.prefetchQueue = [];
this.loading = new Set();
}
// 预加载资源
prefetch(resource, priority = "low") {
const item = {
resource,
priority,
promise: null,
timestamp: Date.now(),
};
this.prefetchQueue.push(item);
this.prefetchQueue.sort((a, b) => {
const priorityOrder = { high: 0, medium: 1, low: 2 };
return priorityOrder[a.priority] - priorityOrder[b.priority];
});
return this.processPrefetch();
}
async processPrefetch() {
while (this.prefetchQueue.length > 0 && this.loading.size < 3) {
const item = this.prefetchQueue.shift();
if (!this.cache.has(item.resource) && !this.loading.has(item.resource)) {
this.loading.add(item.resource);
item.promise = this.loadResource(item.resource).finally(() =>
this.loading.delete(item.resource)
);
}
}
}
async loadResource(url) {
// 检查缓存
if (this.cache.has(url)) {
return this.cache.get(url);
}
try {
const response = await fetch(url);
const data = await response.blob();
this.cache.set(url, data);
return data;
} catch (error) {
console.error(`加载资源失败: ${url}`, error);
throw error;
}
}
// 图片懒加载
setupLazyLoading(selector = "img[data-src]") {
const observer = new IntersectionObserver(
entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
const src = img.dataset.src;
this.loadResource(src).then(blob => {
const objectURL = URL.createObjectURL(blob);
img.src = objectURL;
img.removeAttribute("data-src");
// 清理 Blob URL
img.onload = () => URL.revokeObjectURL(objectURL);
});
observer.unobserve(img);
}
});
},
{
rootMargin: "50px", // 提前50px加载
threshold: 0.1,
}
);
document.querySelectorAll(selector).forEach(img => {
observer.observe(img);
});
return observer;
}
// 清理缓存
clearCache(maxAge = 3600000) {
// 1小时
const now = Date.now();
for (const [url, cached] of this.cache.entries()) {
if (now - cached.timestamp > maxAge) {
this.cache.delete(url);
}
}
}
}
资源优先级管理
class ResourcePriorityManager {
constructor() {
this.resources = new Map();
this.visibility = document.visibilityState;
// 监听页面可见性变化
document.addEventListener("visibilitychange", () => {
this.visibility = document.visibilityState;
this.adjustPriorities();
});
// 监听网络状态
this.networkInfo = navigator.connection;
if (this.networkInfo) {
this.networkInfo.addEventListener("change", () => {
this.adjustPriorities();
});
}
}
registerResource(type, url, options = {}) {
const resource = {
type,
url,
priority: options.priority || "auto",
size: options.size || 0,
dependencies: options.dependencies || [],
loaded: false,
loading: false,
};
this.resources.set(url, resource);
return resource;
}
calculatePriority(resource) {
let score = 0;
// 基于类型
const typeScores = {
"critical-css": 100,
"above-the-fold-image": 90,
font: 80,
css: 70,
javascript: 60,
"below-the-fold-image": 50,
video: 40,
audio: 30,
analytics: 20,
};
score += typeScores[resource.type] || 50;
// 基于网络条件调整
if (this.networkInfo) {
const { effectiveType, saveData } = this.networkInfo;
if (saveData) {
score -= 30; // 省流量模式降低优先级
}
if (effectiveType === "slow-2g" || effectiveType === "2g") {
if (resource.size > 100000) {
// 大于100KB
score -= 40;
}
}
}
// 页面不可见时降低非关键资源优先级
if (this.visibility === "hidden" && resource.priority !== "high") {
score -= 20;
}
return Math.max(1, Math.min(100, score));
}
adjustPriorities() {
for (const resource of this.resources.values()) {
if (!resource.loaded && !resource.loading) {
resource.calculatedPriority = this.calculatePriority(resource);
}
}
}
getLoadQueue() {
const queue = Array.from(this.resources.values())
.filter(r => !r.loaded && !r.loading)
.sort((a, b) => b.calculatedPriority - a.calculatedPriority);
return queue;
}
}
3. 缓存策略
浏览器缓存管理
class CacheManager {
constructor(cacheName = "app-cache") {
this.cacheName = cacheName;
this.cache = null;
}
async open() {
if (!this.cache) {
this.cache = await caches.open(this.cacheName);
}
return this.cache;
}
async cacheFirst(request, options = {}) {
await this.open();
// 1. 尝试从缓存获取
const cachedResponse = await this.cache.match(request);
if (cachedResponse) {
// 检查缓存是否过期
if (!this.isExpired(cachedResponse, options.maxAge)) {
return cachedResponse;
}
}
// 2. 网络请求
try {
const networkResponse = await fetch(request);
// 缓存新响应
if (networkResponse.ok) {
await this.cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
// 3. 网络失败,返回缓存(即使过期)
if (cachedResponse) {
return cachedResponse;
}
throw error;
}
}
async networkFirst(request, options = {}) {
await this.open();
try {
// 1. 尝试网络请求
const networkResponse = await fetch(request);
// 更新缓存
if (networkResponse.ok) {
await this.cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
// 2. 网络失败,尝试缓存
const cachedResponse = await this.cache.match(request);
if (cachedResponse) {
return cachedResponse;
}
throw error;
}
}
async staleWhileRevalidate(request, options = {}) {
await this.open();
// 1. 立即返回缓存(如果有)
const cachedResponse = await this.cache.match(request);
// 2. 在后台更新缓存
fetch(request)
.then(async networkResponse => {
if (networkResponse.ok) {
await this.cache.put(request, networkResponse.clone());
}
})
.catch(error => console.error("后台更新失败:", error));
// 3. 返回缓存或网络响应
if (cachedResponse && !this.isExpired(cachedResponse, options.staleAge)) {
return cachedResponse;
}
// 等待网络响应
const networkResponse = await fetch(request);
if (networkResponse.ok) {
await this.cache.put(request, networkResponse.clone());
}
return networkResponse;
}
isExpired(response, maxAge = 3600000) {
const dateHeader = response.headers.get("date");
if (!dateHeader) return false;
const date = new Date(dateHeader).getTime();
const now = Date.now();
return now - date > maxAge;
}
async clearOldCaches() {
const cacheNames = await caches.keys();
const deletions = cacheNames
.filter(name => name !== this.cacheName)
.map(name => caches.delete(name));
return Promise.all(deletions);
}
async getStats() {
await this.open();
const keys = await this.cache.keys();
let totalSize = 0;
const stats = [];
for (const request of keys) {
const response = await this.cache.match(request);
if (response) {
const blob = await response.blob();
const size = blob.size;
totalSize += size;
stats.push({
url: request.url,
size,
type: response.headers.get("content-type"),
date: response.headers.get("date"),
});
}
}
return {
totalSize,
count: keys.length,
resources: stats,
};
}
}
内存缓存(LRU)
class LRUCache {
constructor(capacity = 100) {
this.capacity = capacity;
this.cache = new Map();
this.accessOrder = [];
}
get(key) {
if (!this.cache.has(key)) {
return null;
}
// 更新访问顺序
this.updateAccessOrder(key);
return this.cache.get(key).value;
}
set(key, value, options = {}) {
const entry = {
value,
timestamp: Date.now(),
ttl: options.ttl, // 生存时间(毫秒)
};
if (this.cache.has(key)) {
// 更新现有项
this.cache.set(key, entry);
this.updateAccessOrder(key);
} else {
// 添加新项
if (this.cache.size >= this.capacity) {
this.evict();
}
this.cache.set(key, entry);
this.accessOrder.push(key);
}
return true;
}
updateAccessOrder(key) {
const index = this.accessOrder.indexOf(key);
if (index > -1) {
this.accessOrder.splice(index, 1);
}
this.accessOrder.push(key);
}
evict() {
while (this.cache.size >= this.capacity && this.accessOrder.length > 0) {
const oldestKey = this.accessOrder.shift();
// 检查是否过期
const entry = this.cache.get(oldestKey);
if (!this.isExpired(entry)) {
this.cache.delete(oldestKey);
}
}
}
isExpired(entry) {
if (!entry.ttl) return false;
return Date.now() - entry.timestamp > entry.ttl;
}
clearExpired() {
const now = Date.now();
for (const [key, entry] of this.cache.entries()) {
if (this.isExpired(entry)) {
this.cache.delete(key);
const index = this.accessOrder.indexOf(key);
if (index > -1) {
this.accessOrder.splice(index, 1);
}
}
}
}
clear() {
this.cache.clear();
this.accessOrder.length = 0;
}
getStats() {
let totalSize = 0;
for (const entry of this.cache.values()) {
if (entry.value && typeof entry.value === "string") {
totalSize += entry.value.length * 2; // 估算 UTF-16 大小
}
}
return {
size: this.cache.size,
capacity: this.capacity,
estimatedMemory: totalSize,
hitRate: this.calculateHitRate(),
};
}
calculateHitRate() {
// 实际应用中需要记录命中次数
return 0;
}
}
4. Worker 资源管理
Web Worker 池
class WorkerPool {
constructor(workerScript, size = navigator.hardwareConcurrency || 4) {
this.workerScript = workerScript;
this.size = size;
this.workers = [];
this.queue = [];
this.available = new Set();
this.taskId = 0;
this.initWorkers();
}
initWorkers() {
for (let i = 0; i < this.size; i++) {
const worker = new Worker(this.workerScript);
worker.id = i;
worker.busy = false;
worker.onmessage = event => {
this.handleWorkerResponse(worker, event);
};
worker.onerror = error => {
this.handleWorkerError(worker, error);
};
this.workers.push(worker);
this.available.add(worker);
}
}
async execute(task, data, transfer = []) {
return new Promise((resolve, reject) => {
const taskId = ++this.taskId;
const taskInfo = {
id: taskId,
task,
data,
resolve,
reject,
startTime: Date.now(),
};
this.queue.push(taskInfo);
this.processQueue();
return taskId;
});
}
processQueue() {
while (this.available.size > 0 && this.queue.length > 0) {
const task = this.queue.shift();
const worker = this.available.values().next().value;
this.available.delete(worker);
worker.busy = true;
worker.currentTask = task;
worker.postMessage(
{
task: task.task,
data: task.data,
id: task.id,
},
transfer.length > 0 ? task.transfer : []
);
}
}
handleWorkerResponse(worker, event) {
const { id, result, error } = event.data;
const task = worker.currentTask;
if (task && task.id === id) {
if (error) {
task.reject(new Error(error));
} else {
task.resolve(result);
}
worker.busy = false;
delete worker.currentTask;
this.available.add(worker);
this.processQueue();
}
}
handleWorkerError(worker, error) {
const task = worker.currentTask;
if (task) {
task.reject(error);
worker.busy = false;
delete worker.currentTask;
this.available.add(worker);
}
console.error(`Worker ${worker.id} 错误:`, error);
}
terminate() {
this.workers.forEach(worker => worker.terminate());
this.workers = [];
this.available.clear();
this.queue = [];
}
getStats() {
const busy = this.workers.filter(w => w.busy).length;
return {
total: this.size,
busy,
available: this.size - busy,
queueLength: this.queue.length,
completedTasks: this.taskId,
};
}
}
// Worker 脚本示例
// worker.js
self.onmessage = function (event) {
const { task, data, id } = event.data;
try {
let result;
switch (task) {
case "processImage":
result = processImage(data);
break;
case "heavyCalculation":
result = heavyCalculation(data);
break;
default:
throw new Error(`未知任务: ${task}`);
}
self.postMessage({ id, result });
} catch (error) {
self.postMessage({
id,
error: error.message,
});
}
};
function processImage(data) {
// 图像处理逻辑
return { processed: true, size: data.length };
}
function heavyCalculation(data) {
// 复杂计算
let sum = 0;
for (let i = 0; i < data.length; i++) {
sum += data[i];
}
return sum;
}
5. 数据库资源管理
IndexedDB 封装
class IndexedDBManager {
constructor(dbName, version = 1) {
this.dbName = dbName;
this.version = version;
this.db = null;
this.transactions = new Map();
}
async open() {
if (this.db) return this.db;
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve(this.db);
};
request.onupgradeneeded = event => {
const db = event.target.result;
this.setupStores(db);
};
});
}
setupStores(db) {
// 创建对象存储
if (!db.objectStoreNames.contains("cache")) {
const store = db.createObjectStore("cache", { keyPath: "key" });
store.createIndex("timestamp", "timestamp", { unique: false });
store.createIndex("size", "size", { unique: false });
}
if (!db.objectStoreNames.contains("sessions")) {
const sessionStore = db.createObjectStore("sessions", {
keyPath: "sessionId",
});
sessionStore.createIndex("expires", "expires", { unique: false });
}
}
async addToCache(key, value, options = {}) {
await this.open();
const transaction = this.db.transaction(["cache"], "readwrite");
const store = transaction.objectStore("cache");
const item = {
key,
value,
timestamp: Date.now(),
size: this.calculateSize(value),
ttl: options.ttl || 0,
metadata: options.metadata || {},
};
return new Promise((resolve, reject) => {
const request = store.put(item);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async getFromCache(key) {
await this.open();
const transaction = this.db.transaction(["cache"], "readonly");
const store = transaction.objectStore("cache");
return new Promise((resolve, reject) => {
const request = store.get(key);
request.onsuccess = () => {
const item = request.result;
if (!item) {
resolve(null);
return;
}
// 检查是否过期
if (item.ttl && Date.now() - item.timestamp > item.ttl) {
this.deleteFromCache(key);
resolve(null);
} else {
resolve(item.value);
}
};
request.onerror = () => reject(request.error);
});
}
async deleteFromCache(key) {
await this.open();
const transaction = this.db.transaction(["cache"], "readwrite");
const store = transaction.objectStore("cache");
return new Promise((resolve, reject) => {
const request = store.delete(key);
request.onsuccess = () => resolve(true);
request.onerror = () => reject(request.error);
});
}
async clearExpiredCache() {
await this.open();
const transaction = this.db.transaction(["cache"], "readwrite");
const store = transaction.objectStore("cache");
const index = store.index("timestamp");
const now = Date.now();
const range = IDBKeyRange.upperBound(now - 86400000); // 24小时前
return new Promise((resolve, reject) => {
const request = index.openCursor(range);
let count = 0;
request.onsuccess = event => {
const cursor = event.target.result;
if (cursor) {
const item = cursor.value;
if (item.ttl && now - item.timestamp > item.ttl) {
cursor.delete();
count++;
}
cursor.continue();
} else {
resolve(count);
}
};
request.onerror = () => reject(request.error);
});
}
async getCacheStats() {
await this.open();
const transaction = this.db.transaction(["cache"], "readonly");
const store = transaction.objectStore("cache");
return new Promise((resolve, reject) => {
const request = store.count();
let totalSize = 0;
let count = 0;
request.onsuccess = async () => {
count = request.result;
// 遍历计算总大小
const sizeRequest = store.openCursor();
sizeRequest.onsuccess = event => {
const cursor = event.target.result;
if (cursor) {
totalSize += cursor.value.size || 0;
cursor.continue();
} else {
resolve({
count,
totalSize,
averageSize: count > 0 ? totalSize / count : 0,
});
}
};
sizeRequest.onerror = () => reject(sizeRequest.error);
};
request.onerror = () => reject(request.error);
});
}
calculateSize(obj) {
// 粗略估算对象大小
const json = JSON.stringify(obj);
return new Blob([json]).size;
}
async cleanup() {
if (this.db) {
this.db.close();
this.db = null;
}
// 清理所有事务
for (const transaction of this.transactions.values()) {
if (transaction.isActive) {
transaction.abort();
}
}
this.transactions.clear();
}
}
6. 性能监控和优化
资源加载监控
class ResourceMonitor {
constructor() {
this.entries = new Map();
this.observer = null;
this.startTime = performance.now();
}
startMonitoring() {
if (!window.performance || !performance.getEntriesByType) {
console.warn("Performance API 不支持");
return;
}
// 监听资源加载
this.observer = new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
this.recordEntry(entry);
});
});
this.observer.observe({
entryTypes: ["resource", "navigation", "paint"],
});
// 记录已有资源
const resources = performance.getEntriesByType("resource");
resources.forEach(entry => this.recordEntry(entry));
}
recordEntry(entry) {
const resource = {
name: entry.name,
type: this.getResourceType(entry.name),
duration: entry.duration,
size: entry.transferSize || 0,
startTime: entry.startTime,
initiatorType: entry.initiatorType || "unknown",
protocol: this.getProtocol(entry.name),
};
this.entries.set(entry.name, resource);
}
getResourceType(url) {
const ext = url.split(".").pop().split("?")[0].toLowerCase();
const types = {
js: "javascript",
css: "stylesheet",
png: "image",
jpg: "image",
jpeg: "image",
gif: "image",
svg: "image",
woff: "font",
woff2: "font",
ttf: "font",
eot: "font",
json: "json",
xml: "xml",
html: "document",
pdf: "document",
};
return types[ext] || "other";
}
getProtocol(url) {
return url.startsWith("https") ? "https" : url.startsWith("http") ? "http" : "other";
}
getReport() {
const entries = Array.from(this.entries.values());
const now = performance.now();
// 计算统计信息
const stats = {
totalResources: entries.length,
totalDuration: entries.reduce((sum, e) => sum + e.duration, 0),
totalSize: entries.reduce((sum, e) => sum + e.size, 0),
averageDuration:
entries.length > 0
? entries.reduce((sum, e) => sum + e.duration, 0) / entries.length
: 0,
byType: {},
slowestResources: [],
largestResources: [],
};
// 按类型分组
entries.forEach(entry => {
if (!stats.byType[entry.type]) {
stats.byType[entry.type] = {
count: 0,
totalSize: 0,
totalDuration: 0,
};
}
const typeStats = stats.byType[entry.type];
typeStats.count++;
typeStats.totalSize += entry.size;
typeStats.totalDuration += entry.duration;
});
// 找出最慢的资源
stats.slowestResources = [...entries].sort((a, b) => b.duration - a.duration).slice(0, 10);
// 找出最大的资源
stats.largestResources = [...entries].sort((a, b) => b.size - a.size).slice(0, 10);
// 页面加载时间
const navEntries = performance.getEntriesByType("navigation");
if (navEntries.length > 0) {
const nav = navEntries[0];
stats.pageLoad = {
domContentLoaded: nav.domContentLoadedEventEnd - nav.startTime,
load: nav.loadEventEnd - nav.startTime,
tti: this.calculateTTI(), // 首次可交互时间
};
}
return stats;
}
calculateTTI() {
// 简化版 TTI 计算
const longTasks = performance.getEntriesByType("longtask");
let lastLongTaskEnd = 0;
if (longTasks.length > 0) {
lastLongTaskEnd = Math.max(...longTasks.map(task => task.startTime + task.duration));
}
const nav = performance.getEntriesByType("navigation")[0];
if (nav) {
return Math.max(lastLongTaskEnd, nav.domContentLoadedEventEnd);
}
return lastLongTaskEnd;
}
stopMonitoring() {
if (this.observer) {
this.observer.disconnect();
}
}
clear() {
this.entries.clear();
if (performance.clearResourceTimings) {
performance.clearResourceTimings();
}
}
}
最佳实践汇总
- 内存管理:及时清理引用,使用 WeakMap/WeakSet
- 网络优化:合理控制并发,实现懒加载
- 缓存策略:根据场景选择合适的缓存策略
- Worker 使用:耗时代计算使用 Web Worker
- 监控预警:持续监控资源使用情况
- 优雅降级:网络差时提供降级方案
- 资源预判:根据用户行为预加载资源