“使用IndexedDB实现客户端分页查询的方法_高效存储与快速检索技巧”

2025-05-03 24

Image

IndexedDB客户端分页查询实现方法

IndexedDB是一种浏览器内置的NoSQL数据库,适合在客户端存储大量结构化数据。以下是实现分页查询的几种方法:

基本分页实现

1. 使用游标(cursor)和偏移量(offset)

function getPaginatedData(dbName, storeName, pageNumber, pageSize) {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(dbName);
    
    request.onsuccess = (event) => {
      const db = event.target.result;
      const transaction = db.transaction(storeName, 'readonly');
      const store = transaction.objectStore(storeName);
      const data = [];
      let advanced = false;
      let counter = 0;
      
      // 计算跳过多少条记录
      const skip = (pageNumber - 1) * pageSize;
      
      store.openCursor().onsuccess = (cursorEvent) => {
        const cursor = cursorEvent.target.result;
        
        if (!cursor) {
          resolve(data);
          return;
        }
        
        // 跳过前N条记录
        if (!advanced && skip > 0) {
          advanced = true;
          cursor.advance(skip);
          return;
        }
        
        // 收集当前页数据
        if (counter < pageSize) {
          data.push(cursor.value);
          counter++;
          cursor.continue();
        } else {
          resolve(data);
        }
      };
      
      store.openCursor().onerror = (error) => {
        reject(error);
      };
    };
    
    request.onerror = (event) => {
      reject(event.target.error);
    };
  });
}

2. 使用IDBKeyRange和索引

function getPaginatedDataByIndex(dbName, storeName, indexName, pageNumber, pageSize) {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(dbName);
    
    request.onsuccess = (event) => {
      const db = event.target.result;
      const transaction = db.transaction(storeName, 'readonly');
      const store = transaction.objectStore(storeName);
      const index = store.index(indexName);
      const data = [];
      let counter = 0;
      
      const skip = (pageNumber - 1) * pageSize;
      const upperBound = skip + pageSize;
      
      // 使用键范围获取特定范围内的记录
      const keyRange = IDBKeyRange.bound(skip, upperBound, false, true);
      
      index.openCursor(keyRange).onsuccess = (cursorEvent) => {
        const cursor = cursorEvent.target.result;
        
        if (cursor) {
          data.push(cursor.value);
          cursor.continue();
        } else {
          resolve(data);
        }
      };
      
      index.openCursor(keyRange).onerror = (error) => {
        reject(error);
      };
    };
    
    request.onerror = (event) => {
      reject(event.target.error);
    };
  });
}

优化分页实现

1. 带排序的分页

function getSortedPaginatedData(dbName, storeName, indexName, pageNumber, pageSize, direction = 'next') {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(dbName);
    
    request.onsuccess = (event) => {
      const db = event.target.result;
      const transaction = db.transaction(storeName, 'readonly');
      const store = transaction.objectStore(storeName);
      const index = store.index(indexName);
      const data = [];
      let counter = 0;
      const skip = (pageNumber - 1) * pageSize;
      
      const cursorRequest = direction === 'prev' 
        ? index.openCursor(null, 'prev') 
        : index.openCursor();
      
      cursorRequest.onsuccess = (cursorEvent) => {
        const cursor = cursorEvent.target.result;
        
        if (!cursor) {
          resolve(data);
          return;
        }
        
        // 跳过前N条记录
        if (counter < skip) {
          counter++;
          cursor.continue();
          return;
        }
        
        // 收集当前页数据
        if (data.length < pageSize) {
          data.push(cursor.value);
          cursor.continue();
        } else {
          resolve(data);
        }
      };
      
      cursorRequest.onerror = (error) => {
        reject(error);
      };
    };
    
    request.onerror = (event) => {
      reject(event.target.error);
    };
  });
}

2. 带过滤的分页

function getFilteredPaginatedData(dbName, storeName, filterFn, pageNumber, pageSize) {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(dbName);
    
    request.onsuccess = (event) => {
      const db = event.target.result;
      const transaction = db.transaction(storeName, 'readonly');
      const store = transaction.objectStore(storeName);
      const data = [];
      let filteredCounter = 0;
      let pageCounter = 0;
      const skip = (pageNumber - 1) * pageSize;
      
      store.openCursor().onsuccess = (cursorEvent) => {
        const cursor = cursorEvent.target.result;
        
        if (!cursor) {
          resolve(data);
          return;
        }
        
        // 应用过滤函数
        if (filterFn(cursor.value)) {
          // 跳过前N条过滤后的记录
          if (filteredCounter < skip) {
            filteredCounter++;
            cursor.continue();
            return;
          }
          
          // 收集当前页数据
          if (pageCounter < pageSize) {
            data.push(cursor.value);
            pageCounter++;
            cursor.continue();
          } else {
            resolve(data);
          }
        } else {
          cursor.continue();
        }
      };
      
      store.openCursor().onerror = (error) => {
        reject(error);
      };
    };
    
    request.onerror = (event) => {
      reject(event.target.error);
    };
  });
}

使用建议

  1. 索引优化:为经常用于排序或过滤的字段创建索引
  2. 批量处理:对于大数据集,考虑使用IDBKeyRange分批处理
  3. 内存管理:避免一次性加载过多数据到内存
  4. 错误处理:始终添加适当的错误处理逻辑
  5. 事务管理:合理使用事务,避免长时间占用数据库连接

示例调用

// 打开或创建数据库
const dbRequest = indexedDB.open('MyDatabase', 1);

dbRequest.onupgradeneeded = (event) => {
  const db = event.target.result;
  const store = db.createObjectStore('customers', { keyPath: 'id' });
  store.createIndex('name', 'name', { unique: false });
};

// 使用分页函数
getPaginatedData('MyDatabase', 'customers', 2, 10)
  .then(data => {
    console.log('Page 2 data:', data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

以上方法提供了IndexedDB实现分页查询的几种不同方式,您可以根据具体需求选择最适合的方案。

// 来源:https://www.nzw6.com

1. 本站所有资源来源于用户上传和网络,因此不包含技术服务请大家谅解!如有侵权请邮件联系客服!cheeksyu@vip.qq.com
2. 本站不保证所提供下载的资源的准确性、安全性和完整性,资源仅供下载学习之用!如有链接无法下载、失效或广告,请联系客服处理!
3. 您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源!如用于商业或者非法用途,与本站无关,一切后果请用户自负!
4. 如果您也有好的资源或教程,您可以投稿发布,成功分享后有积分奖励和额外收入!
5.严禁将资源用于任何违法犯罪行为,不得违反国家法律,否则责任自负,一切法律责任与本站无关