IndexedDB:upgradeneeded 和 blocked 事件的触发场景

4,429 阅读2分钟

打开数据库

语法:

indexedDB.open(name, version)

例子:

var req = indexedDB.open('store', 1)

注意:

参数 version 必须是大于 0 的自然数,否则报错。

使用 0

indexedDB.open('store', 0)
// Uncaught TypeError: Failed to execute 'open' on 'IDBFactory': The version provided must not be 0.

使用负值:

indexedDB.open('store', -1)
// Uncaught TypeError: Failed to execute 'open' on 'IDBFactory': Value is outside the 'unsigned long long' value range.

req 是打开数据库后,返回的请求实例。如果成功打开数据库,通过 req.result 可获得数据库连接实例。

// 监听 `req` 的 success 事件
req.onsuccess = function() {
  // 成功打开数据库,就可获得数据库连接实例
  var db = storeReq.result
  console.log('success: continue to work with database using `db` object')
}

除了 success,还有 upgradeneedederror 事件:

req.onupgradeneeded = function() {
  var db = storeReq.result
  console.log(`upgradeneeded: client had version ${db.version}`)
}

req.onerror = function() {
  console.log('error:', storeReq.error)
}

indexedDB.open 有以下几种情况:

一、打开之前不存在的数据库

upgradeneeded: client had version 1
success: continue to work with database using `db` object

