再谈原生js跨域解决方案 - JSONP

576 阅读2分钟

JSONP的由来

jsonp全称json with padding,填充式的json,jsonp是为跨域而生的,在早期的前端开发中,经常用该方法解决跨域问题

JSONP如何解决跨域

Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<\script>、<\img>、<\iframe>)

例如,我们在本地可以引用不同域名的在线的js文件,图片地址,都可以正常加载,没有跨域问题。

//引用vue
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.0/vue.js"></script>
<img src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/e08da34488b114bd4c665ba2fa520a31.svg" />

jsonp就是用了这类标加载资源不会发生跨域的问题,模拟发送一个请求到后端,具体流程是这样的:

  • 客户端动态的创建一个script标签,将src设置为服务地址
  • 接着在src后面拼接上一个本地的回调函数的名字,这个函数接收一个参数,就是我们要请求的数据
  • 后端解析函数,调用函数,并将我们要的数据当做参数传递进去
  • 请求结束后将script标签删掉

代码实现

来实现一下,这里使用node搭建了一个简单的服务, 执行node server.js命令,即可运行该服务,监听3001端口

server.js

const http = require('http')
const url = require('url')
http.createServer((req,res) => {
    //解析客户端传递的回调函数
    let clientCallBack = url.parse(req.url, true).query.clientCallBack
    let message = JSON.stringify({
        "city": "上海",
        "street": "武康路",
        "code": "101"
    })
    res.writeHead(200, {"Content-Type":"application/json;charset=utf-8"})
    //执行函数,并将参数传递进去
    res.write(`${clientCallBack}(${message})`);
    res.end();
}).listen(3001)

客户端代码,就是一个基本的html页面 client.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp</title>
</head>
<body>

<div id="message">
    <h1>来自服务端的数据</h1>
</div>
<script >
    //要传递给后端的回调函数,接收服务的返回数据,显示到页面上
    function dataCallback(message) {
        let msgDom = document.getElementById('message')
        let frag = document.createDocumentFragment()
        for(let k in message) {
            let oDiv = document.createElement('div')
            oDiv.innerHTML += `
                <span>${k}</span> : <span>${message[k]}</span>
            `
            frag.append(oDiv)
        }
        msgDom.append(frag)
    }

    //使用jsopn,模拟请求
   function requestData(cb) {
       //这里将函数名拼接到服务地址后面,传递给后端,函数默认的name属性就是函数名
       let url = `http://localhost:3001/?clientCallBack=${cb.name}` 
       //动态创建script标签
       let scriptTag = document.createElement('script')
       scriptTag.setAttribute('src', url)
       document.getElementsByTagName('head')[0].appendChild(scriptTag)
       //请求完成后,删除script标签
       scriptTag.onload = function (e) {
           e.currentTarget.remove()
       }
       scriptTag.onerror = function (e) {
           e.currentTarget.remove()
       }
   }

    requestData(dataCallback)
</script>
</body>
</html>

看下效果,很轻松拿到数据

image.png

image.png

要注意的地方

JSONP仅支持get请求,不支持POST,因为src默认是get请求
<img src='' />
<script src = ''> </script>
调用时也可以传参,后端解析即可
  let url = `http://localhost:3001/?clientCallBack=${cb.name}&city=上海&code=021`