输出就是最好的输入,嗯,有道理。
干货在后面,可以跳过这部分
有一个理论,叫专家盲点,你问一个气象家,什么是风,他会一脸正经,一字一句的说:‘风是由空气流动引起的一种自然现象,它是由太阳辐射热引起的。。。’然后你就。。。如果回答的是:‘你感受一下,这就是风’,你是不是会瞬间明白呢。如果你想检验你是否学懂了一个知识点,你可以这样检验,试着向一个小白解释这个知识点,,看看他能不能听懂。我就是想检验一下偶自己,,也许没人看,但,根本目的不是为了让别人看到,因为知识有输出,你就会有输入。。
跨域到底是啥?
比如:你自己做了一个Web应用,功能是展示各地天气情况,好了,你的页面都已经写好了,但我改如何获取各地的天气情况呢,这就需要你通过请求去获取,比如你的天气数据来源是中国天气网,那么你就需要向该网站的天气API发送请求获取天气数据。但是,,人家的数据你是访问不到的,因为浏览器不会允许你的网站从第三方网站获取数据,这是因为浏览器的同源政策,所谓同源,指两个网站的协议,域名,端口号都相同,这样两个网站直接才能相互通信。同源政策是网景公司引入浏览器的,现在浏览器都支持,目的是为了保证用户信息安全。
但是,安全是安全了,但我们的功能该如何实现呢?即如何向第三方发起请求,二者进行通信呢?这就是所谓的跨域。
如何实现跨域呢?
现在我们知道了,需要跨域才能进行两个非同源的网站的通信,那具体该如何实现呢?有以下几种方法:
- JSONP
额,,不要先纠结这个名词。看看他是如何实现的吧。
JSONP原理: 利用script标签没有跨域限制的特性,让它指向第三方网站,即用script标签发出请求,但它请求的资源会被当成JS去执行,那如何让这个返回的数据能够被我所用,能够被得到执行呢?这就需要第三方网站给你的数据是一个数据JSON包,它返回的数据,不是简单的数据,而是用一个回调函数包装起来的,作为函数参数返回给你自己已经定义好的函数,这个函数的功能就是你对数据的操作处理。这样就可以请求到底三方的资源了,,但前提是需要第三方给你提供这个请求接口你才能够请求到数据。
实例;功能点击按钮,实现换组新闻:
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>news</title>
<style>
.container {
width: 900px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<ul class="news">
<li>第11日前瞻:中国冲击4金 博尔特再战</li>
<li>男双力争会师决赛 </li>
<li>女排将死磕巴西!</li>
</ul>
<button class="change">换一组</button>
</div>
<script>
$('.change').addEventListener('click', function() {
var script = document.createElement('script'); //创建一个script标签
script.src = 'http://localhost:8080/getNews?callback=appendHtml';
document.head.appendChild(script);
document.head.removeChild(script);
})
function appendHtml(news) {
var html = '';
for (var i = 0; i < news.length; i++) {
html += '<li>' + news[i] + '</li>';
}
console.log(html);
$('.news').innerHTML = html;
}
function $(id) {
return document.querySelector(id);
}
</script>
</html>
后端数据mock
app.get('/getNews', function(req, res) {
var news = [
"第11日前瞻:中国冲击4金 博尔特再战200米羽球",
"正直播柴飚/洪炜出战 男双力争会师决赛",
"女排将死磕巴西!郎平安排男陪练模仿对方核心",
"没有中国选手和巨星的110米栏 我们还看吗?",
"中英上演奥运金牌大战",
"博彩赔率挺中国夺回第二纽约时报:中国因对手服禁药而丢失的奖牌最多",
"最“出柜”奥运?同性之爱闪耀里约",
"下跪拜谢与洪荒之力一样 都是真情流露"
]
var data = [];
for (var i = 0; i < 3; i++) {
var index = parseInt(Math.random() * news.length);
data.push(news[index]);
news.splice(index, 1);
}
var cb = req.query.callback; //获取请求参数
if (cb) { //判断是否有回调函数这个参数
res.send(cb + '(' + JSON.stringify(data) + ')'); //如果有:就会返回appendHtml()
} else {
res.send(data);
}
})
- 第二种方法,CORS实现跨域。
对于AJAX来说,要实现跨域请求,前端代码不需要做任何改变,就像用AJAX发送同源请求一样,,只不过需要在后端进行一些额外配置,就是在服务端的响应头部增加一段代码处理即可,整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>news</title>
<style>
.container {
width: 900px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<ul class="news">
<li>第11日前瞻:中国冲击4金 博尔特再战</li>
<li>男双力争会师决赛 </li>
<li>女排将死磕巴西!</li>
</ul>
<button class="change">换一组</button>
</div>
<script>
//注意,AJAX跨域并不需要前端代码改变什么,就和正常AJAX代码一样写就可以。。即,,AJAX本身就是支持跨域,只是后端需要支持
$('.change').addEventListener('click', function() {
var xhr = new XMLHttpRequest();
xhr.open('get', 'http://b.jrg.com:8080/getNews', true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
appendHtml(JSON.parse(xhr.responseText))
}
}
})
function appendHtml(news) {
var html = '';
for (var i = 0; i < news.length; i++) {
html += '<li>' + news[i] + '</li>';
}
console.log(html);
$('.news').innerHTML = html;
}
function $(id) {
return document.querySelector(id);
}
</script>
</html>
后端数据mock
app.get('/getNews', function(req, res) {
var news = [
"第11日前瞻:中国冲击4金 博尔特再战200米羽球",
"正直播柴飚/洪炜出战 男双力争会师决赛",
"女排将死磕巴西!郎平安排男陪练模仿对方核心",
"没有中国选手和巨星的110米栏 我们还看吗?",
"中英上演奥运金牌大战",
"博彩赔率挺中国夺回第二纽约时报:中国因对手服禁药而丢失的奖牌最多",
"最“出柜”奥运?同性之爱闪耀里约",
"下跪拜谢与洪荒之力一样 都是真情流露"
]
var data = [];
for (var i = 0; i < 3; i++) {
var index = parseInt(Math.random() * news.length);
data.push(news[index]);
news.splice(index, 1);
}
res.header("Access-Control-Allow-Origin", "http://a.jrg.com:8080"); //服务端在响应投加上允许跨域请求的网站即可,只有它允许的才可以访问到
//res.header("Access-Control-Allow-Origin", "*"); *表示接收所有跨域请求
res.send(data);
})
- 第三种方法,降域。
当你在一个网页中用iframe标签引入另一个网站时,有两种情况,1,该网页是你自己的一个网页,顶级域名和父的域名是一样的,只是二级域名不同,这种情况下,可以通过降域的方式实现两个窗口之间的通信。降域的关键:通过设置网页的 domain 属性实现。
父窗口代码
<html>
<style>
.ct {
width: 910px;
margin: auto;
}
.main {
float: left;
width: 450px;
height: 300px;
border: 1px solid #ccc;
}
.main input {
margin: 20px;
width: 200px;
}
.iframe {
float: right;
}
iframe {
width: 450px;
height: 300px;
border: 1px dashed #ccc;
}
</style>
<div class="ct">
<h1>使用降域实现跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.jrg.com:8080/a.html">
</div>
<iframe src="b.html" frameborder="0"></iframe>
</div>
<script>
//URL: http://a.jrg.com:8080/a.html
document.querySelector('.main input').addEventListener('input', function() {
console.log(this.value);
window.frames[0].document.querySelector('input').value = this.value;
})
// document.domain = "jrg.com" 这是降域
</script>
</html>
子窗口代码
<html>
<style>
html,
body {
margin: 0;
}
input {
margin: 20px;
width: 200px;
}
</style>
<input id="input" type="text" placeholder="http://b.jrg.com:8080/b.html">
<script>
// URL: http://b.jrg.com:8080/b.html
document.querySelector('#input').addEventListener('input', function() {
window.parent.document.querySelector('input').value = this.value;
})
// document.domain = 'jrg.com'; 这是降域处理
</script>
</html>
- 第四种降域方法 :postmessage实现
上面直说了一种情况,,而另外一种情况就是,这两个网站完全不同同源,如何实现二者之间的通信呢,HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。
关键就是调用 postMessage这个方法。
父窗口
<html>
<style>
.ct {
width: 910px;
margin: auto;
}
.main {
float: left;
width: 450px;
height: 300px;
border: 1px solid #ccc;
}
.main input {
margin: 20px;
width: 200px;
}
.iframe {
float: right;
}
iframe {
width: 450px;
height: 300px;
border: 1px dashed #ccc;
}
</style>
<div class="ct">
<h1>使用postMessage实现跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.jrg.com:8080/a.html">
</div>
<iframe src="http://localhost:8080/b.html" frameborder="0"></iframe>
</div>
<script>
//URL: http://a.jrg.com:8080/a.html
$('.main input').addEventListener('input', function() {
console.log(this.value);
window.frames[0].postMessage(this.value, '*');
})
window.addEventListener('message', function(e) {
$('.main input').value = e.data
console.log(e.data);
});
function $(id) {
return document.querySelector(id);
}
</script>
</html>
子窗口
<html>
<style>
html,
body {
margin: 0;
}
input {
margin: 20px;
width: 200px;
}
</style>
<input id="input" type="text" placeholder="http://b.jrg.com:8080/b.html">
<script>
// URL: http://b.jrg.com:8080/b.html
$('#input').addEventListener('input', function() {
window.parent.postMessage(this.value, '*');
})
window.addEventListener('message', function(e) {
$('#input').value = e.data
console.log(e.data);
});
function $(id) {
return document.querySelector(id);
}
</script>
</html>
说明:
前后端代码都需要在静态服务器上运行,我用的是Nojs下的srver-mock工具。