Ajax学习 & CORS跨域问题

221 阅读5分钟

Ajax学习记录

Ajax的作用

在不刷新页面的情况下请求数据,刷新局部页面。

XML介绍

用来传输、存储数据 和HTML的区别: 功能不同,HTML用来呈现数据 细节:XML全都是自定义标签,没有预定的标签

JSON介绍

“取代了XML”——老师曰 XML的一些劣势:在JS中不好处理,同时复杂的标签也浪费了数据传输

但是JSON到底是什么呢?看看百度的简述

JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

JSON的本质就是个字符串

JSON和JS对象互相转换

JSON字符串转换为JS对象,使用 JSON.parse() 方法

var obj = JSON.parse('{"a": "Hello", "b": "World"}'); // {a: 'Hello', b: 'World'}

JS对象转换为JSON字符串,使用 JSON.stringify() 方法

var json = JSON.stringify({a: 'Hello', b: 'World'}); //'{"a": "Hello", "b": "World"}'

具体的使用方法 JSON详解

总之,这就是一个类似于Js对象的东西,用它来表示数据,用的时候当成一个对象,传输的时候当成一个字符串,简洁耐用。

Ajax特点

优点

  • 在不刷新的情况下与服务器端进行通信
  • 允许根据用户事件来更新局部页面

缺点

  • 没有浏览历史
  • 存在跨域问题
  • SEO不友好(搜索引擎优化)

Ajax请求基本操作

前置说明,以下服务端采用了 Express 框架。使用它之前需要先引入并创建对象。

//引入express
const express = require('express');

//创建应用对象
const app = express();
});

GET

其中有一个属性xhr.readState表示的该Ajax请求所处在的状态,其值为[0,4],通常我们使用 4

  • 0:请求未初始化,还没有调用 open()。
  • 1:请求已经建立,但是还没有发送,还没有调用 send()。
  • 2:请求已发送,正在处理中(通常现在可以从响应中获取内容头)。
  • 3:请求在处理中;通常响应中已有部分数据可用了,没有全部完成。
  • 4:响应已完成;您可以获取并使用服务器的响应了。

发送一个GET请求

btn.onclick = function(){
    const xhr = new XMLHttpRequest();
    xhr.open('GET', 'http://127.0.0.1:8000/server');
    xhr.send();
    xhr.onreadystatechange = function(){
        if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
                result.innerHTML = xhr.response
            }
        }
    }
}

对应的服务器端的响应

//引入express
const express = require('express');

//创建应用对象
const app = express();

//创建路由规则
app.get('/server',(request, response) => {
    // 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    // 允许自定义请求头
    response.setHeader('Access-Control-Allow-Headers','*');
    response.send('Hello Ajax!');
});

POST

前端代码

const result = document.getElementById('result')
result.addEventListener('mouseover', function(){
    //1. 创建对象
    const xhr = new XMLHttpRequest()
    //2. 初始化 设置类型与URL
    xhr.open('POST', 'http://127.0.0.1:8000/server')
    // 设置请求头
    xhr.setRequestHeader("Content-Type", 'application/x-www-from-urlencoded')
    //3. 发送
    xhr.send('a=100&b=200&c=300')
    // 4. 事件绑定
    xhr.onreadystatechange = () => {
        if (xhr.readyState ===4) {
            if (xhr.status >= 200 && xhr.status < 300) {
                // 处理服务端返回的结果
                result.innerHTML = xhr.response
            }
        }
    }
})

服务端代码

app.all('/server',(request, response) => {
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Headers','*');
    response.send('Hello Ajax!--post');
});

请求JSON数据

const result = document.getElementById('result')
window.onkeydown = () => {
    const xhr = new XMLHttpRequest()
    // 设置响应体数据的类型
    // 如果未设置,需要手动转换数据为json类型
    // 如果设置了,则无需转换
    xhr.responseType = 'json'
    //2. 初始化 设置类型与URL
    xhr.open('POST', 'http://127.0.0.1:8000/json-server')
    // 设置请求头
    xhr.setRequestHeader("Content-Type", 'application/x-www-from-urlencoded')
    //3. 发送
    xhr.send()
    // 4. 事件绑定
    xhr.onreadystatechange = () => {
        if (xhr.readyState ===4) {
            if (xhr.status >= 200 && xhr.status < 300) {
                // console.log(xhr.response)
                // result.innerHTML = xhr.response

                //let data = JSON.parse(xhr.response)
                result.innerHTML = xhr.response.name
            }
        }
    }
}

服务端代码

