前言:
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份.
一 什么是cookie
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
1.1 cookie的原理
一个cookie的设置以及发送过程分为以下四步:
1.2 操作cookie
cookie的常用属性:
// 1. expires 有效期
// 2. path 路径
// 3. domain 域名
1.2.1 服务器操作cookie
php操作cookie
// 设置cookie
// setcookie(name, value, expire, path, domain);
// 设置1小时后过期
setcookie("user", "lisi", time()+3600);
// 获取单个cookie
echo $_COOKIE["user"];
// 查看所有cookie
print_r($_COOKIE);
1.2.2 (客服端)JS操作cookie
// 设置cookie
function setCookie(key,value,param){
document.cookie = key + '=' + value + '; expires=' + param.expires + '; path=' + param.path;
}
// 获取cookie
function getCookie(key){
var cookies = document.cookie;
var arr = cookies.split('; ');
if(arr){
for (var i = 0; i < arr.length; i++) {
var kv = arr[i].split('=');
if(kv[0] == key){
return kv[1];
}
}
}
}
1.2.3 jQuery操作cookie
cookie插件基本使用
设置单个cookie
获取单个cookie
获取所有cookie
删除单个cookie
浏览器中cookie基本设置
1.3 cookie的节流
因为浏览器每次发送http请求的时候,都会带上cookie,这样如果浏览器请求的静态资源(图片等)也会带上cookie,那样会造成比较严重的带宽浪费.
解决办法:
1. 利用cdn实现请求静态资源是一个服务器,实现业务逻辑是一个服务器.
2. 严格控制cookie存放的有效,有用信息
1.4 cookie的跨域
二 什么是session
seesion是为了解决cookie安全问题的,把数据存入前端太危险,(数据存入服务端)
因为如果重要的数据都存在浏览器端,这样的数据容易泄露以及被修改,所以我们把原来存在cookie中部分数据存到服务器端.这种技术就叫session.
session可以理解为是服务器上的一个保险柜,把所有数据都保存在服务器的保险柜里,只给浏览器一把钥匙用来访问这些数据。数据都是存在服务器端,而浏览器端只存一把 钥匙。因为要把 钥匙给浏览器存起来,存钥匙的技术还是cookie,所以说session技术是基于cookie的。
2.1 php操作session
1. php新增session
//一定要先调用此函数开启session
session_start();
//通过超全局变量添加一条session记录
$_SESSION['名']= 值;
2. php删除session
// 开启session
session_start();
// 删除保存的session
// 删除变量的值
unset($_SESSION['name']);
3. php修改session
// 开启session
session_start();
//修改
$_SESSION['name'] = 'rose';
4. php查询session
//一定要先调用此函数开启session
session_start();
//直接通过超全局变量可以取到session记录
var_dump($_SESSION);
//单独取出某条$_SESSION记录
echo $_SESSION['name'];
2.2 nodeJS操作session
const session = require('express-session')
// 使用session的组件
// 集成session中间件 req.session.xxx = yyy req.session.xxx
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}));
app.get('/awesome', function(req, res){
if(req.session.lastPage) {
console.log('Last page was: ' + req.session.lastPage + ".");
}
req.session.lastPage = '/awesome';
res.send("You're Awesome. And the session expired time is: " + req.session.cookie.maxAge);
});
三 cookie与session总结
session可以借助cookie实现状态维持,也可以不依赖cookie(URL重写)
3.1 cookie的不可跨域性:
Cookie具有不可跨域名性。根据Cookie规范,浏览器访问Google只会携带Google的Cookie,而不会携带Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。
Cookie在客户端是由浏览器来管理的。浏览器能够保证Google只会操作Google的Cookie而不会操作Baidu的Cookie,从而保证用户的隐私安全。浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。Google与Baidu的域名不一样,因此Google不能操作Baidu的Cookie。
需要注意的是,虽然网站images.google.com与网站www.google.com同属于Google,但是域名不一样,二者同样不能互相操作彼此的Cookie。而session对于整个网站都是有效的.
四 localStorage的优势
1、localStorage拓展了cookie的4K限制
2、localStorage会可以将第一次请求的数据直接存储到本地,这个相当于一个5M大小的针对于前端页面的数据库,相比于cookie可以节约带宽,但是这个却是只有在高版本的浏览器中才支持的
4.1 localStorage的局限
1、浏览器的大小不统一,并且在IE8以上的IE版本才支持localStorage这个属性
2、目前所有的浏览器中都会把localStorage的值类型限定为string类型,这个在对我们日常比较常见的JSON对象类型需要一些转换
3、localStorage在浏览器的隐私模式下面是不可读取的
4、localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡
5、localStorage不能被爬虫抓取到
localStorage与sessionStorage的唯一一点区别就是localStorage属于永久性存储,而sessionStorage属于当会话结束的时候,sessionStorage中的键值对会被清空
4.2 实例:实现localStorage的增删查功能
页面代码:
<input type="search" id="search-input" placeholder="请输入你想比价的商品">
<div class="search-history">
<ul>
ul>
div>
<button id="search-btn">搜索button>
操作localStorage的JS代码
var search = {
//添加localstorage的方法
addlocalStorage : function () {
var that = this;
$('#search-btn').on('click',function(){
var searchContent = $('#search-input').val(); //搜索框里的内容
//判断搜索框里的内容是否为空,如果为空不添加到localstorage不继续下面的操作
if(!searchContent) {
//内容为空
return false;
}
//获取浏览器localstorage中键名为searchHistory中的数据
var historyData = localStorage.getItem('searchHistory');
if(!historyData) {
//因为一开始就没有这个searchHistory键名所以获取到为空,此时将historyData赋值为空数组
historyData = [];
}else {
//不为空,那么键名存在,数据也存在并且是字符串,现在转化为数组
historyData = JSON.parse(historyData);
}
//判断搜索的内容是否重复
if(historyData.indexOf(searchContent) != -1) {
//如果有删除原来的内容,再作为第一个元素添加在数组前
historyData.splice(historyData.indexOf(searchContent),1); //删除
historyData.unshift(searchContent); //添加
}else {
//如果没有,直接作为第一个元素添加在数组前
historyData.unshift(searchContent);
}
//将数据添加到键名为searchHistory中localstorage中的数据,不过又要把数组转成字符串
localStorage.setItem('searchHistory',JSON.stringify(historyData));
//调用查询历史记录的方法
that.queryHistory(); //在这里调用的意思是我搜索后就渲染到页面,可以不加
//清空下拉框里面的数据
$('#search-input').val("");
});
},
//查询localstorage的方法
queryLocalStorage : function(){
//先取出来
var searchData = JSON.parse(localStorage.getItem('searchHistory'));
//循环遍历生成li标签
var liHTML = "";
for(var i=0;i<searchData.length;i++) {
//searchData里面有没有数据
if(searchData[i]) {
var str = '' + searchData[i] +'';
liHTML += str;
}
}
//在页面上的操作渲染li
//doSometing.......
//给输入框一个成为焦点事件
$('#search-input').on('focus',function(){
$('.search-history ul').html(liHTML);
//再给li一个点击事件
$('.search-history ul li').on('click',function(){
$('#search-input').val($(this).text());
$('.search-history ul').hide();
});
});
},
//删除历史记录
deleteHistory : function () {
var that = this;
$('.content ul').on('tap','i',function() {
//获取被点击的索引id
var index = $(this).data('id');
//获取当前本地存储的数组 把当前索引的值删掉
var historyData = JSON.parse(localStorage.getItem('historyData')) || [];
historyData.splice(index, 1);
//删除完成后存储到本地存储中 把数组转成字符串
localStorage.setItem('historyData', JSON.stringify(historyData));
//查询新的历史记录
that.queryHistory();
});
},
//清空历史记录
clearHistory : function () {
var that = this;
$('.clearTrash').on('tap',function(){
//2. 调用本地存储removeItem 删除某个键和值
localStorage.removeItem('historyData');
// 3. 在清空完成后调用查询刷新页面
that.queryHistory();
});
}
}
//调用添加localstorage的方法
search.addlocalStorage();
//调用查询localstorage的方法并渲染到页面
search.queryLocalStorage();
//调用方法,删除历史记录
search.deleteHistory();
//调用方法,清空历史记录
search.clearHistory();
五 vuex配合localStorage增删改
5.1 技术简介
因为在实际操作中,vuex作为一个内存版的管理数据的仓库,当页面一刷新所有的数据都将变为初始值;所以我们可以将数据将存储在localStorage,我们vuex只管在初始化的时候或则数据变化的时候从localStorage获取数据就行了.
5.2 案例
整体需求:
技术实现:
具体业务代码:
因为涉及到两个模块,所以我们抽取为不同的两个模块出来:
store.js模块
//store.js模块
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//按需引入操作LocalStorage的模块
import { addLocalStorageItem,totalBuyCount,updateLocalStorageItem,deleteLocalStorageItem } from "../common/localStorage.js"
//创建一个仓库
const store = new Vuex.Store({
state : {
buyCount : 0, //购物车商品总数
},
getters: { //获取仓库中的数据
getTotalGoodsNum(state){
/**
1) 因为页面一刷新,此时的state.buyCount重置为0,然而,localstorage中存储的购买商品的
总数据是存在的,所以需要假设:如果state.buyCount === 0先去查询localstorage中存储
的购买商品的总数据返回给他,此时view更新.
2) 添加购物车被点击了,state.buyCount在mutations里面变化了,state.buyCount !== 0
此时直接返回state.buyCount当前的值.
*/
return state.buyCount === 0 ? totalBuyCount() : state.buyCount
},
getWantVisitPath(state) {
return state.wantVisitPath
}
},
mutations: { //存入数据到仓库
/**
* 添加购物车信息
* @param {*} state
* @param {*} goods 约定的载荷
* goods { goodsID : 91, count: 2 }
*/
addShopCart(state,addGoods){
//添加数据,将数据存入localstorage中,将更新后的数据返回
state.buyCount = addLocalStorageItem(addGoods);
},
//更新购物车信息
updateShopCart(state,updateItemGoods){
//更新数据,将更新后的数据返回
state.buyCount = updateLocalStorageItem(updateItemGoods)
},
//删除购物车数据
deleteShopCart(state,delGoodsId){
//删除数据,将删除后的数据返回
state.buyCount = deleteLocalStorageItem(delGoodsId)
},
},
});
//导出仓库,在main.js里面注入
export default store
localStorage.js模块
//localStorage.js模块
const key = "GOODS";
/**
* 获取localstorage里面的值 { 90 : 3}
* @param {*} key 键名
*/
const getLocalStorage = ( key = "GOODS" )=>{
return JSON.parse(localStorage.getItem(key) || '{}')
}
//获取总共的buycount
const totalBuyCount = ()=>{
const localGoods = getLocalStorage(key); //获取本地存储的数据
let totalCount = 0;
for( var key in localGoods ) { //遍历本地储存的数据
//将后面的键值加起来
totalCount += localGoods[key];
}
return totalCount
}
//增加记录
const addLocalStorageItem = goods => {
//1. 从localstorage里面取出item值判断
const localGoods = getLocalStorage(key) //这里传值亦可以不传亦可以
//2. 判断是否有这个item,有就在和面覆盖键值,没有就加上去
// goods { goodsID : 91, count: 2 }
if(localGoods[goods.goodsID]) { //这里是利用[]来取对象的键值
//有,计算覆盖前面的item的键值
localGoods[goods.goodsID] += goods.count
}else {
//没有的话,就重新添加
localGoods[goods.goodsID] = goods.count
}
// 3.把对象转成字符串,覆盖保存回去
localStorage.setItem(key,JSON.stringify(localGoods))
// 4.重新统计一下localStorage中最新的数量,返回
return totalBuyCount();
};
//更改记录
const updateLocalStorageItem = updateGoods => {
// console.log(updateGoods) {goodsID: 88, count: 2}
//1. 从localstorage里面取出item值
const localGoods = getLocalStorage(key) //这里传值亦可以不传亦可以
//2. 更新对应的item的值
localGoods[updateGoods.goodsID] = updateGoods.count
// 3.把对象转成字符串,覆盖保存回去
localStorage.setItem(key,JSON.stringify(localGoods))
// 4.重新统计一下localStorage中最新的数量,返回
return totalBuyCount();
};
//删除记录
const deleteLocalStorageItem = delGoodsId => {
//console.log(delGoods) //goodsID: 88
//1. 从localstorage里面取出item值
const localGoods = getLocalStorage(key) //这里传值亦可以不传亦可以
//console.log(localGoods.delGoodsId) 这种方法是错误的,因为不能直接点
//2. 删除对应的值
delete localGoods[delGoodsId]
// 3.把对象转成字符串,覆盖保存回去
localStorage.setItem(key,JSON.stringify(localGoods))
// 4.重新统计一下localStorage中最新的数量,返回
return totalBuyCount();
}
export {
addLocalStorageItem,
totalBuyCount,
getLocalStorage, //获取LocalStorage的数据
updateLocalStorageItem,
deleteLocalStorageItem
};
其他组件获取buycount的操作
this.$store.getters.getTotalGoodsNum
组件提供增删改数据的操作
this.$store.commit('addShopCart',addGoods) //增
this.$store.commit('deleteShopCart',id) //删
this.$store.commit('updateShopCart',goods) //改