localStorage完成当地贮存树形菜单

localStorage完成当地贮存树形菜单

最近在写一个Todo-list的页面,页面规划和操纵都写完后,想要用localStorage完成当地贮存。然而对贮存数据的要领一窍不通,就先去了解了web的数据贮存。

数据贮存

经常运用的web的数据贮存有cookie和Web Storage贮存机制。

cookie

cookie是“小型文本文件”,主要用途是分辨用户身份、保留用户登录信息。cooki是由服务器端天生、贮存在客户端上的数据(平常经由加密)。

  • Cookie会被附加在每一个HTTP要求中,所以无形中增添了流量。

  • HTTP要求中的Cookie是明文通报的,安全性成问题。(除非用HTTPS)

  • Cookie的大小限定在4KB摆布。于庞杂的存储需求来说是不够用的。

Web Storage

Web Storage定义了两种贮存数据的对象,localStorage和sessionStorage,它们贮存大小平常为5MB,机制以下:

  1. sessionStorage由浏览器存储某个会话(browsing session)的数据,数据在当前会话下有用,革新页面数据照旧存在,但在封闭页面或浏览器后被消灭。

  2. localStorage由浏览器存储数据,数据没有逾期时候(expiration time),也就是浏览器封闭再从新翻开后数据依然存在。

localStorage对象的操纵

在存储中设置值:Storage.setItem()
从存储中获取值:Storage.getItem()
相应存储的变化:window.addEventListener(‘storage’, function(e){});
删除数据纪录:Storage.removeItem() 接收一个参数——你想要移除的数据项的键,然后会将对应的数据项从域名对应的存储对象中移除。Storage.clear() 不接收参数,只是简朴地清空域名对应的全部存储对象。

树形菜单的数据贮存

在我的todo-list页面中,用户对树的操纵有:

  • 在树形菜单上增添、删除使命分类

  • 在使命分类中增添、删除该分类中的使命。
    也是是说用户的每次操纵后都要更新存储的数据,以便在革新页面后显示出用户操纵后更悛改的页面。

由于当地贮存只能存字符串数据,所以存储属性菜单能够用到JSON。用JSON.parse将一个JavaScript对象字符串化为JSON,然后全部存入localStorage来完成保留。用JSON.stringify将JSON字符串剖析成为一个JavaScript对象来从存储中获取值。
那末怎样用对象存储树形菜单呢,我想到了两个体式格局。

体式格局一

一种是用一个对象treeframe存储树的构造(一切的使命分类,不包括分类中的使命)以及树结点中的使命,存储情势以下。一旦对树形菜单实行任何一项操纵,就更新treeframe对象。

var treeframe = {
    "使命列表":{
        "运用说明":[
            {
                "title":"一般使命列表",
                "content":"能够设定使命的称号、使命形貌、使命停止时候",
                "endTime":"2016-05-20",
                "type":"normal",
            },
            {
                "title":"增添新分类",
                "content":"经由过程左边自定义分类的文件夹图标增添新分类",
                "endTime":"2017-07-20",
                "type":"normal",
            }
        ],
        "本日使命":{},
        "短时间使命":{
            "7月":{},
            "8月":{},
            "9月":{}
        },
        "历久使命":{}
     }
}

体式格局二

另一种体式格局是用一个对象treeframe存储树的构造,另有一个对象allTasks存储一切结点中的使命。这类体式格局只须要在增添、删除使命分类时更新树的构造treeframe;增添、删除使命时变动allTasks。由于使命须要增添到树的构造上,所以要纪录使命是增添到哪一个结点上的,须要为每一个树结点增添一个id作为标识以便于在结点上增添使命,树状构造中每一个结点的id依据树的先序遍历将结点的id顺次贮存于数组treeClassify.list中。为了每一个新增的结点的id都不与已存在的id的结点雷同,用一个计数器fatherIdjsq来纪录可增添的新id。
存储情势以下:

var treeframe = {
    "使命列表":{
        "运用说明":{},
        "本日使命":{},
        "短时间使命":{
            "7月":{},
            "8月":{},
            "9月":{}
        },
        "历久使命":{}
     }
}

var allTasks = {
    'list' : [    {
                "fatherId":"fatherId1"
                "title":"一般使命列表",
                "cnode":{"value":"一般使命列表"},
                "pnode":{"value":"运用说明"},
                "content":"能够设定使命的称号、使命形貌、使命停止时候",
                "endTime":"2016-05-20",
                "type":"normal",
            },
            {
                "fatherId":"fatherId5"
                "title":"增添新分类",
                "cnode":{"value":"增添新分类"},
                "pnode":{"value":"运用说明"},
                "content":"经由过程左边自定义分类的文件夹图标增添新分类",
                "endTime":"2017-07-20",
                "type":"fast",
            }]
};
var treeClassfiy = {"list":["fatherId0","fatherId1","fatherId2","fatherId3","fatherId4","fatherId5","fatherId6","fatherId8","fatherId7"]}
var fatherIdjsq = 9

