+6

Lưu dữ liệu khi offline với IndexDB

Chào các bạn, ở hai bài trước thì mình có giới thiệu về service worker, workbox và cách giúp bạn cache những tài nguyên trong web để có thể chạy được trang web kể cả khi không có mạng. Tại bại lần này thì mình sẽ giới thiệu cho bạn cách lưu dữ liệu khi đang ở trạng thái offline với indexDB

IndexDB là gì

IndexDB là một kho lưu trữ dưới trình duyệt người dùng (giống như localStorage vậy). Các dữ liệu trong indexDB sẽ được lưu dưới dạng key - value. Khái niệm khá là đơn giản phải không nào, giờ hãy bắt tay vào việc chính thôi nhỉ

Cách sử dụng

Các bạn hãy clone project này về nhé: https://github.com/hoangdm-2060/index-db-demo. Các bạn mở file main.js sẽ thấy mình kiểm tra xem trình duyệt của bạn có hỗ trợ indexDb không, nhưng chắc hẳn là sẽ hỗ trợ thôi, vì indexDB giờ đã chạy được gần như có thể chạy được trên mọi trình duyệt

if (!('indexedDB' in window)) {
    console.log('This browser doesn\'t support IndexedDB');
}

Để có thể chạy được demo kia thì bạn cần cài http-server

npm install http-server -g

Sau đó là cd vào thư mục project và chạy

http-server -p 8080 -a localhost -c 0

Tạo kết nối

Đầu tiện để tạo ra 1 kết nối thì bạn có thể làm như sau

const dbPromise = idb.open('products', 1);

Trong đó products là tên kết nối, 1 là version. Bạn hãy f5 lại, vào phần Application trong inspect sẽ thấy 1 mục là IndexedDB

Vậy là chúng ta đã mở được 1 kết nối thành công

Tạo một object để lưu trữ (object stores)

Giờ trong kết nối đã tạo, chúng ra sẽ tạo thêm 1 object để lưu trữ dữ liệu liên quan tới đồ nội thất nhé. Update đoạn code trên một chút nhỉ

const dbPromise = idb.open('products', 2, function(upgradeDb) {
    switch (upgradeDb.oldVersion) {
        case 0:
            //chạy nếu db không tồn tại
        case 1:
            console.log('Creating the products object store');
            upgradeDb.createObjectStore('furniture', {keyPath: 'id'});
    }
});

Bạn sẽ thấy version giờ đã lên là 2, tại sao vậy? Vì để đảm bảo tính toàn vẹn của dữ liệu object stores và indexes sẽ chỉ được tạo trong quá trình database upgrade. Điều này có nghĩa chúng sẽ được tạo ra trong upgrade callback function trong idb.open. Callback này sẽ chỉ chạy khi version (lúc này là 2) lớn hơn version hiện tại trên trình duyệt hoặc db đó không tồn tại. Và bạn cũng sẽ thấy trong từng case đều không có break, điều này để đảm bảo tất cả các case sau case đầu tiên được chạy đều sẽ được thực thi.

Thêm dữ liệu

Giờ mình sẽ thêm 1 ít dữ liệu vào nhé

function addProducts() {
        alert(1);
        dbPromise.then(function(db) {
            const tx = db.transaction('furniture', 'readwrite');
            const store = tx.objectStore('furniture');
            const items = [
                {
                    name: 'Couch',
                    id: 'cch-blk-ma',
                    price: 499.99,
                    color: 'black',
                    material: 'mahogany',
                    description: 'A very comfy couch',
                    quantity: 3
                },
                {
                    name: 'Armchair',
                    id: 'ac-gr-pin',
                    price: 299.99,
                    color: 'grey',
                    material: 'pine',
                    description: 'A plush recliner armchair',
                    quantity: 7
                },
                {
                    name: 'Stool',
                    id: 'st-re-pin',
                    price: 59.99,
                    color: 'red',
                    material: 'pine',
                    description: 'A light, high-stool',
                    quantity: 3
                },
                {
                    name: 'Chair',
                    id: 'ch-blu-pin',
                    price: 49.99,
                    color: 'blue',
                    material: 'pine',
                    description: 'A plain chair for the kitchen table',
                    quantity: 1
                },
                {
                    name: 'Dresser',
                    id: 'dr-wht-ply',
                    price: 399.99,
                    color: 'white',
                    material: 'plywood',
                    description: 'A plain dresser with five drawers',
                    quantity: 4
                },
                {
                    name: 'Cabinet',
                    id: 'ca-brn-ma',
                    price: 799.99,
                    color: 'brown',
                    material: 'mahogany',
                    description: 'An intricately-designed, antique cabinet',
                    quantity: 11
                }
            ];
            return Promise.all(items.map(function(item) {
                    console.log('Adding item: ', item);
                    return store.add(item);
                })
            ).catch(function(e) {
                tx.abort();
                console.log(e);
            }).then(function() {
                console.log('All items added successfully!');
            });
        });
    }

