目录
1、什么是浏览器的同源策略?为什么要有同源策略?
2、怎么解决跨域问题?
2.1、JSONP
2.2、WebSocket
2.3、Cors
2.4、反向代理(node接口代理)
2.5、Nginx
2.6、postMessage
3、浏览器的本地存储方式有哪些,有什么区别,分别有哪些应用场景?
3.1、webStorage
3.2、Cookie
3.3、IndexedDB
3.4、Web SQL
4、回流与重绘
官方解析:同源策略(Same-Origin Policy)是浏览器安全策略的一项重要规则,它限制了浏览器只允许当前网页的脚本与来自同一站点(协议、主机、端口号相同)的窗口进行交互,而限制了与不同源(协议、主机、端口号任一不同)的窗口进行交互。同源策略的目的在于有效保护用户的信息安全和隐私。
同源策略主要分为三种:
如果不存在同源策略的限制,将会导致一系列Web攻击,如跨站脚本攻击(XSS)和跨站请求伪造(CSRF)。例如,通过iframe引入其他页面,用户在页面中输入了用户名和密码,恶意攻击者就可以获取不同源的DOM结构,进而获取用户的敏感信息。又如,用户向网站A发送的请求携带了对应的Cookie,当用户访问恶意网站B时,恶意脚本可能导致向网站A发送携带Cookie的Ajax请求,从而获取用户信息。同源策略的存在有助于防范这些安全风险。
官方解析:跨域问题(Cross-Origin Resource Sharing,CORS)是由于浏览器的同源策略(Same-Origin Policy)导致的。同源策略要求两个 URL 的协议、主机名和端口号都相同,否则就被认为是跨域的。浏览器在跨域请求时会受到同源策略的限制。
为了解决跨域问题,可以采用以下几种方法:
因为浏览器同源策略的存在,导致存在跨域问题,以下这三个标签加载资源路径是不受束缚的:
1. script标签:
2. link标签:
3. img标签:
JSONP利用了script标签的src加载不受同源策略限制的特性。通过在前端创建一个script标签,将回调函数名传递给后端,后端在接收到请求后,将所需数据拼接成一个字符串,形如callback(所需数据),并作为脚本返回给前端。前端接收到这个字符串后,会自动执行回调函数,从而获取数据。JSONP需要前后端协作,而且仅支持GET请求方法。
前端代码示例:
// index.html http://127.0.0.1:8080/index.html
const jsonp = (url, params, cbName) => {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
window[cbName] = (data) => {
resolve(data);
document.body.removeChild(script);
};
params = { ...params, callback: cbName };
const str = Object.keys(params).map(key => `${key}=${params[key]}`);
script.src = `${url}?${str.join('&')}`;
document.body.appendChild(script);
});
};
jsonp('http://example.com/api', { name: 'john', age: 25 }, 'callback')
.then(data => {
console.log(data); // 处理获取的数据
});
后端代码示例:
// index.js http://127.0.0.1:8000
const http = require('http');
const urllib = require('url');
const port = 8000;
http.createServer(function(req, res) {
const { query } = urllib.parse(req.url, true);
if (query && query.callback) {
const { name, age, callback } = query;
const person = `${name}今年${age}岁了!!!`;
const str = `${callback}(${JSON.stringify(person)})`;
res.end(str);
} else {
res.end(JSON.stringify('没有数据'));
}
}).listen(port, function() {
console.log('server is listening on port ' + port);
});
JSONP的缺点是需要前后端协作,且仅支持GET请求方法。
WebSocket在设计时就考虑了可以跨域使用,与同源策略不附属。与传统的跨域检测方式不同,WebSocket的跨域检测是由服务端来处理的。尽管浏览器仍会携带Origin跨域请求头,但服务端负责判断这次跨域WebSocket请求是否合法。这种设计机制为WebSocket提供了更灵活的跨域使用方式。
前端代码示例:
// index.html http://127.0.0.1:8080/index.html
function myWebsocket(url,params){
return new Promise((resolve,reject)=>{
const socket = new WebSocket(url)
socket.onopen = () => {
socket.send(JSON.stringify(params))
}
socket.onmessage = (e) => {
resolve(e.data)
}
})
}
myWebsocket('ws://127.0.0.1:8000',{ name:'mos', age:18 }).then(data=>){
console.log(data) // mos今年18岁了!!!
}
后端代码示例:
// index.js http://127.0.0.1:8000
const Websocket = require('ws');
const port = 8000;
const ws = new Websocket.Server({ port })
ws.on('connection',(obj) =>{
obj.on('message',(data) =>{
data = JSON.parse(data.toString())
const { name, age } = data
obj.send(`${name}今年${age}岁了!!!`)
})
})
CORS,全称为Cross-Origin Resource Sharing,意为跨域资源共享。通常由后端来进行配置开启,一旦开启,前端就可以跨域访问后端。在前端跨域访问到后端时,后端开启CORS,发送Access-Control-Allow-Origin字段到前端,如果域名匹配,浏览器就不会执行跨域拦截,从而解决跨域问题。
前端代码示例:
// index.html http://127.0.0.1:8080/index.html
// 步骤一:创建异步对象
var ajax = new XMLHttpRequest();
// 步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数
ajax.open('get', 'http://127.0.0.1:8000?name=梁理论&age=23');
// 步骤三:发送请求
ajax.send();
// 步骤四:注册事件 onreadystatechange 状态改变就会调用
ajax.onreadystatechange = function () {
if (ajax.readyState == 4 && ajax.status == 200) {
// 步骤五 如果能够进到这个判断 说明数据完美的回来了,并且请求的页面是存在的
console.log(ajax.responseText);
}
}
后端代码示例:
// index.js http://127.0.0.1:8000
const http = require('http');
const urllib = require('url');
const port = 8000;
http.createServer(function(req,res){
// 开启Cors
res.writeHead(200,{
// 设置允许跨域的域名,也可设置*允许所有域名
'Access-Control-Allow-Origin': 'http://127.0.0.1.8080',
// 跨域允许的请求⽅法,也可以设置*允许所有⽅法
"Access-Control-Allow-Methods": 'DELETE,PUT,POST,GET,OPIIONS',
// 允许的header类型
'Access-Contorl-Allow-Headers': 'Content-Type'
})
const { query : { name, age } } = urllib.parse(req.url, true);
res.end(`${name}今年${age}岁啦!!!`);
}).listen(port,function(){
console.log('server is listening on port ' + port);
})
同源策略它只是浏览器的⼀个策略而已,它是不限制后端的,也就是前端-后端会被同源策略限制,但是后端-后端 则不会被限制,所以可以通过Node接口代理,先访问已设置Cors的后端1,再让后端1去访问后端2获取数据到后端1,后端1再把数据传到前端
前端代码:
// index.html http://127.0.0.1:8080
//步骤⼀:创建异步对象
var ajax = new XMLHttpRequest();
//步骤⼆:设置请求的url参数,参数⼀是请求的类型,参数⼆是请求的url,可以带参数,动态的传递参数
starName到服务端
ajax.open('get', 'http://127.0.0.1:8888?name=mos&age=18');
//步骤三:发送请求
ajax.send();
//步骤四:注册事件 onreadystatechange 状态改变就会调⽤
ajax.onreadystatechange = function () {
if (ajax.readyState == 4 && ajax.status == 200) {
//步骤五 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的⻚⾯是存在的
console.log(ajax.responseText);//输⼊相应的内容
}
}
后端1代码:
// index2.js http://127.0.0.1:8888
const http = require('http');
const urllib = require('url');
const querystring = require('querystring');
const port = 8888;
http.createServer(function (req, res) {
// 开启Cors
res.writeHead(200, {
//设置允许跨域的域名,也可设置*允许所有域名
'Access-Control-Allow-Origin': 'http://127.0.0.1:5500',
//跨域允许的请求⽅法,也可设置*允许所有⽅法
"Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS",
//允许的header类型
'Access-Control-Allow-Headers': 'Content-Type'
})
const { query } = urllib.parse(req.url, true);
const { methods = 'GET', headers } = req
const proxyReq = http.request({
host: '127.0.0.1',
port: '8000',
path: `/?${querystring.stringify(query)}`,
methods,
headers
}, proxyRes => {
proxyRes.on('data', chunk => {
console.log(chunk.toString())
res.end(chunk.toString())
})
}).end()
}).listen(port, function () {
console.log('server is listening on port ' + port);
})
后端2代码:
// index.js http://127.0.0.1:8000
const http = require('http');
const urllib = require('url');
const port = 8000;
http.createServer(function (req, res) {
console.log(888)
const { query: { name, age } } = urllib.parse(req.url, true);
res.end(`${name}今年${age}岁啦!!!`)
}).listen(port, function () {
console.log('server is listening on port ' + port);
})
其实Nginx跟node接口代理是同一个道理,只不过Nginx不需要我们去搭建一个中间服务
server{
listen 8888;
server_name 127.0.0.1;
location /{
proxy_pass 127.0.0.1:8000;
}
}
场景: 在 http://127.0.0.1:5500/index.html 页面中使用了
尽管这两个页面存在于同一个页面中,但需要使用
那应该怎么办呢?使用postMessage可以使这两个页面进行通信
// http:127.0.0.1:5500/index.html
// http://127.0.0.1:5555/index.html
常见的浏览器的本地存储方式:webSrorage( localStorage 和 sessionStorage )、indexedDB、Cookies、Web SQL
localStorage:
用于存储持久数据,除非用户手动将其从浏览器中删除,否则数据经终身存储,即使用户关闭窗口或选项卡,它也不会过期。
sessionStorage:
用户存储临时会话数据,页面重新加载后数据仍然存在,但当关闭浏览器或选项卡时数据就会丢失。
方法和属性:
localStorage 和 sessionStorage 都非常适合缓存非敏感应用数据,可以在需要存储少量简单值的情况下使用。它们本质上是同步的,并且会阻塞主 UI 线程,因此需要谨慎使用。
Cookie主要用于身份验证和用户数据持久性。Cookies与请求一起发送到服务器,并在响应时发送到客户端;因此,cookie数据在每次请求时都会与服务器交换。服务器可以使用 cookie 数据向用户发送个性化内容。严格来说,cookie 并不是客户端存储方式,因为服务器和浏览器都可以修改数据。它是唯一可以在一段时间后自动使数据过期的方式。
每个 HTTP 请求和响应都会发送 cookie 数据。存储过多的数据会使 HTTP 请求更加冗长,从而使应用比预期更慢:
Cookie 通常用于会话管理、个性化以及跨网站跟踪用户行为。我们可以通过服务端和客户端设置和访问 cookie。Cookie 还具有各种属性,这些属性决定了在何处以及如何访问和修改它们。
Cookie 分为两种类型:
IndexedDB 提供了一个类似 NoSQL 的 key/value 数据库,它可以存储大量结构化数据,甚至是文件和 blob。每个域至少有 1GB 的可用空间,并且最多可以达到剩余磁盘空间的 60%。
IndexedDB 特点如下:
IndexedDB 使用场景:
Web SQL 是 HTML5 中的另一种本地数据库存储方案,采用 SQL 语句进行数据存储和查询。然而,目前已经被弃用,不建议使用。
应用场景:
Web SQL 类似于 IndexedDB,适用于需要离线存储数据和进行本地数据库操作等场景。
回流一定会伴随着重绘,但是重绘可以单独发生。
页面渲染过程包括:
回流(reflow): Render Tree 中节点的大小、边距等发生改变时需要重新计算几何信息的过程,称为回流。
重绘(repaint): 改变元素的字体颜色、背景颜色等不会影响页面布局变化的操作,称为重绘。
简而言之,影响页面布局的需要回流,不会改变页面布局的只需要重绘。回流的消耗较大(涉及到重新布局,一定会伴随着重绘),而重绘的消耗相对较小。
引起回流的操作
减少回流的方法