http://www.abc123.com/item/a.js
/以下两个数据非同源,因为主机名不同/
http://www.abc123.com.cn/item/a.js
http://item.abc123.com.cn/item/a.js
/以下两个数据非同源,因为协议不同/
http://www.abc123.com.cn/item/a.js
http://www.abc123.com.cn:8080/item/a.js
/* 以下两个数据非同源,域名和 ip 视为不同源
-
这里应注意,ip和域名替换一样不是同源的
-
假设www.abc123.com.cn解析后的 ip 是 195.155.200.134
*/
http://www.abc123.com.cn/
http://195.155.200.134/
/以下两个数据同源/ /* 这个是同源的*/
http://www.abc123.com.cn/source/a.html
http://www.abc123.com.cn/item/b.js
HTTP 简单请求和非简单请求
http 请求满足一下条件时称为简单请求,否则是非简单请求:
-
请求方法是 HEAD,GET,POST 之一
-
HTTP的头信息不超出以下几种字段:
-
Accept
-
Accept-Language
-
Content-Language
-
Last-Event-ID
-
Content-Type
- Content-Type 取值仅限于
application/x-www-form-urlencoded
,multipart/form-data
,text/plain
非简单请求在发送之前会发送一次 OPTION 预请求,如果在跨域操作遇到返回 405(Method Not Allowed) 错误,需要服务端允许 OPTION 请求。
HTTP 跨域访问的处理办法及适用条件
JSOP
适用条件:请求的 GET 接口需要支持 jsonp 访问
这里需要强调的是,jsonp 不属于 Ajax 的部分,它只是把 url 放入 script 标签中实现的数据传输,不受同源策略限制。由于一般库也会把它和 Ajax 封装在一起,由于其和 Ajax 根部不是一回事,所以这里不讨论。下面是一个 jsonp 的例子:
window.jsonpCallback = console.log;
var JSONP = document.createElement(“script”);
JSONP.src = “http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=13122222222&t=” + Math.random() + “&callback=jsonpCallback”;;
document.body.appendChild(JSONP);
后端支持jsonp方式(Nodejs)
var querystring = require(‘querystring’);
var http = require(‘http’);
var server = http.createServer();
server.on(‘request’, function(req, res) {
var params = qs.parse(req.url.split(‘?’)[1]);
var fn = params.callback;
// jsonp返回设置
res.writeHead(200, { ‘Content-Type’: ‘text/javascript’ });
res.write(fn + ‘(’ + JSON.stringify(params) + ‘)’);
res.end();
});
server.listen(‘8080’);
console.log(‘Server is running at port 8080…’);
document.domain
适用条件: host 中仅服务器不同的情况,域名本身应该相同
www.dom.com
和 w1.dom.com
需要同源才能访问,可以将 document.domain 设置为 dom.com
解决该问题
document.domain = ‘dom.com’;
例如,我想开发一个浏览器插件,发现腾讯视频页有个 iframe 其本身的跨域的,无法获取其 iframe 的 DOM 对象。但域名部分相同,可以通过该方法解决.
注:如果你想设置它为完全不同的域名,那肯定会报同源错误的,注意使用范围!
嵌入 iframe
适用条件: host 中仅服务器不同的情况,域名本身应该相同
有了上面的例子就不难理解这个方法了,严格来说这不是一个新的方法,而是上一个方法的延伸。通过设置document.domain
, 使同一个域名下不同服务器名的页面可以访问数据,但值得注意的是:这个数据访问不是相互的,外部页面可以访问 iframe 内部的数据,但 iframe 无法不能访问外部的数据。
location.hash
适用条件:iframe 和其宿主页面通信
一个完成的 url 中 # 及后面的部分为 hash, 可以通过修改这个部分完成iframe 的和宿主直接的数据传递,下面演示一下 iframe 页面(B.html)像宿主(A.html)传数据, 反之同理:
// A.html
data = [‘book’, ‘map’, ‘shelf’, ‘knife’];
setTimeout(() => {
location.hash = window.encodeURIComponent(data.join(‘/’));
}, 1000);
// B.html
window.parent.onhashchange = function (e) {
var data = window.decodeURIComponent(e.newURL.split(‘#’)[1]).split(‘/’);
console.log(data); // [“book”, “map”, “shelf”, “knife”]
}
*注意反向传递数据时应该使用 window.parent.location.hash
window.name
适用条件:宿主页面和 iframe 之间通信
window对象有个name属性,该属性有个特征:即在 window 的生命周期内,窗口载入的所有的页面 (iframe) 都是共享一个 window.name
的,每个页面对 window.name
都有读写的权限,window.name
是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
这样在 window 中编辑 window.name 就可以在 iframe 中得到,但这个过程缺乏监听,宿主页面(A.html)和 iframe 页面(B.html)相互并不知道对方在什么时候修改该值:
// A.html
setTimeout(() => {
window.parent.name = “what!”;
}, 2000);
// B.html
setTimeout(() => {
console.log(window.name); // what!
}, 2500);
postMessage
适用条件:postMessage 是 H5 提出的一个消息互通的机制,解决 iframe 不能消息互通的问题,也可以跨 window 通信,语法如下:
// 在 www.siteA.com 中发出消息
// @message{any} 要发送的数据(注意:老版本浏览器只支持字符串类型)
// @targetOrigin{string} 规定接收数据的域,只有其指定的域才能收到消息,如果为"*"则没用域的限制
// transfer{any} 与 message 一同发送并转移所有权
window.postMessage(message, targetOrigin, [transfer]);
// 在另一个页面接受参数
window.onmessage = console.log;
这里暂不谈论第三个参数,因为你可能一辈子也用不到它。而 targetOrigin 最好不要使用 “*”,除非你想让所有页面都收到你的消息。
一种你会用到的场景(iframe):
<!-- www.siteA.com/index.html --> <script> window.addEventListener('message', function(e){ console.log('Get message: "' + e.data.title + '" from ' + e.origin); // 'Get message: "Saying hello to siteA!" from http://www.siteB.com' }); </script> <iframe src="http://www.siteB.com"></iframe> <!-- www.siteB.com/index.html --> <script> function sendMessage(){ window.postMessage({title: 'Saying hello to siteA!'}, 'http://www.siteA.com'); } setTimeout(sendMessage, 2000); </script>
这一种仅仅是没有了iframe,当你在同一个浏览器窗口同时打开 www.siteA.com
和 www.siteB.com
两个标签时也可以这样用
<!-- www.siteA.com/index.html --> <script> window.addEventListener('message', function(e){ console.log('Get message: "' + e.data.title + '" from ' + e.origin); // 'Get message: "Saying hello to siteA!" from http://www.siteB.com' }); </script> <!-- www.siteB.com/index.html --> <script> function sendMessage(){ window.postMessage({title: 'Saying hello to siteA!'}, 'http://www.siteA.com'); } setTimeout(sendMessage, 2000); </script>
反向代理服务器
页面需要访问一些跨域接口,由于代理的存在,在服务器看来请求是不跨域,所以使用各种请求。但需要注意 http 到 https 的兼容问题。
比如当我在一些在线平台开发网站后得到一个页面 www.site-A.com
, 而这个页面需要请求我自己的数据服务器data.site-B.com
上的数据, 这样同样会产生跨域问题,但是www.site-A.com
这个页面是挂在第三方服务器上的,解决这个问题可以采用代理服务器的方法:
var express = require(‘express’);
var request = require(‘request’);
var app = express();
app.use(‘/api’, function(req, res) {
var url = ‘http://data.site-B.com/api2’ + req.url;
req.pipe(request(url)).pipe(res);
});
app.use(‘/’, function(req, res) {
var url = ‘http://data.site-C.com’;
req.pipe(request(url)).pipe(res);
});
当然还需要同时配置一个 host:
127.0.0.1 local.www.site-B.com
然后访问 local.www.site-B.com 就 OK 了。
CORS
适用条件:CORS 需要服务端支持,且存在一定的兼容性问题(如今你已经可以不考虑,但必要时不要忘了这个’bug’)。其通过添加 http 头关键字实现跨域可访问,包括如下头内容:
www.siteA.com/api 返回相应需要具有如下 http 头字段
Access-Control-Allow-Origin: ‘http://www.siteB.com’ # 指定域可以请求,通配符’*'(必须)
Access-Control-Allow-Methods: ‘GET,PUT,POST,DELETE’ # 指定允许的跨域请求方式(必须)
Access-Control-Allow-Headers: ‘Content-Type’ # 请求中必须包含的 http 头字段
Access-Control-Allow-Credentials: true # 配合请求中的 withCredentials 头进行请求验证
通过 express 实现也很简单,在注册路由之前添加:
var cors = require(‘cors’); // 通过 npm 安装
app.use(cors());
当然你也可以自定义一个中间件:
// 自定义中间件
var cors = function (req, res, next) {
// 自定义设置跨域需要的响应头。
res.header(‘Access-Control-Allow-Origin’, ‘http://www.siteB.com’);
res.header(‘Access-Control-Allow-Methods’, ‘GET,PUT,POST,DELETE’);
next();
};
app.use(cors); // 运用跨域的中间件
WebSocket 协议跨域
ws 协议是 H5 中的 web 全双工通信解决方案,常规 http 属于请求相应的过程,在客户端没有请求的情况下,服务端无法给客户端主动推送数据,ws 协议解决了这个问题,但处于安全考虑,其同样有同源策略的限制。
*这里不讨论通过长连接和服务端挂起请求等方法推送数据,本文只讨论跨域。
下面举个例子(依赖socket.io.js):
// 前端部分
socket.on(‘connect’, function() {
// 监听服务端消息
socket.on(‘message’, function(msg) {
console.log('data from server: ’ + msg);
});
// 监听服务端关闭
socket.on(‘disconnect’, function() {
console.log(‘Server socket has closed.’);
});
});
document.getElementById(‘input’).onkeyup = function(e) {
if(!e.shiftKey && !e.ctrlKey && !e.altKey && e.keyCode === 13)
socket.send(this.value);
};
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
最后
一个好的心态和一个坚持的心很重要,很多冲着高薪的人想学习前端,但是能学到最后的没有几个,遇到困难就放弃了,这种人到处都是,就是因为有的东西难,所以他的回报才很大,我们评判一个前端开发者是什么水平,就是他解决问题的能力有多强。
分享一些简单的前端面试题以及学习路线给大家,狂戳这里即可免费领取
片转存中…(img-Fb6bmEbV-1713693116059)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
[外链图片转存中…(img-P95bISlP-1713693116059)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
[外链图片转存中…(img-vLCnK9GJ-1713693116059)]
最后
一个好的心态和一个坚持的心很重要,很多冲着高薪的人想学习前端,但是能学到最后的没有几个,遇到困难就放弃了,这种人到处都是,就是因为有的东西难,所以他的回报才很大,我们评判一个前端开发者是什么水平,就是他解决问题的能力有多强。
分享一些简单的前端面试题以及学习路线给大家,狂戳这里即可免费领取
[外链图片转存中…(img-MQkPw1LW-1713693116060)]