HTML5本地存储备选解决方案

121

我正在寻找能够在不支持本地存储的浏览器上模拟 localStorage 的 JavaScript 库和代码。

基本上,我想使用 localStorage 来存储数据,并确信它也可以在没有本地支持的浏览器上正常工作。这意味着库将检测是否存在 window.localStorage 并在存在时使用它。如果不存在,则会通过在 window.localStorage 命名空间中创建自己的实现来创建某种本地存储的回退方法。

到目前为止,我已经找到了以下解决方案:

  1. 简单的sessionStorage 实现。
  2. 使用 cookie 的实现(不太满意这个想法)。
  3. Dojo 的dojox.storage,但它是独立的实现,并不是回退。

我知道 Flash 和 Silverlight 也可以用于本地存储,但尚未找到有关将它们用作标准 HTML5 localStorage 的回退的任何信息。也许 Google Gears 也有这个功能?

请分享您发现的任何相关库、资源或代码片段!我会特别感兴趣纯 JavaScript 或基于 jQuery 的解决方案,但我猜这很不可能。


sessionStorage和localStorage都是Web Storage规范的一部分(http://dev.w3.org/html5/webstorage/)。唯一的区别在于浏览器保存数据的时间长度。我猜你不会找到只有其中一个而没有另一个的实现(但我不能100%确定)。 - rlovtang
1
值得一提的是,Gears在去年2月份已经正式被宣布弃用,我不会在其上构建任何东西。 - josh3736
@rlovtang:谢谢,我知道session和local storage之间的区别。根据24ways.org文章(问题中的第一个链接,解决方案#1),Chrome仅支持localStorage而不是sessionStorage。也许情况已经改变了,因为那篇文章是一段时间以前写的。 - Tauren
@josh3736:是的,我个人想避免使用cookies和gears。我肯定不会构建任何依赖它的东西,但如果它是某个已安装它的人的备用存储机制,并且我没有直接编码到它,那么它可以被使用。 - Tauren
这里只是记录一下,在我使用Firefox 15和16进行测试时,如果禁用了cookies,则也会禁用localStorage。即使您尝试访问window.localStorage创建polyfill,也会出现Error:The operation is insecure.的错误。使用try catch可以检查是否已禁用,但不能用自己的解决方案替换变量。 :/ - Mauvis Ledford
显示剩余2条评论
9个回答

63

纯JS编写的简单localStorage polyfill:

演示:http://jsfiddle.net/aamir/S4X35/

HTML:

<a href='#' onclick="store.set('foo','bar')">set key: foo, with value: bar</a><br/>
<a href='#' onclick="alert(store.get('foo'))">get key: foo</a><br/>
<a href='#' onclick="store.del('foo')">delete key: foo</a>

JS:

=>

JavaScript:

window.store = {
    localStoreSupport: function() {
        try {
            return 'localStorage' in window && window['localStorage'] !== null;
        } catch (e) {
            return false;
        }
    },
    set: function(name,value,days) {
        if (days) {
            var date = new Date();
            date.setTime(date.getTime()+(days*24*60*60*1000));
            var expires = "; expires="+date.toGMTString();
        }
        else {
            var expires = "";
        }
        if( this.localStoreSupport() ) {
            localStorage.setItem(name, value);
        }
        else {
            document.cookie = name+"="+value+expires+"; path=/";
        }
    },
    get: function(name) {
        if( this.localStoreSupport() ) {
            var ret = localStorage.getItem(name);
            //console.log(typeof ret);
            switch (ret) {
              case 'true': 
                  return true;
              case 'false':
                  return false;
              default:
                  return ret;
            }
        }
        else {
            // cookie fallback
            /*
             * after adding a cookie like
             * >> document.cookie = "bar=test; expires=Thu, 14 Jun 2018 13:05:38 GMT; path=/"
             * the value of document.cookie may look like
             * >> "foo=value; bar=test"
             */
            var nameEQ = name + "=";  // what we are looking for
            var ca = document.cookie.split(';');  // split into separate cookies
            for(var i=0;i < ca.length;i++) {
                var c = ca[i];  // the current cookie
                while (c.charAt(0)==' ') c = c.substring(1,c.length);  // remove leading spaces
                if (c.indexOf(nameEQ) == 0) {  // if it is the searched cookie
                    var ret = c.substring(nameEQ.length,c.length);
                    // making "true" and "false" a boolean again.
                    switch (ret) {
                      case 'true':
                          return true;
                      case 'false':
                          return false;
                      default:
                          return ret;
                    }
                }
            }
            return null; // no cookie found
        }
    },
    del: function(name) {
        if( this.localStoreSupport() ) {
            localStorage.removeItem(name);
        }
        else {
            this.set(name,"",-1);
        }
    }
}​

4
为什么这个没有得到认可!?谢谢! - Robert
4
我不喜欢为所需的每个东西都添加整个库。 - Aamir Afridi
2
在JavaScript中,是否允许在本地定义var expires,然后在其他作用域中使用?在函数set中。 - happy_marmoset
3
想指出的是,虽然您可以在cookie上设置过期时间,但localStorage永远不会过期。如果您希望某些东西过期,这可能会导致一些问题。最好在localstorage中添加过期日期,并进行日期比较以查看它是否仍然适用。当然,如果需要过期时间,我认为您应该使用cookie。但我认为,这个脚本应该要么不允许过期,要么对两者都允许,而不是像现在这样不一致。 - Hanna
1
@MohsinSaeed 在私密模式下,我认为浏览器也不会允许您创建cookie。http://superuser.com/questions/589248/can-chrome-incognito-mode-access-regular-chrome-cookies-etc - Aamir Afridi
显示剩余10条评论

56
我使用 PersistJS github 存储库),该库可以无缝、透明地处理客户端存储,并提供单一的API支持以下后端:
  • flash: Flash 8 持久化存储。
  • gears: 基于 Google Gears 的持久化存储。
  • localstorage: HTML5 草案存储。
  • whatwg_db: HTML5 草案数据库存储。
  • globalstorage: HTML5 草案存储(旧规范)。
  • ie: Internet Explorer userdata行为。
  • cookie: 基于 Cookie 的持久化存储。
如果您不想使用 cookie 等任何一种,这些存储方式都可以被禁用。使用此库,您将获得 IE 5.5+、Firefox 2.0+、Safari 3.1+ 和 Chrome 中本机客户端存储支持;如果浏览器具有Flash或Gears,则可以插件辅助支持。如果启用了 cookie,则它将在所有浏览器中工作(但会限制在 4 kB)。

10
PersistJS还得到支持吗?我想知道它是如何解决浏览器升级后所选择的存储方式发生改变的问题(比如本地存储变得可用)。旧的位置会变得无法访问吗? - jcalfee314
2
至少看起来它不再处于活跃开发状态了。 - Mahn
这是一个非常大胆的库。在大多数技术中没有关于最大尺寸的任何知识,我们应该满足于我们的应用程序能够运行得很顺利...此外,如果您想要保存小于64KB,这似乎只是一个解决方案。 - dude
@julmot,这就是库的作用。在抽象掉繁琐的东西的同时提供便利。通过足够的研究和时间,通常可以弄清楚如何使其工作,无论最大尺寸如何。当然,看起来该项目的作者决定这太多了... - William
@Liam:任何不支持Flash的东西都支持localStorage,所以我认为这并不是一个问题。请记住,这是一个旧浏览器的备用库。 - josh3736
显示剩余4条评论

19

1
似乎它们都不能在私密/无痕模式下工作(例如Safari或Chrome),因为它们的解决方法是创建cookie,而该模式下也被禁用了。 - Alex Klaus

14

以下是 Aamir Afridi 的答案的整理版本,它将所有代码封装在本地范围内。

我删除了创建全局 ret 变量的引用,也删除了在 BrowserStorage.get() 方法中将存储的 "true" 和 "false" 字符串解析为布尔值的内容,这可能会导致实际上要存储字符串 "true" 或 "false" 时出现问题。

由于本地存储 API 只支持字符串值,因此可以通过将数据编码为 JSON 字符串来存储/检索 JavaScript 变量数据及其适当的数据类型,然后使用 JSON 编码/解码库(如https://github.com/douglascrockford/JSON-js)对其进行解码。

var BrowserStorage = (function() {
    /**
     * Whether the current browser supports local storage as a way of storing data
     * @var {Boolean}
     */
    var _hasLocalStorageSupport = (function() {
        try {
            return 'localStorage' in window && window['localStorage'] !== null;
        } catch (e) {
            return false;
        }
    })();

    /**
     * @param {String} name The name of the property to read from this document's cookies
     * @return {?String} The specified cookie property's value (or null if it has not been set)
     */
    var _readCookie = function(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') c = c.substring(1, c.length);
            if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
        }

        return null;
    };

    /**
     * @param {String} name The name of the property to set by writing to a cookie
     * @param {String} value The value to use when setting the specified property
     * @param {int} [days] The number of days until the storage of this item expires
     */
    var _writeCookie = function(name, value, days) {
        var expiration = (function() {
            if (days) {
                var date = new Date();
                date.setTime(date.getTime() + (days*24*60*60*1000));
                return "; expires=" + date.toGMTString();
            }
            else {
                return "";
            }
        })();

        document.cookie = name + "=" + value + expiration + "; path=/";
    };

    return {
        /**
         * @param {String} name The name of the property to set
         * @param {String} value The value to use when setting the specified property
         * @param {int} [days] The number of days until the storage of this item expires (if storage of the provided item must fallback to using cookies)
         */
        set: function(name, value, days) {
            _hasLocalStorageSupport
                ? localStorage.setItem(name, value)
                : _writeCookie(name, value, days);
        },

        /**
         * @param {String} name The name of the value to retrieve
         * @return {?String} The value of the 
         */
        get: function(name) {
            return _hasLocalStorageSupport
                ? localStorage.getItem(name) 
                : _readCookie(name);
        },

        /**
         * @param {String} name The name of the value to delete/remove from storage
         */
        remove: function(name) {
            _hasLocalStorageSupport
                ? localStorage.removeItem(name)
                : this.set(name, "", -1);
        }
    };
})();

10

我个人更喜欢使用amplify.js。在过去的使用中,它为我提供了良好的表现,并且我建议将其用于所有本地存储需求。

支持IE 5+,Firefox 2+,Safari 4+,Chrome,Opera 10.5+,iPhone 2+,Android 2+,并提供一致的API以处理跨浏览器的存储。


3

store.js 在 IE 中使用 userData,在其他浏览器中使用 localStorage。

  • 它不会尝试做过于复杂的事情

  • 无需 cookie、flash 或 jQuery。

  • 简洁的 API。

  • 压缩后只有 5 kb。

https://github.com/marcuswestin/store.js


1

Lawnchair似乎也是一个不错的选择。

草地椅子有点像沙发,只是更小并且在室外。对于需要轻量级、适应性强、简单优雅的持久性解决方案的html5移动应用程序非常完美。

  • 集合。Lawnchair实例实际上只是一个对象数组。
  • 适应性持久性。底层存储在一致的接口后面被抽象化。
  • 可插入集合行为。有时我们需要集合助手,但并不总是需要。

1

0

谢谢。不幸的是,它似乎支持的浏览器比 @josh3736 建议的 PersistJS 更少。我仍然会看一下它,并感谢这个建议。 - Tauren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接