描述cookie、sessionStorage和localStorage的区别
- 场景应用不同:cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。cookie由服务端生成,用于标识用户身份;而两个storage用于浏览器端缓存数据。
- 存储大小限制也不同,cookie数据不能超过4k,因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。
- 数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。
- 作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。
按照过期时间 cookie 分为两类:会话cookie和持久cookie。会话cookie是一种临时cookie,用户退出浏览器,会话cookie就会被删除了,持久cookie则会储存在硬盘里,保留时间更长,关闭浏览器,重启电脑,它依然存在,通常是持久性的cookie会维护某一个用户周期性访问服务器的配置文件或者登录信息。持久cookie 设置一个特定的过期时间(Expires)或者有效期(Max-Age),客户端会根据这个有效时间进行删除,如果不写,该有效时间默认为永久。
ajax,jsonp 与 cors
ajax 与 jsonp 的不同之处
- 本质不同
ajax 是一种发送 HTTP 请求与后台进行异步通讯从而实现局部刷新页面的技术,核心是通过 XMLHttpRequest 对象来获取服务端所提供的数据。
jsonp 是数据格式 JSON 的一种“使用模式”,可以让网页从别的网域要数据,核心是利用 标签没有跨域限制的漏洞,服务端解析 URL 得到查询参数 callback,同时将这个 callback 参数值作为函数名来包裹住客户端所需要的 JSON 数据,从而实现获取服务端数据。 - 请求方法不同
ajax 可以发送 GET 和 POST 请求,而 jsonp 只能发送 GET 请求。 - 补充
它们的区别并不在于是否跨域,ajax 利用 cors 也能实现跨域,而 jsonp 也可以访问同域数据。
js 实现 ajax
///对发送数据的序列化
function serialize(data) {
let arr = [];
for(let key in data) {
arr.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
}
return arr.join('&');
}
/**
* @param {} params
* data: oebjct,
* type: String('GET' OR 'POST'),
* url: String,
* success: Function,
* error: Function
*/
function ajax(params) {
params = params || {};
params.data = params.data || {};
params.type = params.type || 'GET';
params.data = serialize(params.data);
//如果是在ie6浏览器,那么XMLHttoRequest是不存在的,应该调用ActiveXObject;
let xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
if(params.type === 'GET') {
xhr.open(params.type, params.url + '?' + params.data, true);
xhr.send(null);
} else {
xhr.open(params.type, params.url, true);
//设置表单提交时的内容类型
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(params.data);
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
var res;
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
if(params.success && params.success instanceof Function) {
res = JSON.parse(xhr.responseText);
params.success.call(xhr, res);
}
} else {
if(params.error && params.error instanceof Function) {
res = JSON.parse(xhr.responseText);
params.error.call(xhr, res);
}
}
}
}
}
jsonp 实现过程
1:首先声明一个用于处理服务器返回过来的数据的回调函数;
2:创建一个<script>
标签,把跨域的API数据接口地址,赋值给 script 的 src,同时将前面创建的回调函数名加在地址的查询参数里,以便传给服务器端;
3:服务器接收到请求后,解析 url 获取传递过来的函数名,把函数名和想要返回给客户端的 JSON 数据(或者是其他格式的数据)拼接成一个字符串;
4:把上一步拼接成的字符串通过 HTTP 协议返回给客户端,于是这段 js 代码就可以调用回调函数,以此实现跨域数据访问。
简单代码实现:
//前端代码
function jsonp({ url, params, callback }) {
return new Promise((resolve, reject) => {
let script = document.createElement('script')
window[callback] = function(data) {
resolve(data)
document.body.removeChild(script)
window[callback] = null
}
params = { ...params, callback }
let arrs = []
for (let key in params) {
arrs.push(`${key}=${params[key]}`)
}
script.src = `${url}?${arrs.join('&')}`
document.body.appendChild(script)
})
}
jsonp({
url: 'http://localhost:3000/say',
params: {
wd: 'Iloveyou'
},
callback: 'show'
}).then(data => {
alert(data)
})
//后端代码
let express = require('express')
let app = express()
app.get('/say', function(req, res) {
let { wd, callback } = req.query
console.log(wd) // Iloveyou
console.log(callback) // show
res.end(`${callback}('Iloveyoutoo')`)
})
app.listen(3000);
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。实现 cors 的关键在于后端,与前端的关系在于是简单请求还是复杂请求。下面是一个复杂请求的 cors 跨域实例:
//前端代码
//发送一个ajax请求
let xhr = new XMLHttpRequest();
xhr.open('PUT', 'http://localhost:3000/test', true);
xhr.withCredentials = true;
xhr.setRequestHeader('name', 'xiamen');
xhr.send();
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
console.log(xhr.getAllResponseHeaders());
console.log(document.cookie);
/*Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的 Cookie。*/
}
}
//后端代码
let express = require('express');
let app = express();
let whiteList = ['http://127.0.0.1:8080'];
app.use((req, res, next) => {
let origin = req.headers.origin;
let cookie = req.headers.cookie;
if(whiteList.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Methods', 'PUT');
// 表示服务器将会支持的请求头部值
res.setHeader('Access-Control-Allow-Headers', 'name');
// 允许携带cookie
res.setHeader('Access-Control-Allow-Credentials', 'true');
// 该字段可选,用来指定本次预检请求的有效期,单位为秒。
res.setHeader('Access-Control-Max-Age', 5);
// 允许返回的头
res.setHeader('Access-Control-Expose-Headers', 'name');
if(req.method === 'OPTIONS') {
res.end();
} else if(!cookie) {
res.setHeader('Set-Cookie', 'age=22');
}
}
next();
});
app.put('/test', (req, res) => {
console.log(req.headers);
res.setHeader('name', 'lily');
res.end('hello world!');
});
app.listen(3000, () => {
console.log('server is running on port 3000.');
});