我以为假如最最先写全部todo-list时对全部页面的功用和操纵以及逻辑都理的迥殊清晰的话,第一种存储要领肯定用起来越发轻易。然则我选了第二种,由于如许便于我分别对树的构造和使命列表举行操纵,和我之前写好的代码比较轻易融会在一起。
所以终究要存储的数据以下:

- 树状构造的存储:对象字面量情势与json的互相转化
- 树状构造中每一个结点的id:依据树的先序遍历将结点的id顺次贮存于数组中
- 一切使命:每一个使命存储于一个对象中,一切使命的对象构成一个数组
- 能够增添的新id

树形菜单的操纵与贮存

1. 删除使命分类

变动树状构造,在界面上删除这个分类的后,将这个分类的id从存储id的数组中删去。

var jsq = 0;                                                      //计数器jsq用来纪录按先序遍历树,并将结点存于数组时,该结点在数组中的位置
preOrder(treedata);                                               //先序遍历treeframe(treeframe初始贮存于treedata)
function preOrder(treeframe) {
    for(var key in treeframe) {
        if(jsq===fatherIdArr.indexOf(choseDiv.id)) {              //查找到当前要删除的结点(经由过程检察当前要删除结点的id在贮存结点id的数组(treeClassfiy.list)中的位置)
            delete treeframe[key];                                   //经由过程detele删除对象的属性来删除结点
            var pos = treeClassifyIdArr.list.indexOf(choseDiv.id);//查找当前要删除的结点id在贮存结点id的数组中(treeClassfiy.list)的位置
            treeClassifyIdArr.list.splice(pos,1);                  //把id从数组中删除
            fatherIdArr.splice(jsq, 1);
            break;
        }
        jsq++;
        if(typeof treeframe[key] === "object") {
            preOrder(treeframe[key]);
        }
    }
}
localStorage.setItem('treeframe', JSON.stringify(treedata));      //将更新的treedata存入设置的值treeframe中

2. 增添使命分类

每次手动增添使命分类时,树状构造会转变。也就是说树状构造的转变,意味着树的结点增添/删除了,存储结点id的数组也要转变。
所以要变动树状构造,为新添的分类设置一个新的id,在界面上建立这个分类的后,将这个分类的id插进去到存储id的数组中。

var jsq = 0;
preOrder(treedata);
function preOrder(treeframe) {
    for(var key in treeframe) {
        if(jsq===fatherIdArr.indexOf(choseDiv.id)) {             //找到被选中的结点
            fatherIdArr.push("fatherId"+fatherIdjsq);            //先在数组中增添新的id,使新的结点衬着到页面上
            treeframe[key][value] = {};                          //更新树状构造,增添结点(为纪录树的构造的对象增添属性)
            preOrder2(treeframe[key]);
            function preOrder2(treeframe) {                      //jsq纪录被选中结点的的末了一个子节点
                for(var key in treeframe) {
                    jsq++;
                    if(typeof treeframe[key] === "object") {
                        preOrder2(treeframe[key]);
                    }
                }
            }
            break;
        }
        jsq++;
        if(typeof treeframe[key] === "object") {
            preOrder(treeframe[key]);
        }
    }
}
localStorage.setItem('treeframe', JSON.stringify(treedata));     //将更新的treedata存入设置的值treeframe中
/*此处衬着页面*/ 
fatherIdArr.pop();                                               //新的结点衬着到页面上后删除该结点的id
fatherIdArr.splice(jsq, 0, "fatherId"+fatherIdjsq++);            //更新该结点id在id数组中的位置
treeClassifyIdArr.list = [];                                     //将id数组中的元素增添到对象中
for(var j=0;j<fatherIdArr.length;j++) {
    treeClassifyIdArr.list.push(fatherIdArr[j]);
}
localStorage.setItem('treeClassify', JSON.stringify(treeClassifyIdArr));  //将更新的treeClassifyIdArr存入设置的值treeClassify中
localStorage.setItem('fatherIdjsq', fatherIdjsq);                         //将更新的fatherIdjsq存入设置的值fatherIdjsq中

3. 删除使命

