AJAX&跨域

286 阅读4分钟

AJAX&跨域

AJAX

AJAX是什么?

AJAX 全称:Async Javascript And XML(异步JavaScript和XML)。是一种创建交互式网页应用(动态网页)的开发技术(实际上就是发请求和接收响应),使用这种技术后,我们可以不需要重新加载整个网页就能更新网页中的部分内容。

AJAX这技术开创时,主要是为了对后台的XML数据进行请求,所以其全称中的“X”指的是XML,而且这个技术中也有专门给XML格式内容使用的API。但是AJAX发展不到2年,JSON就取代了XML的地位,因为JSON体积更小,更容易编码,解析也更快。所以如今的开发中虽然我们使用的是AJAX但是,我们与后台互动的更多是JSON数据。

AJAX的核心JS代码,MDN

// 1.创建实例
let request = new XMLHttpRequest // 这里的()可以省略
// 2.初始化请求
request.open(method,url,async)
// 3.监听交互进行的阶段,并在成功获取数据后进行回调
request.onreadystatechange = ()=>{
    if(request.readyState===4&&request.status===200){
        fn()
    }
}
// 4.发送上面设置好的HTTP请求
request.send()

一些解释:

  1. open()中,第三个参数是一个布尔值,默认为true,用于决定该实例是否需要使用异步操作。在生产环境下,永远不要使用同步的AJAX,否则在加载数据时,整个页面都会卡主。
  2. readyState:0~4个代号分别代表不同的阶段:open前(0) open(1) send(2) 第一个信息下载(3) 所有信息下载完成(4)。
  3. status状态码:实际上 200~299 都是表示请求成功的,所以我们一般监听这个范围,而不是仅仅监听200,这里只是一个示范。

主流交互对象JSON

JSON是一门语言,是一门标记语言,有自己的语法,但是非常简单。JSON是一门吸取了JS语言精粹的而创造的一门优秀的数据交换语言,用于表示数据。

JavaScript中有2个与JSON相关的API:

  • JSON.stringify() :将一个 JavaScript 值(对象或者数组)转换为一个 JSON 字符串。
  • JSON.parse():用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。

跨域

跨域及其意义

跨域是基于浏览器同源策略下的一个现象,简单来说就是浏览器不允许JS获取非同源(Origin)的数据,只能获取同源的数据(实际上数据文件是获取了,但是浏览器并不允许使用这些数据)。

同源的定义:

  1. window.origin&location.origin可以得到当前的源
  2. 源=协议+域名+端口号,这3个必须要完全一致。如:http://qq.comhttp://www.qq.com这两个源是不同的源。

同源策略的目的:安全

  • 在同源策略下,只有在一定设定下才能获取跨域的资源。防止恶意网站进行恶意的请求,获取用户的隐私。

为什么在跨域的情况下我们依旧能请求JS、CSS、图片等资源?

  • 可以这样理解,跨域时我们可以引用请求到的数据但是并不知道这些数据的实际代码。JS、CSS、img这些内容,我们请求到之后,通过对应的解析手段就能引用。而一些数据,因为我们并不知道实际的代码,所以无法进行合适的解析。

如何解决跨域请求数据的问题?

两种方法:1.主流,CORS。 2.兼容IE,JSONP。

CORS

CORS:Cross-Origin-Resource-Sharing,跨域资源共享。

CORS的原理很简单,就是提前在服务端声明可以访问该数据的源。而实现这个声明的手段就是在响应头中加上允许访问的源。

// node.js 服务端文件
response.setHeader('Access-Control-Allow-Origin', '*') 
// * 代表允许任何源访问,也可以设置成具体的url

根据请求条件的不同,有简单请求和复杂请求,区别在于简单请求不会触发CORS预检请求,具体内容,看MDN。

JSONP

  • 理解JSONP的一些TIPS:

    • 当涉及到请求时其实我们的所有的请求都是和服务端对接的,无论是我们传递的参数,还是接收的响应,都是在服务端进行处理的。
    • 实际上我们接收到的响应基本上都是字符串,而至于要怎么解析这个响应,则由响应头中的Content-Type决定。
  • JSONP实际上和JSON没有任何关系,它的原理就是利用 script 标签能够引用不同域下的数据 。

  • 最简单的JSONP:

    • 我们将需要获取的数据存储在一个全局变量中,那么当我们引用这个JS文件时,我们自然就能获取到这个全局变量。正如:jQuery的 $ 。
  • 最常用的JSONP过程:

    1. 请求端的JS文件下,创建script节点。
    2. 设置我们对请求的数据处理的回调函数,这个函数名一般是随机的,并暴露在全局作用域下。
    3. 设置script标签的url并通过 ?callback=fn 传递我们的回调函数。这个callback是开发者的约定俗成,不一定是callback但最好是。
    4. 插入script标签,开始请求。
    5. 服务端收到我们的请求后,进行处理。
    6. 最终返回一个 回调函数(数据)形式的响应(包裹了所需参数的回调函数)。
    7. 当我们接收到响应后,便直接调用这个回调函数。
    // 服务端的JSONP响应处理示例
    if (path === '/friends.js')){
        response.statusCode = 200
    	response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
    	const data = (fs.readFileSync('public/friends.json')).toString()
    	console.log(data);
    	const string = `window['{{fnName}}']({{trueFn}})`   		         		
      response.write(string.replace('{{fnName}}',query.callback).replace('{{trueFn}}', data))
    	response.end()	
    }
    
    1. 当节点加载完毕后,删除节点,节省内存,因为请求回来的JS执行完毕后就没有存在的必要了。
    script.onload = ()=>{
        script.remove()
    }	
    
  • 利用promise封装JSONP

    const jsonp = (url) => {
        return new Promise((resolve, reject) => {
            let random = 'Alongcallbackfunctionname' + Math.random()
            // 将这个回调函数暴露在全局作用一下
            window[random] = data => {
                resolve(data)
            }
            let friends = document.createElement('script')
            friends.src = `${url}?callback=${random}`
            // 成功调用后删除节点
            friends.onload = () => { friends.remove() }
            friends.onerror = () => { reject() }
            document.body.appendChild(friends)
        })
    }
    

CORS和JSONP的比较

  1. 对IE的兼容性,JSONP兼容IE而CORS不兼容
  2. 相对于AJAX,JSONP通过Promise封装后也只有2中状态:成功与失败。而AJAX中readyState则有5种状态,而且能获取更多的信息,如状态码等。