客户端存储 —— IndexedDB 实现分页查询(下)

简介: 客户端存储 —— IndexedDB 实现分页查询

使用索引

let storeName = "productions"
  let store = db.transaction(storeName, 'readwrite').objectStore(storeName)
  let request = store.index('price').get('72.57');
  request.onsuccess = (event) => {
    console.log(event.target.result);
  }
  request.onerror = (event) => {
    console.log(`query_index error with code:${event.target.errorCode}`);
  }
复制代码

游标查询 —— store.openCursor() + cursor.continue()

从上面可以知道,使用事务通过一个已知键或者索引都可以取得一条记录,但如果想取得多条数据,那就需要在事务中创建一个游标.

let cursorRequest = store.openCursor();
cursorRequest.onsuccess = (event) => {
    let cursor = event.target.result;
    // 当游标 cursor 值不存在时,代表游标查找结束或出现异常,所以需要提前判断
    if (!cursor) return;
    console.log(cursor.value);
    // 在本次游标指向中,通过显示调用 continue 方法就可以将游标指向下一个数据
    cursor.continue();
}
cursorRequest.onerror = (event) => {
    console.log(`query_cursor error with code:${event.target.errorCode}`);
}
复制代码

范围查找 —— index(索引) + cursor(游标) + IDBKeyRange(键范围)

IDBKeyRange(键范围)

  • IDBKeyRange.only(1643607832742) 得到对应键值的记录
  • IDBKeyRange.lowerBound(1643607832742, false) 得到对应键值的下限(开始位置),第二个参数:
  • false ——  从 1643607832742 记录开始,直到最后
  • true —— 从 1643607832742 的下一条记录开始,直到最后
  • IDBKeyRange.upperBound(1643607832742, false) 得到对应键值的上限(结束位置),第二个参数:
  • false ——  从头开始,到 1643607832742 记录为止
  • true —— 从头开始,到 1643607832742 的前一条记录为止
  • IDBKeyRange.bound() —— 同时指定上下限,接收四个参数:下限键上限键可选布尔值 表示是否 跳过下限可选布尔值 是否 跳过上限

使用这样的查找方式更加理想,因为这样的方式可以实现根据指定索引在指定范围内开始游标查询,实现比较合理的范围查找.

let request = store.index('price').openCursor(IDBKeyRange.only(1643607832742));
request.onsuccess = (event) => {
    let cursor = event.target.result;
    if (!cursor) return;
    console.log(cursor.value);
    cursor.continue();
}
request.onerror = (event) => {
    console.log(`query_idnex_cursor error with code:${event.target.errorCode}`);
}
复制代码

实现分页查询

模拟数据

要实现查询的前提是得先有数据,这里就通过 setTimeout 模拟异步请求获取数据:

const getData = () => {
  setTimeout(() => {
    const list = []
    const now = Date.now()
    for (let index = 0; index < 100; index++) {
      let price = Math.random() * 100
      price = price < 10 ? price + 10 : price
      list.push({
        id: now + index,
        name: `产品_${index + 1}`,
        price: price.toFixed(2)
      })
    }
    data.value = list
  })
}
复制代码

然后通过循环调用 add() 方法将数写入到数据库中

const inser = (data = []) => {
  let objectStore = db.transaction(storeName, 'readwrite').objectStore(storeName)
  data.forEach(item => objectStore.add({ ...item }))
  console.log('数据插入成功!')
} 
复制代码

分页效果

实现效果如下

image.png

主要代码

useIndexDB.js