二、升级数据库版本(如:indexedDB.open('store', 2),之前存储的版本号为 1

upgradeneeded: client had version 2
success: continue to work with database using `db` object

三、打开数据库使用的版本比现有的低 比如:之前使用 indexedDB.open('store', 2) 打开过,现在使用 indexedDB.open('store', 1) 打开。

error: DOMException: The requested version (1) is less than the existing version (2).

四、打开已有数据库

success: continue to work with database using `db` object

删除数据库

语法:

indexedDB.deleteDatabase(name)

直接指定数据库名即可,无需带上版本号。 例子:

var delReq = indexedDB.deleteDatabase('store')

delReq.onerror = function(event) {
  console.log('Error deleting database.')
}

delReq.onblocked = function(event) {
  console.log('Blocked deleting database.')
}
 
delReq.onsuccess = function(event) {
  console.log('Database deleted successfully')
  console.log(event.result) // should be undefined
}

这里需要注意的问题是:如果删除的数据库(如这里的 “store”)已被打开,那么执行 indexedDB.deleteDatabase('store') 后,会触发 delReq 的 blocked 事件,而非是 success 事件。

// 打开数据库 store
var req = indexedDB.open('store', 1)

// ...

// 删除数据库
var delReq = indexedDB.deleteDatabase('store')

delReq.onblocked = function(event) {
  console.log('Blocked deleting database.')
}

// 输出:
// Blocked deleting database.

删除数据库遭到阻塞,是因为数据库连接实例 db 没有关闭。我们给 db 添加 onversionchange 事件,就能看出来:

// 打开数据库 store
var req = indexedDB.open('store', 1)

// 监听 `req` 的 success 事件
req.onsuccess = function() {
  // 成功打开数据库,就可获得数据库连接实例
  var db = storeReq.result
  
  db.onversionchange = function() {
    console.log('Database is outdated, please reload the page.')
  }
  
  console.log('success: continue to work with database using `db` object')
}

// 删除数据库
var delReq = indexedDB.deleteDatabase('store')

delReq.onblocked = function(event) {
  console.log('Blocked deleting database.')
}

// 输出:
// Database is outdated, please reload the page.
// Blocked deleting database.

再看输出,发现 db.onversionchange 触发在前,delReq.onblocked 触发在后。原因就在于:删除数据库,没有把已有的数据库连接关闭。我们在 db.onversionchange 中,使用 db.close() 关闭连接。再来看看结果:

// 打开数据库 store
var req = indexedDB.open('store', 1)

// 监听 `req` 的 success 事件
req.onsuccess = function() {
  // 成功打开数据库,就可获得数据库连接实例
  var db = storeReq.result
  
  db.onversionchange = function() {
+   db.close()
    console.log('Database is outdated, please reload the page.')
  }
  
  console.log('success: continue to work with database using `db` object')
}

// 删除数据库
var delReq = indexedDB.deleteDatabase('store')

delReq.onblocked = function(event) {
  console.log('Blocked deleting database.')
}

+ delReq.onsuccess = function(event) {
+  console.log('Database deleted successfully')
+ }

// 输出:
// Database is outdated, please reload the page.
- // Blocked deleting database.
+ // Database deleted successfully

数据库成功删除了。

注意:

即使前面删除时没有关闭数据库链接,导致触发 delReqblocked 事件,但实际上数据库 store 也是被成功删除了的(刷新页面就能看到数据库已被删除)。

升级数据库

升级数据库版本的时候,也要注意已经打开的数据库连接。如果现有的数据库连接没有关闭,调用 var newReq = indexedDB.open('store', 2) 就会触发 newReq 的 blocked 事件。

let req = indexedDB.open('store', 1)

req.onupgradeneeded = function() {
  var db = req.result
  console.log(`[req] upgradeneeded: client had version ${db.version}`)
}

req.onsuccess = function() {
  var db = req.result

  db.onversionchange = function() {
    console.log('[req] Database is outdated, please reload the page.')
  }

  console.log('[req] success: continue to work with database using `db` object')
}

// 3 秒后,使用新版本号打开数据库
setTimeout(() => {
  var newReq = indexedDB.open('store', 2)

  newReq.onblocked = function() {
    console.log('[newReq] onblocked:')
    console.log(newReq.error)
  }

  newReq.onsuccess = function() {
    console.log(
      '[newReq] success: continue to work with database using `db` object'
    )
  }
}, 3000)

// 输出:
// [req] upgradeneeded: client had version 1
// [req] success: continue to work with database using `db` object
// [req] Database is outdated, please reload the page.
// [newReq] onblocked:
// Uncaught DOMException: Failed to read the 'error' property from 'IDBRequest': The request has not finished.

在现有的数据库连接没有关闭的情况下,使用新版本号打开数据库同样会触发已有数据库连接实例的 versionchange 事件,如果不在此事件中关闭连接,则打开数据库失败。 我们将关闭连接的逻辑加上试试:

let req = indexedDB.open('store', 1)

req.onupgradeneeded = function() {
  var db = req.result
  console.log(`[req] upgradeneeded: client had version ${db.version}`)
}

req.onsuccess = function() {
  var db = req.result

  db.onversionchange = function() {
+    db.close()
     console.log('[req] Database is outdated, please reload the page.')
  }

  console.log('[req] success: continue to work with database using `db` object')
}

// 3 秒后,使用新版本号打开数据库
setTimeout(() => {
  var newReq = indexedDB.open('store', 2)

  newReq.onblocked = function() {
    console.log('[newReq] onblocked:')
    console.log(newReq.error)
  }

  newReq.onsuccess = function() {
    console.log(
      '[newReq] success: continue to work with database using `db` object'
    )
  }
}, 3000)

// 输出:
// [req] upgradeneeded: client had version 1
// [req] success: continue to work with database using `db` object
// [req] Database is outdated, please reload the page.
- // [newReq] onblocked:
- // Uncaught DOMException: Failed to read the 'error' property from 'IDBRequest': The request has not finished.
+ [newReq] success: continue to work with database using `db` object

打开新版数据库实例成功~

(正文完)


广告时间(长期有效)

我有一位好朋友开了一间猫舍,在此帮她宣传一下。现在猫舍里养的都是布偶猫。如果你也是个爱猫人士并且有需要的话,不妨扫一扫她的【闲鱼】二维码。不买也不要紧,看看也行。

(完)