Và giờ hãy ấn button add Products và kiểm tra lại nhé 😄

Tìm kiếm

Giờ hãy thử làm chức năng tìm kiếm nhé. Trước tiên chúng ta phải tạo thêm vài index để tìm kiếm theo nó nhé.

   const dbPromise = idb.open('products', 3, function(upgradeDb) {
        switch (upgradeDb.oldVersion) {
            case 0:
            //chạy nếu db không tồn tại
            case 1:
                console.log('Creating the products object store');
                upgradeDb.createObjectStore('furniture', {keyPath: 'id'});
            case 2:
                console.log('Creating a name index');
                var store = upgradeDb.transaction.objectStore('products');
                store.createIndex('name', 'name', {unique: true});
            case 3:
                console.log('Creating a price index');
                const storeV1 = upgradeDb.transaction.objectStore('furniture');
                storeV1.createIndex('price', 'price', {unique: true});
            case 4:
                console.log('Creating a description index');
                const storeV2 = upgradeDb.transaction.objectStore('furniture');
                storeV2.createIndex('description', 'description', {unique: true});
        }
    });

Giờ hãy thêm những function này

    function getByName(key) {
        return dbPromise.then(function (db) {
            const tx = db.transaction('furniture', 'readonly');
            const store = tx.objectStore('furniture');
            const index = store.index('name');

            return index.get(key);
        });

    }

    function getByPrice() {

        var lower = document.getElementById('priceLower').value;
        var upper = document.getElementById('priceUpper').value;
        var lowerNum = Number(document.getElementById('priceLower').value);
        var upperNum = Number(document.getElementById('priceUpper').value);

        if (lower === '' && upper === '') {
            return;
        }
        var range;
        if (lower !== '' && upper !== '') {
            range = IDBKeyRange.bound(lowerNum, upperNum);
        } else if (lower === '') {
            range = IDBKeyRange.upperBound(upperNum);
        } else {
            range = IDBKeyRange.lowerBound(lowerNum);
        }
        var s = '';
        dbPromise.then(function (db) {
            var tx = db.transaction('furniture', 'readonly');
            var store = tx.objectStore('furniture');
            var index = store.index('price');
            return index.openCursor(range);
        }).then(function showRange(cursor) {
            if (!cursor) {
                return;
            }
            console.log('Cursored at:', cursor.value.name);
            s += '<h2>Price - ' + cursor.value.price + '</h2><p>';
            for (var field in cursor.value) {
                s += field + '=' + cursor.value[field] + '<br/>';
            }
            s += '</p>';
            return cursor.continue().then(showRange);
        }).then(function () {
            if (s === '') {
                s = '<p>No results.</p>';
            }
            document.getElementById('results').innerHTML = s;
        });

    }

    function getByDesc() {
        var key = document.getElementById('desc').value;
        if (key === '') {
            return;
        }
        var range = IDBKeyRange.only(key);
        var s = '';
        dbPromise.then(function (db) {
        }).then(function () {
            if (s === '') {
                s = '<p>No results.</p>';
            }
            document.getElementById('results').innerHTML = s;
        });
    }

    function displayByName() {
        var key = document.getElementById('name').value;
        if (key === '') {
            return;
        }
        var s = '';
        getByName(key).then(function (object) {
            console.log(object);
            if (!object) {
                return;
            }

            s += '<h2>' + object.name + '</h2><p>';
            for (var field in object) {
                s += field + ' = ' + object[field] + '<br/>';
            }
            s += '</p>';

        }).then(function () {
            if (s === '') {
                s = '<p>No results.</p>';
            }
            document.getElementById('results').innerHTML = s;
        });
    }

Viết theo cách này thì chỉ là demo nên hơi dở 1 chút, vì nó sẽ chỉ search được đúng theo tên đầy đủ đươc lưu 😦 cái này nếu có thời gian chắc mình sẽ update sau

Với 3 bài viết về offline này mong sẽ giúp ích được các bận nhiều. Cảm ơn các bạn đã quan tâm tới bài viết của mình


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.