浏览器 IndexedDB 简明教程 ( 五 ) - 数据库版本升级机制

yufei       6 年, 9 月 前       3948

浏览器数据库 IndexedDB 简明教程 ( 四 ) - 创建与打开数据库 章节中我们学习了如何创建和打开 IndexedDB 数据库,学习了 IDBOpenDBRequest 对象的三个时间属性,也分别对它们做了一些介绍,我们主要当 IndexedDB 把数据库成功创建了一个新的版本时会触发 onupgradeneeded 事件

数据库版本升级机制

也许是上一个章节我们没有表述清楚,我们并没有明确表达 onupgradeneeded 事件的触发时机,在这里,我们细细阐述一下

「 当使用 open 方法打开一个 IndexedDB 数据库时,如果发现传递了一个新的版本,则 IndexedDB,会自动将数据库版本编号改成新的,如果成功,则再回调 onupgradeneeded 事件 」

也就是说,对于下面的 JavaScript 代码

req.onupgradeneeded = function (event) {
  console.log(event.target.result);
  console.log('升级成功');
}

event.target.result 所代表的数据库,里面的数据还是原来的,只不过只更改了数据库的版本编号而已,真正的数据库升级,是通过调用 onupgradeneeded 来完成,如果 onupgradeneeded 没有创建任何存储对象或主键或索引,那么,数据库还是原来那个数据库,只不过编号不一样了而已

所以,当我们需要将数据库升级时,一般会做写如下的代码

req.onupgradeneeded = function(event) {
  db = event.target.result;
  var cityStore   = db.createObjectStore('city', { keyPath: 'city_id' });
}

先不用管 createObjectStore 是啥意思,上面这个回调的意思是,如果版本提升成功,那么继续创建一个存储对象 city,并且指定主键为 city_id

当然了,一般情况下我们不会这么写,因为我们需要根据当前的版本号来指定如何升级,所以,正常的写法如下

req.onupgradeneeded = function(event) {
  db = event.target.result;

  // 特别指定,只要版本号为 3 时才会这么多
  if ( db.version == 3 ) 
      var schoolObject = db.createObjectStore('city', { keyPath: 'school_id' })

}

但是,这个上面这个版本还是不能再正式环境中使用的,因为我们并不知道之前的版本号是什么?如果只升级到最新的版本,那么中间的版本就可能遗漏了,所以,能用在正式环境的完整代码如下

// 先定义当前最新的版本,一般从服务器端获取
const latest_version = 3;

// 定义最新版本的数据库
let db;

// 定义一个变量,用于提示是否需要升级数据库版本号
var need_update_version = -1;

//尝试打开当前版本的数据库,如果没创建则创建一个
var req = window.indexedDB.open('demo');

req.onerror = function (event) {
  console.log('打开数据库失败');
};

req.onupgradeneeded = function(event){
    let old_version = event.target.result.version;
    // 检查数据库版本是否最新,如果最新则直接返回
    if ( old_version == latest_version )
        db = event.target.result;

    // 如果数据库版本小于 2 则执行一些更新
    if ( old_version < 2 ) {
        need_update_version = 2;
        var schoolObject = db.createObjectStore('city', { keyPath: 'school_id' });
    }

    // 如果数据库版本小于 3 则执行另一些更新
    if ( old_version < 3 ) {
        need_update_version = 3;
        var addressObject = db.createObjectStore('address', { keyPath: 'address_id' });
    }
    console.log('打开数据库成功');  
};

req.onsuccess = function (event) {
    // 如果检测到需要更新数据库版本,什么事情都不做,否则设置 db 对象
    if ( need_update_version == -1 )
        db = event.target.result;
}

// 一个坑爹的死轮询开始了, 50ms 轮询一次
const tick = setInterval(function(){
    // 如果 db 有值,则说明没发生升级,直接清楚定时器
    if ( db ) {
        clearInterval(tick);

    } else if ( need_update_version > 0 && need_update_version != latest_version )  {
        // 否则判断升级后的版本是否是最新的,如果不是最新的,说明升级失败,提示即可,也退出
        alert('升级 IndexedDB 失败');
        clearInterval(tick);
    } else if ( need_update_version == latest_version )   {
        // 升级成功,用最新的版本打开数据库
        //尝试打开最新版本的数据库,如果没创建则创建一个
        var req = window.indexedDB.open('demo',latest_version);

        req.onerror = function (event) {
          console.log('打开数据库失败');
        };

        // req.onupgradeneeded 事件已经没啥用了

        // 打开成功
        req.onsuccess = function (event) {
            db = event.target.result;
        }


    } else {
        // 说明升级还没完成,继续等待
    }

},50);

一个版本升级,都这么坑爹,所以,大部分人为了方便起见,直接强行打开最新的版本,然后不管三七二十一,把最新版本所需要的存储对象啦,主键啦索引啦,一股脑儿的全部创建了

创建一个新的数据库也会触发 onupgradeneeded

让我们回到上一章节的第一次执行以下代码的结果

let db;
const req = window.indexedDB.open('demo');

req.onerror = function (event) {
  console.log('打开数据库失败');
};

req.onsuccess = function (event) {
  db = event.target.result;
  console.log('打开数据库成功');
};

req.onupgradeneeded = function (event) {
  console.log(event.target);
  console.log('升级成功');
}

运行结果如下

升级成功
打开数据库成功

你会发现一个惊人的事情,就是首次运行竟然会出现 升级成功,这是一个非常有趣的特性,因为有了这个特性,我们上面的介绍的 「 主键啦索引啦,一股脑儿的全部创建了 」 就有了新的解决方案

创建一个数据库的新版本并执行更新

因为无论是数据库为创建,或者需要升级,IndexedDB 都会触发 onupgradeneeded 事件,所以我们有了一个大胆的方案,那就是

「 无论如何都创建数据库的最新版本,然后再 onupgradeneeded 回调中根据有无需要的更新结果选择是否重新更新 」

例如下面的代码,如果没有检测到 city 这个存储对象,那么就新建一个 city 存储对象

req.onupgradeneeded = function (event) {
  db = event.target.result;
  var objectStore;
  if (!db.objectStoreNames.contains('city')) {
    objectStore = db.createObjectStore('city', { keyPath: 'city_id' });
  }
}

这样一来,就简单的多了,真的是要扶额擦汗,还好有变通的方法

结束语

在数据库版本升级的过程中,我们差点误入歧途,还好中间我们迷途知返,找了一个简单的解决方案

目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.