indexDB

501 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第28天,点击查看活动详情

indexdb VS localstorage

LocalStorage 是用 key-value 键值模式存储数据,它存储的数据都是字符串形式。,就是专门为小数量数据设计的,所以它的 api 设计为同步的。

对于简单的数据,你应该继续使用 localstorage,但当你希望存储大量数据时,IndexedDB 会明显的更适合,IndexedDB 能提供你更为复杂的查询数据的方式。

indexDB特点

  • 键值对储存: IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。

  • 异步:  IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。

  • 支持事务:  IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。

  • 同源限制: IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。

  • 储存空间大: IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限

  • 支持二进制储存: IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。

简单DEMO

<form id="form">
    <p>项目名称:<input required name="name" autocomplete="off" required></p>
    <p>开始时间:<input type="date" value="2017-07-16" name="begin" required></p>
    <p>预计结束:<input type="date" value="2057-07-16" name="end" required></p>
    <p>参与人员:<input name="person" placeholder="多人空格分隔" required autocomplete="off"></p>
    <p>补充说明:<textarea rows="5" placeholder="非必填" name="remark"></textarea></p>
    <p><input type="submit" value="确定创建"></p>
</form>

<div id="result" class="result">
    <table>
        <thead>
            <tr>
                <th>项目名称</th>
                <th>开始时间</th>
                <th>结束时间</th>
                <th>参与人员</th>
                <th>补充说明</th>
                <th width="30">操作</th>
            </tr>
        </thead>
        <tbody></tbody>
    </table>
    <div id="status" class="status">加载中...</div>
</div>

<!-- 列表数据模板 -->
<script id="tplList" type="text/template">
<tr>
    <td data-key="name" data-id="$id$" contenteditable="plaintext-only">$name$</td>
    <td data-key="begin" data-id="$id$" contenteditable="plaintext-only">$begin$</td>
    <td data-key="end" data-id="$id$" contenteditable="plaintext-only">$end$</td>
    <td data-key="person" data-id="$id$" contenteditable="plaintext-only">$person$</td>
    <td data-key="remark" data-id="$id$" contenteditable="plaintext-only">$remark$</td>
    <td><a href="javascript:" role="button" class="jsListDel" data-id="$id$">删除</a></td>
</tr></script>
JS代码:
  1. 打开数据库:window.indexedDB.open(dbName, version)
