PWA 中基于 IndexedDB 实现本地数据存储的技巧

前言

随着 PWA 技术的发展,越来越多的网站开始采用 PWA 技术来提升用户体验。然而,PWA 在离线使用时需要存储大量的数据,例如历史记录、用户信息等,而 IndexedDB 可以方便地存储这些数据。本文将介绍如何使用 IndexedDB 实现本地数据存储。

什么是 IndexedDB?

IndexedDB 是浏览器提供的低级别的非关系型数据库,可以用来存储大量数据。IndexedDB 是一个对象存储库,可以存储任意 JavaScript 对象。IndexedDB 可以在离线状态下存储数据,并且支持事务。

IndexedDB 的使用

IndexedDB 是异步操作,因此需要使用 Promise 或回调函数来处理 IndexedDB 操作的结果。

打开数据库

使用 indexedDB.open() 方法可以打开一个 IndexedDB 数据库:

var dbPromise = indexedDB.open('myDatabase', 1);

以上代码会在本地创建一个名为 myDatabase 的数据库,版本号为 1。如果数据库不存在,则会创建一个新的数据库。

创建存储对象

在打开数据库之后,需要创建一个存储对象。存储对象类似于关系型数据库中的表格,用来存储数据:

dbPromise.onupgradeneeded = function(event) {
  var db = event.target.result;
  var store = db.createObjectStore('myStore', { keyPath: 'id' });
  store.createIndex('name', 'name', { unique: false });
};

以上代码会在 myDatabase 数据库中创建一个名为 myStore 的存储对象,该存储对象可以通过 id 属性来唯一标识每一条数据,同时还会创建一个名为 name 的索引。

添加数据

添加数据需要使用存储对象的 put() 方法:

dbPromise.then(function(db) {
  var tx = db.transaction('myStore', 'readwrite');
  var store = tx.objectStore('myStore');
  var item = { id: 1, name: 'John', age: 30 };
  store.put(item);
  return tx.complete;
}).then(function() {
  console.log('Data added to store');
});

以上代码会向 myStore 存储对象中添加一条数据。由于使用了事务,因此需要等待事务的完成(Promise 中的 tx.complete)才能确保数据已经存储成功。

获取数据

获取数据需要使用存储对象的 get() 方法:

dbPromise.then(function(db) {
  var tx = db.transaction('myStore', 'readonly');
  var store = tx.objectStore('myStore');
  return store.get(1);
}).then(function(item) {
  console.log('Got item:', item);
});

以上代码会从 myStore 存储对象中获取 id 为 1 的数据,并将获取的数据打印到控制台中。

删除数据

删除数据需要使用存储对象的 delete() 方法:

dbPromise.then(function(db) {
  var tx = db.transaction('myStore', 'readwrite');
  var store = tx.objectStore('myStore');
  store.delete(1);
  return tx.complete;
}).then(function() {
  console.log('Item deleted from store');
});

以上代码会从 myStore 存储对象中删除 id 为 1 的数据。

基于 IndexedDB 的 PWA 应用示例

以下是一个基于 IndexedDB 的 PWA 应用示例。该应用可以存储用户输入的笔记,并在离线状态下提供访问。

HTML 文件

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IndexedDB Example</title>
  </head>
  <body>
    <h1>IndexedDB Example</h1>
    <input type="text" id="noteInput" placeholder="Enter note">
    <button id="addButton">Add note</button>
    <ul id="noteList"></ul>
    
    <script src="app.js"></script>
  </body>
</html>

JavaScript 文件

var dbPromise = indexedDB.open('noteDatabase', 1);

dbPromise.onupgradeneeded = function(event) {
  var db = event.target.result;
  var store = db.createObjectStore('noteStore', { keyPath: 'id', autoIncrement: true });
  store.createIndex('note', 'note', { unique: false });
};

function addNote() {
  var noteInput = document.getElementById('noteInput');
  var note = noteInput.value.trim();
  
  if (note !== '') {
    dbPromise.then(function(db) {
      var tx = db.transaction('noteStore', 'readwrite');
      var store = tx.objectStore('noteStore');
      var item = { note: note };
      store.put(item);
      return tx.complete;
    }).then(function() {
      console.log('Note added to store:', note);
      noteInput.value = '';
      displayNotes();
    });
  }
}

function displayNotes() {
  var noteList = document.getElementById('noteList');
  noteList.innerHTML = '';
  
  dbPromise.then(function(db) {
    var tx = db.transaction('noteStore', 'readonly');
    var store = tx.objectStore('noteStore');
    return store.getAll();
  }).then(function(notes) {
    notes.forEach(function(note) {
      var li = document.createElement('li');
      li.textContent = note.note;
      noteList.appendChild(li);
    });
  });
}

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('serviceWorker.js').then(function(registration) {
      console.log('ServiceWorker registration successful with scope:', registration.scope);
    }, function(error) {
      console.log('ServiceWorker registration failed:', error);
    });
  });
}

document.getElementById('addButton').addEventListener('click', addNote);
displayNotes();

以上代码实现了一个基于 IndexedDB 的笔记应用。应用将用户输入的笔记存储在 IndexedDB 中,离线状态下可以访问存储的笔记。要启用离线访问,需要添加一个名为 serviceWorker.js 的 Service Worker 脚本文件。

Service Worker 文件

var CACHE_NAME = 'note-v1';
var urlsToCache = [
  '/',
  'app.js',
  'manifest.json',
  'index.html',
];

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Opened cache:', CACHE_NAME);
        return cache.addAll(urlsToCache);
      })
      .then(function() {
        return self.skipWaiting();
      })
  );
});

self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.filter(function(cacheName) {
          return cacheName != CACHE_NAME;
        }).map(function(cacheName) {
          return caches.delete(cacheName);
        })
      );
    }).then(function() {
      return self.clients.claim();
    })
  );
});

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        if (response) {
          console.log('Serving from cache:', event.request.url);
          return response;
        }
        
        console.log('Fetching from network:', event.request.url);
        return fetch(event.request);
      })
  );
});

以上代码实现了一个简单的 Service Worker,用于离线访问。Service Worker 会缓存应用的核心文件,并在离线状态下提供这些文件。

总结

对于需要本地存储数据的 PWA 应用,IndexedDB 是一个非常好的选择。通过 IndexedDB,PWA 应用可以在离线状态下存储大量数据,并且可以方便地在在线状态下将数据上传到服务器。本文介绍了如何使用 IndexedDB 创建存储对象、添加数据、获取数据和删除数据,并提供了一个基于 IndexedDB 的 PWA 应用示例。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a50d01add4f0e0ffd7583e


纠错反馈