前言
大家好,我是林三心。用最通俗易懂的话讲最难的知识点是我的座右铭,根底是进阶的前提是我的初衷。
咱们做前端的,平时跟后端对接接口那是必须的事件,然而可能很多同学疏忽了一个对接过程中可能会产生的问题——跨域,那跨域到底是啥呢?为什么会跨域呢?又怎么能力解决呢?
为什么跨域?
为什么会呈现跨域问题呢?那就不得不讲浏览器的同源策略了,它规定了协定号-域名-端口号
这三者必须都雷同
才合乎同源策略
如有有一个不雷同
,就会呈现跨域问题,不合乎同源策略
导致的结果有
- 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>
后果如下
结语
发表回复