let db = null
let storeName = 'test_db'
let lastRecords = []
let currSize = 0
let indexArr = ["name", "price"]
const getStoreByTransaction = () => db.transaction(storeName, 'readwrite').objectStore(storeName)
const query = (payload) => {
  return new Promise((resovle, reject) => {
    let records = []
    let { name, value, type, size = 10 } = payload || {}
    let objectStore = getStoreByTransaction()
    let request
    if (type) {
      request = type === 'next'
        ? objectStore.openCursor(IDBKeyRange.lowerBound(lastRecords[lastRecords.length - 1].id, type))
        : objectStore.openCursor(IDBKeyRange.upperBound(lastRecords[0].id, true), type)
    } else {
      request = name
        ? objectStore.index(name).openCursor(IDBKeyRange.only(value), type)
        : objectStore.openCursor()
    }
    lastRecords = []
    request.onsuccess = (event) => {
      let cursor = event.target.result
      if (!cursor || currSize >= size) {
        currSize = 0
        resovle(records)
        return
      }
      let lastItem = cursor.value
      lastRecords.push(lastItem)
      records.push(lastItem)
      currSize++
      if (!name) {
        cursor.continue()
      } else {
        currSize = 0
        resovle(records)
      }
    }
    request.onerror = (event) => {
      reject(`query_idnex_cursor error with code:${event.target.errorCode}`)
    }
  })
}
const inser = (data = []) => {
  let objectStore = db.transaction(storeName, 'readwrite').objectStore(storeName)
  data.forEach(item => objectStore.add({ ...item }))
  console.log('数据插入成功!')
}
const open = (payload) => {
  payload = payload || { name: 'test', version: 1, storeName: "productions" }
  return new Promise((resovle, reject) => {
    let request = indexedDB.open(payload.name, payload.version)
    storeName = payload.storeName
    request.onsuccess = (event) => {
      db = event.target.result
      resovle({
        query,
        inser,
        getStoreByTransaction,
        db
      })
      console.log(`Database connection successfully established`)
    }
    request.onerror = (event) => {
      reject(`Failed to open: ${event.target.errorCode}`)
    }
    request.onupgradeneeded = (event) => {
      db = event.target.result
      if (db.objectStoreNames.contains("productions")) {
        db.deleteObjectStore("productions")
      }
      let objectStore = db.createObjectStore("productions", {
        keyPath: "id"
      })
      indexArr.forEach((name) => {
        objectStore.createIndex(name, name, {
          unique: false
        })
      })
    }
  })
}
export default open
复制代码

App.vue

<script setup lang="ts">
import { reactive, ref, onMounted, computed } from "vue"
import openDB from "./useIndexDB.js";
const data = ref([])
const queryResult = ref([])
const formItem = reactive({ price: '', name: '' })
let dbObj = null
onMounted(() => {
  openDB()
    .then(({ query, inser }) => {
      dbObj = { query, inser }
    })
})
const getData = () => {
  setTimeout(() => {
    const list = []
    const now = Date.now()
    for (let index = 0; index < 100; index++) {
      let price = Math.random() * 100
      price = price < 10 ? price + 10 : price
      list.push({
        id: now + index,
        name: `产品_${index + 1}`,
        price: price.toFixed(2)
      })
    }
    data.value = list
    console.log("数据获取成功!", data.value);
  })
}
const insertData = () => {
  dbObj.inser(data.value)
}
const payload = computed(() => {
  let obj = { value: formItem.name }
  if (formItem.name) {
    obj.name = 'name'
    obj.value = formItem.name
  }
  if (formItem.price) {
    obj.name = 'price'
    obj.value = formItem.price
  }
  return obj
})
const loadPage = (type) => {
  dbObj
    .query({ type })
    .then((result) => {
      console.log(result)
      queryResult.value = result.sort((a,b)=> a.id - b.id)
    })
}
const queryData = () => {
  dbObj
    .query({ ...payload.value })
    .then((result) => {
      console.log(result)
      queryResult.value = result.sort()
    })
}
</script>
<template>
  <div class="btn-box">
    <button @click="getData">获取数据</button>
    <button @click="insertData">插入数据</button>
  </div>
  <div class="box">
    <label for="name">
      名称:
      <input id="name" v-model="formItem.name" type="text" />
    </label>
    <label for="price">
      价格:
      <input id="price" v-model="formItem.price" type="text" />
    </label>
    <button class="btn" @click="queryData">查询</button>
  </div>
  <ul class="list" v-show="queryResult.length">
    <li v-for="item in queryResult" :key="item.id">{{item.name}} —— {{ item.price}}</li>
    <div>
      <button @click="loadPage('prev')">上一页</button>
      <button @click="loadPage('next')">下一页</button>
    </div>
  </ul>