(function () {
    // 元素们
    var eleForm = document.querySelector('#form');
    var eleTbody = document.querySelector('#result tbody');
    var eleStatus = document.getElementById('status');
    // 模板字符内容
    var strTplList = document.getElementById('tplList').innerHTML;
    
    var logError = function (error) {
        eleStatus.style.display = 'block';
        eleStatus.innerHTML = '<span class="error">'+ error +'</span>';
    }, logInfo = function (info) {
        eleStatus.style.display = 'block';
        eleStatus.innerHTML = '<span class="info">'+ info + '</span>';
    };
    
    // 简易模板方法
    String.prototype.temp = function(obj) {
        return this.replace(/$\w+$/gi, function(matchs) {        
            return obj[matchs.replace(/$/g, "")] || '';
        });
    };
    
    // 本演示使用的数据库名称
    var dbName = 'project';
    // 版本
    var version = 1;
    // 数据库数据结果
    var db;

    // 打开数据库
    var DBOpenRequest = window.indexedDB.open(dbName, version);
    
    // 如果数据库打开失败
    DBOpenRequest.onerror = function(event) {
        logError('数据库打开失败');
    };
    
    DBOpenRequest.onsuccess = function(event) {        
        // 存储数据结果
        db = DBOpenRequest.result;
        
        // 显示数据
        method.show();
    };
    
    // 下面事情执行于:数据库首次创建版本,或者window.indexedDB.open传递的新版本(版本数值要比现在的高)
    DBOpenRequest.onupgradeneeded = function(event) {
        var db = event.target.result;
     
        db.onerror = function(event) {
            logError('数据库打开失败');
        };
    
        // 创建一个数据库存储对象
        var objectStore = db.createObjectStore(dbName, { 
            keyPath: 'id',
            autoIncrement: true
        });
    
        // 定义存储对象的数据项
        objectStore.createIndex('id', 'id', {
            unique: true    
        });
        objectStore.createIndex('name', 'name');
        objectStore.createIndex('begin', 'begin');
        objectStore.createIndex('end', 'end');
        objectStore.createIndex('person', 'person');
        objectStore.createIndex('remark', 'remark');
    };
    
    var method = {
        add: function (newItem) {
            var transaction = db.transaction([dbName], "readwrite");
            // 打开已经存储的数据对象
            var objectStore = transaction.objectStore(dbName);
            // 添加到数据对象中
            var objectStoreRequest = objectStore.add(newItem);        
            objectStoreRequest.onsuccess = function(event) {
                method.show();
            };
        },
        edit: function (id, data) {
            // 编辑数据
            var transaction = db.transaction([dbName], "readwrite");
            // 打开已经存储的数据对象
            var objectStore = transaction.objectStore(dbName);
            // 获取存储的对应键的存储对象
            var objectStoreRequest = objectStore.get(id);
            // 获取成功后替换当前数据
            objectStoreRequest.onsuccess = function(event) {
                // 当前数据
                var myRecord = objectStoreRequest.result;
                // 遍历替换
                for (var key in data) {
                    if (typeof myRecord[key] != 'undefined') {
                        myRecord[key] = data[key];
                    }
                }
                // 更新数据库存储数据                
                objectStore.put(myRecord);
            };
        },
        del: function (id) {
            // 打开已经存储的数据对象
            var objectStore = db.transaction([dbName], "readwrite").objectStore(dbName);            
            // 直接删除            
            var objectStoreRequest = objectStore.delete(id);
            // 删除成功后
            objectStoreRequest.onsuccess = function() {
                method.show();
            };
        },
        show: function () {
            // 最终要展示的HTML数据
            var htmlProjectList = '';
            // 打开对象存储,获得游标列表
            var objectStore = db.transaction(dbName).objectStore(dbName);
            objectStore.openCursor().onsuccess = function(event) {
                var cursor = event.target.result;
                // 如果游标没有遍历完,继续下面的逻辑
                if (cursor) {
                    htmlProjectList = htmlProjectList + strTplList.temp(cursor.value);            
                    // 继续下一个游标项
                    cursor.continue();
                // 如果全部遍历完毕
                } else {
                    logInfo('');
                    eleTbody.innerHTML = htmlProjectList;
                    
                    if (htmlProjectList == '') {
                        logInfo('暂无数据');    
                    }
                }
            }
        }
    };
    
    // 表单提交新增数据
    eleForm.addEventListener('submit', function (event) {
        event.preventDefault();    
        
        var formData = {};
        
        [].slice.call(this.querySelectorAll('input,textarea')).forEach(function (ele) {
            if (ele.name) {
                formData[ele.name] = ele.value;    
            }
        });
            
        // 添加新的数据
        method.add(formData);
        
        this.reset();
    });
    
    // 编辑事件
    eleTbody.addEventListener('focusout', function (event) {
        var eleTd = event.target;
        // 获取id,也就是获得主键
        var id = eleTd && eleTd.getAttribute('data-id');
        if (!id || !/td/.test(eleTd.tagName)) { return; }
        
        // 这是要替换的数据
        var data = {
            id: id * 1    
        };
        // 获得现在的数据
        [].slice.call(eleTd.parentElement.querySelectorAll('td[data-key]')).forEach(function (td) {
            var key = td.getAttribute('data-key');
            var value = td.innerText || td.textContent || '';
            
            data[key] = value;
        });
        
        // 更新本地数据库
        method.edit(id, data);
    });
    // 删除事件
    eleTbody.addEventListener('click', function (event) {
        var eleBtn = event.target, id = '';
        if (eleBtn && eleBtn.classList.contains('jsListDel') && (id = eleBtn.getAttribute('data-id'))) {
            method.del(id * 1);    
            event.preventDefault();        
        }
    });
})();