共计 8728 个字符,预计需要花费 22 分钟才能阅读完成。
前言
大家好,我是林三心。用最通俗易懂的话讲最难的知识点 是我的座右铭,根底是进阶的前提 是我的初衷。
咱们做前端的,平时跟后端对接接口那是必须的事件,然而可能很多同学疏忽了一个对接过程中可能会产生的问题——跨域,那跨域到底是啥呢?为什么会跨域呢?又怎么能力解决呢?
为什么跨域?
为什么会呈现跨域问题呢?那就不得不讲浏览器的 同源策略 了,它规定了 协定号 - 域名 - 端口号
这三者必须都 雷同
才合乎 同源策略
如有有一个 不雷同
,就会呈现跨域问题,不合乎 同源策略
导致的结果有
- 1、
LocalStorge、SessionStorge、Cookie
等浏览器内存无奈跨域拜访 - 2、
DOM 节点
无奈跨域操作 - 3、
Ajax 申请
无奈跨域申请
留神点:一个 IP 是能够注册多个不同域名的,也就是多个域名可能指向同一个 IP,即便是这样,他们也不合乎 同源策略
跨域的机会?
跨域产生在什么时候呢?我考过很多位同学,失去了两种答案
- 1、申请一收回就被浏览器的跨域报错拦下来了(大多数人答复)
- 2、申请收回去到后端,后端返回数据,在浏览器接管后端数据时被浏览器的跨域报错拦下来
那到底是哪种呢?咱们能够验证下,咱们先 npm i nodemon -g
,而后创立一个index.js
,而后nodemon index
起一个 node 服务
// index.js http://127.0.0.1:8000
const http = require('http');
const port = 8000;
http.createServer(function (req, res) {const { query} = urllib.parse(req.url, true);
console.log(query.name)
console.log('到后端喽')
res.end(JSON.stringify('林三心'));
}).listen(port, function () {console.log('server is listening on port' + port);
})
再创立一个 index.html
,用来写前端的申请代码,咱们就写一个简略的AJAX 申请
吧
// index.html http://127.0.0.1:5500/index.html
<script>
// 步骤一: 创立异步对象
var ajax = new XMLHttpRequest();
// 步骤二: 设置申请的 url 参数, 参数一是申请的类型, 参数二是申请的 url, 能够带参数
ajax.open('get', 'http://127.0.0.1:8000?name= 前端过去的林三心');
// 步骤三: 发送申请
ajax.send();
// 步骤四: 注册事件 onreadystatechange 状态扭转就会调用
ajax.onreadystatechange = function () {if (ajax.readyState == 4 && ajax.status == 200) {
// 步骤五 如果可能进到这个判断 阐明 数据 完满的回来了, 并且申请的页面是存在的
console.log(ajax.responseText);// 输出相应的内容
}
}
</script>
最终,前端的确是跨域报错了。但这不是后果,咱们要想晓得是哪一个答案,关键在于看后端的 node 服务那里有没有输入,就高深莫测了。所以,答案 2 才是对的。
同域状况 && 跨域状况?
后面提到了 同源策略 ,满足 协定号 - 域名 - 端口号
这三者 都雷同
就是 同域
,反之就是 跨域
,会导致 跨域报错
,上面通过几个例子让大家坚固一下对 同域和跨域
的意识把!
解决跨域的计划
跨域其实是一个很久的问题了,对应的解决方案也有很多,一起接着往下读吧!!!
JSONP
后面咱们说了,因为浏览器 同源策略 的存在,导致存在 跨域问题 ,那有没有不受 跨域问题 所解放的货色呢?其实是有的,以下这三个标签 加载资源门路
是不受解放的
- 1、script 标签:
<script src="加载资源门路"></script>
- 2、link 标签:
<link herf="加载资源门路"></link>
- 3、img 标签:
<img src="加载资源门路"></img>
而 JSONP 就是利用了 script
的src
加载不受解放,从而能够领有从 不同的域
拿到数据的能力。然而 JSONP 须要前端后端配合,能力实现最终的 跨域获取数据
。
JSONP 艰深点说就是:利用 script 的 src 去发送申请,将一个办法名 callback
传给后端,后端拿到这个办法名,将所需数据,通过字符串拼接成新的字符串callback(所需数据)
,并发送到前端,前端接管到这个字符串之后,就会主动执行办法callback(所需数据)
。老规矩,先上图,再上代码。
后端代码
// 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)})` // 拼成 callback(data)
res.end(str);
} else {res.end(JSON.stringify('没货色啊你'));
}
}).listen(port, function () {console.log('server is listening on port' + port);
})
前端代码
// index.html http://127.0.0.1:5500/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 arr = Object.keys(params).map(key => `${key}=${params[key]}`)
script.src = `${url}?${arr.join('&')}`
document.body.appendChild(script)
})
}
jsonp('http://127.0.0.1:8000', { name: '林三心', age: 23}, 'callback').then(data => {console.log(data) // 林三心往年 23 岁啦!!!})
JSONP 的毛病就是,须要前后端配合,并且只反对
get 申请办法
WebSocket
WebSocket 是什么货色?其实我也不怎么懂,然而我也不会像他人一样把 MDN 的材料间接复制过去,因为复制过去置信大家也是看不懂的。
我了解的 WebSocket 是一种协定(跟 http 同级,都是协定),并且他能够进行跨域通信,为什么他反对跨域通信呢?我这里找到一篇文章 WebSocket 凭啥能够跨域?,讲的挺好
后端代码
先装置npm i ws
// 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}岁啦!!!`)
})
})
前端代码
// index.html http://127.0.0.1:5500/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: '林三心', age: 23}).then(data => {console.log(data) // 林三心往年 23 岁啦!!!})
后果如下
Cors
Cors,全称是 Cross-Origin Resource Sharing
,意思是 跨域资源共享
,Cors 个别是由后端来开启的,一旦开启,前端就能够跨域拜访后端。
为什么后端开启 Cors,前端就能跨域申请后端呢?我的了解是:前端跨域拜访到后端,后端开启 Cors,发送 Access-Control-Allow-Origin: 域名
字段到前端(其实不止一个),前端浏览器判断Access-Control-Allow-Origin
的域名如果跟前端域名一样,浏览器就不会履行跨域拦挡,从而解决跨域问题。
后端代码
// 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:5500',
// 跨域容许的申请办法,也可设置 * 容许所有办法
"Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS",
// 容许的 header 类型
'Access-Control-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);
})
前端代码
// index.html http://127.0.0.1:5500/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);// 输出相应的内容
}
}
后果如下
Node 接口代理
还是回到 同源策略 ,同源策略它只是浏览器的一个策略而已,它是限度不到后端的,也就是 前端 - 后端
会被同源策略限度,然而 后端 - 后端
则不会被限度,所以能够通过 Node 接口代理,先拜访已设置 Cors 的后端 1,再让后端 1 去拜访后端 2 获取数据到后端 1,后端 1 再把数据传到前端
后端 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);
})
创立一个index2.js
,并nodmeon index2.js
后端 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);
})
前端代码
// index.html http://127.0.0.1:5500
// 步骤一: 创立异步对象
var ajax = new XMLHttpRequest();
// 步骤二: 设置申请的 url 参数, 参数一是申请的类型, 参数二是申请的 url, 能够带参数, 动静的传递参数 starName 到服务端
ajax.open('get', 'http://127.0.0.1:8888?name= 林三心 &age=23');
// 步骤三: 发送申请
ajax.send();
// 步骤四: 注册事件 onreadystatechange 状态扭转就会调用
ajax.onreadystatechange = function () {if (ajax.readyState == 4 && ajax.status == 200) {
// 步骤五 如果可能进到这个判断 阐明 数据 完满的回来了, 并且申请的页面是存在的
console.log(ajax.responseText);// 输出相应的内容
}
}
后果如下
Nginx
其实 Nginx
跟Node 接口代理
是一个情理,只不过 Nginx 就不须要咱们本人去搭建一个两头服务
先下载 nginx,而后将 nginx 目录下的 nginx.conf 批改如下:
server{
listen 8888;
server_name 127.0.0.1;
location /{proxy_pass 127.0.0.1:8000;}
}
最初通过命令行 nginx -s reload
启动 nginx
后端代码
// 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: { name, age} } = urllib.parse(req.url, true);
res.end(`${name}往年 ${age}岁啦!!!`);
}).listen(port, function () {console.log('server is listening on port' + port);
})
前端代码
// index.html http://127.0.0.1:5500
// 步骤一: 创立异步对象
var ajax = new XMLHttpRequest();
// 步骤二: 设置申请的 url 参数, 参数一是申请的类型, 参数二是申请的 url, 能够带参数, 动静的传递参数 starName 到服务端
ajax.open('get', 'http://127.0.0.1:8888?name= 林三心 &age=23');
// 步骤三: 发送申请
ajax.send();
// 步骤四: 注册事件 onreadystatechange 状态扭转就会调用
ajax.onreadystatechange = function () {if (ajax.readyState == 4 && ajax.status == 200) {
// 步骤五 如果可能进到这个判断 阐明 数据 完满的回来了, 并且申请的页面是存在的
console.log(ajax.responseText);// 输出相应的内容
}
}
后果如下
postMessage
场景:http://127.0.0.1:5500/index.html
页面中应用了 iframe 标签
内嵌了一个 http://127.0.0.1:5555/index.html
的页面
尽管这两个页面存在于一个页面中,然而须要 iframe 标签
来嵌套才行,这两个页面之间是无奈进行通信的,因为他们 端口号
不同,依据 同源策略
,他们之间存在 跨域问题
那应该怎么办呢?应用 postMessage
能够使这两个页面进行通信
// http:127.0.0.1:5500/index.html
<body>
<iframe src="http://127.0.0.1:5555/index.html" id="frame"></iframe>
</body>
<script>
document.getElementById('frame').onload = function () {this.contentWindow.postMessage({ name: '林三心', age: 23}, 'http://127.0.0.1:5555')
window.onmessage = function (e) {console.log(e.data) // 林三心往年 23 岁啦!!!}
}
</script>
// http://127.0.0.1:5555/index.html
<script>
window.onmessage = function (e) {const { data: { name, age}, origin } = e
e.source.postMessage(`${name}往年 ${age}岁啦!!!`, origin)
}
</script>
document.domain && iframe
场景:a.sanxin.com/index.html
与 b.sanxin.com/index.html
之间的通信
其实下面这两个失常状况下是无奈通信的,因为他们的 域名
不雷同,属于跨域通信
那怎么办呢?其实他们有一个共同点,那就是他们的二级域名都是 sanxin.com
,这使得他们能够通过document.domain && iframe
的形式来通信
因为本菜鸟临时没有服务器,所以临时应用本地来模仿
// http://127.0.0.1:5500/index.html
<body>
<iframe src="http://127.0.0.1:5555/index.html" id="frame"></iframe>
</body>
<script>
document.domain = '127.0.0.1'
document.getElementById('frame').onload = function () {console.log(this.contentWindow.data) // 林三心往年 23 岁啦!!!}
</script>
// http://127.0.0.1:5555/index.html
<script>
// window.name="林三心往年 23 岁啦!!!"
document.domain = '127.0.0.1'
var data = '林三心往年 23 岁啦!!!';
</script>
后果如下
结语