首先我们要知道跨域是什么,又为什么要有跨域操作
跨域是什么
跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。
好这里我们提炼出两个东西---1.同源策略:url首部的协议+域名+端口号必须一样,一者不同即为跨域
2.安全限制(这也是为什么需要跨域操作下一段介绍)
为什么需要跨域操作/为啥需要同源策略/为什么要限制ajax跨域(面试可能会这样问)
这个和cookie的存储原理有关(这里不介绍cookie),在生活里我们登录一个网站P(photoshop学习网站),然后他会存个通行证,下一次我们再上p站的时候
它是不是就自动登录了,我们用别的网站去请求登录p是不是就登录不上。
然后这个例子我们把它专业化一下下
1.客户向p网站的服务器发送登录请求,携带账号密码数据
2.P网站的服务器校验账号密码正确后,返回响应并给本地添加了Cookie
3.之后客户再次向P网站发起请求会自动带上P网站存储在本地的cookie
4.P网站的服务器从cookie中获取账号密码数据后,返回登陆成功界面。
假如ajax请求可以跨域,那我是不是可以在这个博客里写一段js,使用ajax向你的学习网站发起登录请求,因为
很多人的电脑上会存有学习网站的cookie不需要输入账号密码直接就自动登录了,再ajax回调函数中解析了返回的数据,
我就能知道你学的是不是potoshop了。
所以才需要同源策略,为了用户的信息安全也必须限制ajax的跨域操作
ps:同源策略限制内容:(1)存储内容:cookie,localStorage,sessionStorage,(2)DOM节点
(3)ajax请求,需注意ajax请求会发送出去,也会被返回,但会被浏览器拦截。
不被限制的有img link script
进入正题jsonp
我们现在已经知道了能够不被同源限制的标签有img link script 那我们该选择哪个嘞?
当然是script,只有这兄弟和代码有关嘛
首先咱得整个服务器出来用 node模拟一下
//服务端 const http =require("http");//导入http模块 http.createServer( (req,res) => { let weather="四川 梅雨天";//客户端要访问的数据 res.writeHead(200,{ "Content-Type":"text/plain;charset=utf-8" });//防止乱码,用jq的Ajax就不需要,Ajax会自动识别 res.write(weather); res.end(); }) .listen(3000);//3000端口
然后在客户端写<script src="http://localhost:3000",会得到一个类型错误,因为js没法解析四川 梅雨天呀,这玩意儿都不是个语句,它笨解析不了
也就是说我们要在res.write拼接一条语句传给客户端于是乎
//服务端 const http =require("http"); http.createServer( (req,res) => { let weather="四川 梅雨天"; res.writeHead(200,{ "Content-Type":"text/plain;charset=utf-8" }); res.write(`("document.write(${weather}")`); res.end(); }) .listen(3000);
然后再在客户端写<script src="http://localhost:3000",就发现页面上能打印出四川 梅雨天了,可是有个问题,我们的操作是不是写死了?那咱就在
客户端整个操作有关的函数呗
function show(w) { alert(w); } </script>
然后把服务器的res.write(`("document.write(${weather}")`);改成res.write(`("show(${weather}")`);嗯~ o(* ̄▽ ̄*)o可是又有问题了我要是想改函数是不是要改两个地方
可不可以把这个函数整成动态的?可以的我们把函数的信息放到url的query里
<script src="http://localhost:3000?callback=show"> /*这里用callback接受函数信息,又因为本来show就是回调函数(相信大家体会出来了)所以用的callback命名,爱用啥都可以*/
那么在服务器咋个取得callbck嘞
onst http =require("http"); const url=require("url");//引入url模块 http.createServer( (req,res) => { var Url=url.parse(req.url,true); var callback=Url.query.callback;//大概意思从url取到callback let weather="四川 梅雨天"; res.writeHead(200,{ "Content-Type":"text/plain;charset=utf-8" }); res.write(`${callback}("${weather}")`); res.end(); }) .listen(3000);
然后这个问题也解决了,但我们平时点一个按钮然后做跨域请求肯定不是这样把script标签写在外面浪费内存不说,代码还不美观
所以终极的代码来了
//客户端代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <button>天气预报</button> <script> let click=document.querySelector("button"); click.addEventListener("click",()=>{ let script=document.createElement("script"); script.src=`http://localhost:3000/?callback=show`; document.body.appendChild(script) });//对按钮添加点击事件在body里生产script function show(w) { alert(w); document.body.lastChild.remove();//对每次生成的script在结束后移除 } </script> </body> </html> //服务器代码不变
好了以上就是jsonp的原理总结一下,(1)声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,
函数形参为要获取目标数据(服务器返回的data)。
(2)创建一个<script>
标签,把那个跨域的API数据接口地址,赋值给script的src,
还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。
(3)服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,
(4)最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),
对返回的数据进行操作
所以jsonp是一种思想每个人写的jsonp可能都会不一样,但终终极方案JQubiery里ajax请求里写 dataType : "jsonp",完事儿了,但思想还是要理解的。