为什么会有跨域问题存在
因为浏览器的同源策略 (协议, 端口, 域名任何一个不同, 同源策略都会禁止跨域), 原来是浏览器再 giao 事情:
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互. 这是一个用于隔离潜在恶意文件的重要安全机制.
正确展开跨域的几种方式
同源策略是浏览器做的一件好事, 是用来防御来自邪门歪道的攻击, 但是不能吧我们自己人也挡在门外, 这时候就需要正确的打开方式:
JSONP
在 html 标签里, 一些标签比如 script,img 这样的获取资源的标签是没有跨域限制的, 利用这一点, 我们可以这样干. 通过前端方法作为参数传递到服务器, 服务器注入参数后再返还, 实现服务器向客户通信, 只支持 get 方法.
CORS
CORS 是一个 W3C 标准, 全称是 "跨域资源共享"(Cross-origin resource sharing) 跨域资源共享 CORS 详解. 看名字就知道这是处理跨域问题的标准做法. CORS 有两种请求, 简单请求和非简单请求. 这里不再赘述, 请参考阮一峰老师的文章.
代理
如果我们请求的时候还是用前端的域名, 然后有个东西帮我们把这个请求转发到真正的后端域名上, 不就避免跨域了吗? 这时候, Nginx 出场了.
Nginx 配置:
- server{
- # 监听 9099 端口
- listen 9099;
- # 域名是 localhost
- server_name localhost;
- # 凡是 localhost:9099/api 这个样子的, 都转发到真正的服务端地址 http://localhost:9871
- location ^~ /api {
- proxy_pass http://localhost:9871;
- }
- }
跨域 Dom 查询
参考: https://mp.weixin.qq.com/s/Ldh6rkcimZ1ppHPHK3KeUQ
JSONP 的工作流程
请求前: 创建一个 script 标签, 并给 src 赋值 url+callback 的方法名, 并在 window 上注册这个方法
发送请求: 将 script 添加到页面中
数据响应: 服务器将返回的数据作为参数和函数名拼接在一起 jsonpCbk({data:"data"}). 当浏览器接收到响应数据, 由于发起请求的是 script, 所以相当于直接调用 jsonpCbk 方法, 并且给回调传入了一个参数.
封装 jsonp
- /**
- * JSONP handler
- *
- * Options:
- * - param {String} qs parameter (`callback`)
- * - prefix {String} qs parameter (`__jp`)
- * - name {String} qs parameter (`prefix` + incr)
- * - timeout {Number} how long after a timeout error is emitted (`60000`)
- *
- * @param {String} url
- * @param {Object|Function} optional options / callback
- * @param {Function} optional callback
- */
- // url 要请求的地址以及拼接的参数; opts{param: 约定的函数参数, timeout: 超时时间, name: 指定的函数名, prefix: 指定的函数名前缀};fn 回调函数
- var count = 0
- function noop(){}
- function jsonp(url, opts, fn) {
- // opts 如果是一个函数 赋值给 fn 并重置 opts 为空对象
- if('function' == typeof opts){
- fn = opts
- opts = {}
- }
- // 如果没有 opts 给 opts 赋值空对象
- if(!opts) opts = {}
- var prefix = opts.prefix || '__jp'
- // id 为 opts 的 name 或者 prefix + 计数 (prefix 为 opts.prefix 或__jp)
- var id = opts.name || (prefix + (count++))
- var param = opts.param || 'callback'
- // 有 opts.timeout 就取 没有就是 60000ms
- var timeout = null != opts.timeout ? opts.timeout : 60000
- var enc = encodeURIComponent
- // 第一个 script 或者 head 标签
- var target = document.getElementsByTagName('script')[0] || document.head
- var script
- var timer
- // 如果有 timeout 就在 timeout 之后执行 clean 并抛出 Error
- if (timeout) {
- timer = setTimeout(function(){
- cleanup()
- if(fn) fn(new Error('Timeout'))
- }, timeout)
- }
- function cleanup() {
- // 移除创建的 script 回调函数置空 清除定时器
- if(script.parentNode) script.parentNode.removeChild(script)
- window[id] = noop
- if(timer) clearTimeout(timer)
- }
- function cancel() {
- if(window[id]) {
- cleanup()
- }
- }
- // 全局挂载 id
- window[id] = function(data) {
- cleanup()
- // 请求返回后执行回调 fn
- if(fn) fn(data)
- }
- url += (~url.indexOf('?') ? '&' : '?') + param + '=' + enc(id);
- url = url.replace('?&', '?');
- // 创建 script 标签 src 属性赋值 url
- script = document.createElement('script')
- script.src = url;
- // 插入到 head 或 script 的前面
- target.parentNode.insertBefore(script, target);
- return cancel;
- }
此方法在调用时需要自己拼接将参数拼接在 url 后面 opts 如果跟后台有约定 callback 参数名就传 {param: 约定参数名} , 没有默认 callback
来源: http://www.jianshu.com/p/ef1785f1bb97