tasklistArr.splice(i,1);
allTasks.list = [];
for(var j=0;j<tasklistArr.length;j++) {
    allTasks.list.push(tasklistArr[j]);
}
localStorage.setItem('allTasks', JSON.stringify(allTasks)); //更新贮存

4. 增添使命

allTasks.list = [];
for(var i=0;i<tasklistArr.length;i++) {
    allTasks.list.push(tasklistArr[i]);
}
localStorage.setItem('allTasks', JSON.stringify(allTasks));//更新贮存

5. 测试当地存储是不是已被添补

if(!localStorage.getItem('treeClassify')) {
//当地没有存储,加载初始设置的页面
} else {
//当地有存储,不加载初始设置的页面,而是依据贮存来衬着页面
}

一切存储操纵的封装

var Storage = (function(mod) {
    // 删除、增添、编辑疾速使命和一般使命的存储
    mod.TaskChange = function() {
        allTasks.list = [];
        for(var i=0;i<tasklistArr.length;i++) {
            allTasks.list.push(tasklistArr[i]);
        }
        localStorage.setItem('allTasks', JSON.stringify(allTasks));
    };
    // 存储纪录使命分类(树结点)的id
    mod.treeNodeIdChange = function(jsq) {
        // 将id数组中的元素增添到对象中
        treeClassifyIdArr.list = [];
        for(var j=0;j<d.fatherIdArr.length;j++) {
            treeClassifyIdArr.list.push(d.fatherIdArr[j]);
        }
          // 将更新的treeClassifyIdArr存入设置的值treeClassify中
        localStorage.setItem('treeClassify', JSON.stringify(treeClassifyIdArr));
        // 将更新的d.fatherIdjsq存入设置的值d.fatherIdjsq中
        localStorage.setItem('d.fatherIdjsq', d.fatherIdjsq);
    };

    // 增添使命分类后的存储
    mod.addClassifySto = function(choseDiv,value) {
        console.log("addClassifySto");
        var jsq = 0;
        preOrderAdd(d.treedata);
        function preOrderAdd(treeframe) {
            for(var key in treeframe) {
                      // 找到被选中的结点
                      if (jsq===d.fatherIdArr.indexOf(choseDiv.id)) {
                      // 先在数组中增添新的id,使新的结点衬着到页面上
                    d.fatherIdArr.push("fatherId"+d.fatherIdjsq); 
                    // 更新树状构造,增添结点(为纪录树的构造的对象增添属性)
                    treeframe[key][value] = {};
                    preOrder2(treeframe[key]);
                    break;
                }
                jsq++;
                if (typeof treeframe[key] === "object") {
                    preOrderAdd(treeframe[key]);
                }
            }
        }
        // jsq纪录被选中结点的的末了一个子节点
        function preOrder2(treeframe) {
            for(var key in treeframe) {
                jsq++;
                if (typeof treeframe[key] === "object") {
                    preOrder2(treeframe[key]);
                }
            }
        }
        // 将更新的treedata存入设置的值treeframe中
        localStorage.setItem('treeframe', JSON.stringify(d.treedata));
        return jsq;
    };
    // 删除使命分类后的存储
    mod.delClassifySto = function(choseDiv) {
        // tree
        console.log("delClassifySto");
        var jsq = 0;
        preOrderDel(d.treedata);
        function preOrderDel(treeframe) {
            for(var key in treeframe) {
                if (jsq===d.fatherIdArr.indexOf(choseDiv.id)) {
                    delete treeframe[key];
                    var pos = treeClassifyIdArr.list.indexOf(choseDiv.id);
                    treeClassifyIdArr.list.splice(pos,1);
                    d.fatherIdArr.splice(jsq, 1);
                    break;
                }
                jsq++;
                if (typeof treeframe[key] === "object") {
                    preOrderDel(treeframe[key]);
                }
            }
        }
        localStorage.setItem('treeframe', JSON.stringify(d.treedata));
        return jsq;
    };
    return mod;
})(Storage||{});

须要注重的处所

加载初始设置的页面的过程当中,不须要存储任何数据,也就是不须要操纵localStorage对象。如许的话假如用户不对页面举行操纵,就没有任何数据须要存储。只要在用户对页面举行操纵后,才会贮存并再页面再次革新时依据贮存的内容举行页面的衬着。

在线检察demo

在线demo可供参考,迎接review代码,求指摘、建媾和交换:p
localStorage完成当地贮存树形菜单_demo

参考资料

详说 Cookie, LocalStorage 与 SessionStorage
JSON.stringify
链运用 Web Storage API

    原文作者:fantast____
    原文地址: https://segmentfault.com/a/1190000006100988
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