1

为什么要跨域


我们都知道在浏览器地址栏输入地址的时候可以随便访问一个页面,但是如果你在ajax请求中发出一个xhr请求那么因为浏览器安全策略只有同源的服务器才能处理。这就是同源策略 要求协议/域名/端口三者完全一致才能访问


  • 例如以下的代码例子,我们需要获取服务器上README.md里面的数据

    {
      status:'sucess',
      data:{
        city:['南京','北京']
      }
    }
  • 本地js代码如下

  <script type="text/javascript">
        let xhr = new XMLHttpRequest()
        const url = 'http://localhost:8888/detail'
        const METHOD = 'GET'
        xhr.open(METHOD, url)
        xhr.setRequestHeader('Content-Type', 'text/html')
        xhr.send()
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                console.log('xhr text:', xhr.responseText)
            }
        }
    </script>
  • 如果地址栏是下面这样 图片描述

那么我们是可以访问到这个数据文件的


  • 如果我在另外一个不同源域名下执行同样的代码例如下面这个地址图片描述

那么就会出现下面的错误图片描述

这个错误的意思说白了就是服务器禁止不同源的页面进行ajax请求数据。


非要获取这个数据我们应该怎么做

我们注意到有的网站会提供cdn服务我们可以从不同的服务器添加下面的代码
<script src="http://www.baidu.com"></script>
这就为我们跨域获取数据提供了可能

node服务器端代码如下

var path = require('path')
var express = require('express')
const app = express()
app.use(express.static(path.join(__dirname, 'jsonp')))
console.log('> Starting dev server...')
app.get('/detail', (req, res) => {
    let cb = req.query.cb ? req.query.cb : null
        // console.log('succuess')
    let readStream = fs.createReadStream(`README.md`)
    let data = ''
    readStream.on('data', chunk => {
        data += chunk
    })
    readStream.on('end', () => {
        // console.log(data)
        res.send(`${cb}(${data})`)
    })
})
var server = app.listen(8888)
console.log('server is running at port 8888')

1.基本原理我们可以动态生成script标签,把请求的url添加到script标签的src属性上。把数据包裹在一个客户端声明的本地的回调函数中,这样可以动态加载一个跨域服务器数据。


  function jsonp(obj) {
            let scriptDOM = document.createElement('script')
            scriptDOM.setAttribute('src', `${obj.url}?cb=${obj.cb}`)
            window['callback'] = obj.success
            document.querySelector('body').appendChild(scriptDOM)
            scriptDOM.onload = function() {
                window['callback'] = null
                this.parentNode.removeChild(this)

            }
            scriptDOM.onerror = e => {
                console.log('jsonp error!', e)
            }




        }
        jsonp({
            url: 'http://localhost:8888/detail',
            cb: 'callback',
            success: data => {
                if (data) {
                    console.log('jsonp data', data)
                }
            }
        })

2.在本地声明这个动态script中的回调函数名称,并且定义该函数,然后通过url参数传递到服务器。服务器把数据包裹成这个函数的参数传递回来,跨域就实现了。

为什么要这么做?以前我也很困惑。你获取到的数据有可能是个json也有可能是个纯文本,而script标签内的内容只能是合法的js语句,如果你没有用回调函数包裹,直接从服务器传递回来原生数据。有可能是一段恶意代码,或者是非法的js字符,那么浏览器就会报错或者产生不可预料的显示结果。并且你事先不知道浏览器传递过来的代码跟你写的有没有冲突,是不是完全耦合。只有在本地定义一个函数传递到服务器,这样服务器用该函数包裹代码后传递回来才能跟我们的本地js代码紧密配合。这样服务器传递回来执行的代码就是我们自己定义的函数(数据存在函数的参数里),可以很好跟我们自己的代码配合。这样颇有Vue Angular子组件向父组件传递数据的思想


这样不同域名下的数据我们拿到了结果如下
图片描述


Ajaxyz
356 声望23 粉丝

Code is very delicious!