</template>
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
  transition: all 0.5s ease;
}
.box label {
  margin: 0 10px;
}
button {
  margin: 10px;
}
.list{
  width: 400px;
  margin: 10px auto;
}
</style>


目录
相关文章
|
自然语言处理 JavaScript 前端开发
使用Pagefind为VitePress文档添加离线全文搜索能力
前言 VitePress 相信大家都或多或少听说过或者用过了 默认 UI相比 VuePress2.x 好看,启动速度也快(由Vite驱动,当然VuePress也可以切换构建引擎至Vite) 做内容定制也相对简单,笔者的很多静态文档站点(使用VuePress1.x),文章内容多的时候启动非常的慢,于是就从之前的 VuePress 迁移到了 VitePress,并做了一个博客主题 @sugarat/theme => 之前也有过介绍一个简约风的VitePress博客主题 但是 VitePress 官方目前还没有内置开箱即用的搜索能力(相关PR还在施工中)
1060 0
解决element-ui上传多张图片时闪动问题
解决element-ui上传多张图片时闪动问题
757 0
|
存储 JavaScript 前端开发
vue3 专用 indexedDB 封装库,基于Promise告别回调地狱(二)
https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API 这个大概是官网吧,原始是英文的,现在陆续是出中文版。有空的话还是多看看官网。
|
6月前
|
存储 弹性计算 Linux
阿里云服务器从零到精通的购买指南,云服务器购买流程及注意事项参考
对于许多初次接触阿里云服务器的用户而言,如何选择云服务器配置以及在选购过程中有哪些注意事项,是新手用户比较关心的问题。本文为大家展示阿里云服务器选购的完整指南,涵盖了通过云服务器ECS产品页下单的详细步骤,以及通过阿里云的活动选购价格比较实惠的云服务器。重点是介绍每一步的注意事项,以供初次选购阿里云服务器的个人开发者和企业用户参考,尽量一次选购好,避免出现买错从新买的情况出现。
IndexedDB-增增删改查示例
IndexedDB-增增删改查示例
313 5
|
开发框架 移动开发 JavaScript
SpringCloud微服务实战——搭建企业级开发框架(四十六):【移动开发】整合uni-app搭建移动端快速开发框架-环境搭建
正如优秀的软件设计一样,uni-app把一些移动端常用的功能做成了独立的服务或者插件,我们在使用的时候只需要选择使用即可。但是在使用这些服务或者插件时一定要区分其提供的各种服务和插件的使用场景,例如其提供的【uni-starter快速开发项目模版】几乎集成了移动端所需的所有基础功能,使用非常方便,但是其许可协议只允许对接其uniCloud的JS开发服务端,不允许对接自己的php、java等其他后台系统。
706 61
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的社区垃圾回收管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的社区垃圾回收管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
216 1
|
Python
stack=s+stack#TypeError: can only concatenate str (not “list“) to str
stack=s+stack#TypeError: can only concatenate str (not “list“) to str
405 0
|
数据管理 数据库
了解使用IndexedDB的事务管理和数据版本管理
IndexedDB的事务管理确保数据一致性和完整性,通过原子操作单元保证多操作要么全部成功,要么回滚。使用transaction()方法创建事务,指定读写模式和存储空间,异步操作读取、写入或删除数据。提交或中止事务决定更改是否应用于数据库。 数据版本管理处理数据库结构更新、迁移和兼容性,通过版本号管理数据库变化。打开数据库时指定版本,低版本触发升级,执行数据结构更改和兼容性处理。有效版本管理使应用程序在结构变更时平滑迁移,保持与旧数据兼容性。 事务和版本管理是IndexedDB的关键,有助于高效、安全地处理数据并支持数据库的灵活扩展。
|
Web App开发 数据采集 JavaScript
面试官:请用纯 JS 实现,将 HTML 网页转换为图像
在工作时,需要实现一个功能:把一个HTML网页的转换为图像。我想到的第一个想法是使用第三方库,但像dom-to-image或使用Chrome Headless,如Puppeteer。那如何使用纯Javascript解决这种需求呢?
559 0

热门文章

最新文章