app.all('/json-server',(request, response) => {
    // 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Headers','*');
    const data = {
        name: 'zx'
    }
    let str = JSON.stringify(data)
    response.send(str);
});

超时与网络异常

// 超时设置
xhr.timeout = 2000
// 超时回调
xhr.ontimeout = () => {
    alert('network timeout!')
}
// 网络异常回调
xhr.onerror = () => {
    alert('network error!')
}

服务端代码

app.get('/delay',(request, response) => {
    response.setHeader('Access-Control-Allow-Origin','*');
    setTimeout(() => {
        response.send('delay biubiubiu');
    }, 3000);
});

取消请求

btn.onclick = () => {
    xhr.abort()
}

重复请求问题

目的:防止用户疯狂点击造成大量请求

方法:设置一个变量,判断当前是否有正在进行的请求

const btn = document.getElementsByTagName('button')[0]
const result = document.getElementById('result')
let x = null
// 标识变量
let isSending = false //是否正在发送Ajax请求


btn.onclick = () => {
    if (isSending) x.abort()
    x = new XMLHttpRequest()
    isSending = true
    x.open("GET", 'http://127.0.0.1:8000/delay')
    x.send()
    x.onreadystatechange = () => {
        if (x.readyState === 4) {
            isSending = false
        }
    }
}

同源策略

跨域是什么

何为同源? 何为跨域? Ajax只能在同源下发送请求

如何解决跨域

JSONP

JSON with Padding,是一个民间的跨域解决方案,只支持GET请求。它是利用HTML中存在一些天然具有跨域能力的标签,比如img、link、ifram、script

使用方法

<script>
    handle = (data) => {
        const result = document.getElementById('result')
        result.innerHTML = data.name

    }
</script>

<script src="http://127.0.0.1:8000/jsonp-server"></script>


// 服务端代码

app.all('/jsonp-server',(request, response) => {
    //response.send('console.log("hello jsonp-server")')
    const data = {
        name: 'zx'
    }
    // 将数据转化为字符串
    let str = JSON.stringify(data)
    response.end(`handle(${str})`)
});

这里有个小点就是,script标签带回来的东西一定要是JavaScript代码,否则会报错,所以我们通过以上代码的形式,用一层JS封装了数据。

来看看一个实践

当输入框失焦后,从服务端返回验证信息

<script>
    handle = (data) => {
        console.log(data)
        result.innerHTML = data.msg
    }

    const input = document.getElementById('username')
    const result = document.getElementById('result')
    input.onblur = () => {
        let username = this.value
        // 向服务端发送请求
        const script = document.createElement('script')
        script.src = 'http://127.0.0.1:8000/check-username'
        // 将 script 标签插入到文档中
        document.body.appendChild(script)
    }
</script>

//服务端代码
app.all('/check-username', (request, response) => {
    const data = {
        exist: 1,
        msg: '用户名不可用'
    }
    let str = JSON.stringify(data)
    response.end(`handle(${str})`)
})

CORS

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

CORS 是官方的跨域解决方案,特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理。

用起来其实很简单,只需要服务端加上三个响应头即可。

表示通配所有

app.get('/server',(request, response) => {
    response.setHeader('Access-Control-Allow-Origin','*')
    response.setHeader('Access-Control-Allow-Headers','*')
    response.setHeader('Access-Control-Allow-Method','*')
    response.send('Hello Ajax!--2');
});

以下是一些零碎的东西

HTTP介绍

两大内容 请求与响应

请求报文

  • 行 GET / URL / HTTP/1.1
  • 头 Host、Cookie、Content-type、User-Agent:
  • 空行

响应报文

  • 行 HTTP/1.1 200 OK
  • 头 Content-Type、Content-length、Content-encoding
  • 空行

Nodemon

小工具,当node.js服务文件修改时,自动重启服务 安装命令 npm install nodemon 启动服务 nodemon server.js 小坑:安装的时候一开始是用npm i nodemon,结果安装成功了却又用不了,显示没有这个东西,后来换成了npm install nodemon才能用。

简单查了一下,发现这俩东西还真有差别。详情请见 npm i 与 npm install 的区别

IE缓存问题

缓存了之前的Ajax请求结果,当服务端更新时,直接拿了本地的旧的结果

解决方法 URL中添加一个关于时间的参数,使得浏览器认为这是和过去不同的请求

            xhr.open('GET', 'http://127.0.0.1:8000/ie?t='+Date.now())

2022.2.20 学习总结:目前在看视频学习,进度条在18/30,预计明天就能够学完了。

参考资料

ajax方法XHR.readyState五种状态与示例