AJAX 入门

90 阅读3分钟

一、AJAX

1.1 特点

1.1.1 优点

  • 可以无需刷新页面与服务器端进行通信

  • 允许你根据用户时间来更新部分页面内容

1.1.2 缺点

  • 没有浏览历史,不能回退

  • 存在跨域问题(同源)

  • SEO 不友好

1.2 HTTP 超文本传输协议

1.2.1 请求报文

  • 请求方式(POST) / URL / 版本(HTTP/1.1)

  • Host: atguigu.com

    Cookie: name=zehui

    Content-type: application/x-www-form-urlencoded

    User-Agent: chrome 83

  • 空行

  • 请求体

1.2.2 响应报文

  • HTTP/1.1 200 OK

  • Content-type: text/html;charset=utf-8

    Content-length: 2048

    Content-encoding: gzip

  • 空行

  • 响应体

1.3 express的使用

npm init --yes​npm i express

// 引入expressconst { response } = require('express')const express = require('express')    // 创建应用对象const app = express()    // 创建路由规划app.get('/', (request, response) => {        response.send('hello express')    })    // 监听端口启动服务app.listen(8000, () => {    console.log('服务已启动,8000端口监听中...');})

127.0.0.1:8000查看请求

1.4 ajax 的基本操作

1.4.1 GET

const btn = document.querySelector('button')const result_box = document.querySelector('.result__box')btn.onclick = () => {    // 创建对象    const xhr = new XMLHttpRequest()    // 初始化 设置请求方法和url    xhr.open('GET', 'http://127.0.0.1:8000/server')    // 发送    xhr.send()    // 事件绑定 处理服务端返回的结果    // readyState 是xhr对象的属性 状态值: 0 1 2 3 4    xhr.onreadystatechange = () => {        if (xhr.readyState === 4) { // 服务端返回了所有结果            // 判断状态码            if (xhr.status >= 200 && xhr.status < 300) {                result_box.innerHTML = xhr.response            } else {                console.log('error');            }        }    }​}

app.get('/server', (request, response) => {    // 允许跨域    response.setHeader('Access-Control-Allow-Origin', '*')    response.send('hello ajax')})

1.4.2 POST

const result_box = document.querySelector('.result__box')result_box.addEventListener('mouseover', function() {    const xhr = new XMLHttpRequest()    xhr.open('POST', 'http://127.0.0.1:8000/server')    // 设置请求头信息    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')    xhr.setRequestHeader('name', 'szh')    // 设置post方式的请求体    xhr.send('a=1&b=2')    xhr.onreadystatechange = () => {        if (xhr.readyState === 4) {            if (xhr.status >= 200 && xhr.status < 300) {                result_box.innerHTML = xhr.response            }        }    }})

const express = require('express')const app = express()app.post('/server', (req, res) => {    // 设置响应头    res.setHeader('Access-Control-Allow-Origin', '*')    res.setHeader('Access-Control-Allow-Header', '*')    res.send('it is post')})app.listen(8000, () => {    console.log('123');})

1.4.3 服务端响应JSON数据

const box = document.querySelector('.box')window.addEventListener('keypress', () => {    const xhr = new XMLHttpRequest()    xhr.responseType = 'json' // 设置响应体类型    xhr.open('POST', 'http://127.0.0.1:8000/json-server')    xhr.send()    xhr.onreadystatechange = () => {        if (xhr.readyState === 4) {            if (xhr.status >= 200 && xhr.status < 300) {                box.innerHTML += xhr.response.name            }        }    }})

app.all('/json-server', (req, res) => {    res.setHeader('Access-Control-Allow-Origin', '*')    res.setHeader('Access-Control-Allow-Header', '*')    const obj = {        name: 'szh',        age: '22'    }    res.send(obj)})

1.5 nodemon 自动重启工具安装

www.npmjs.com/package/nod…

1.6 IE浏览器缓存问题

解决方法:在请求后面加上一个时间戳

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

1.7 超时请求与异常

客户端发送请求

window.addEventListener('keypress', () => {
    const xhr = new XMLHttpRequest()

    /***********************************************/
    xhr.timeout = 2000 // 超时设置
    xhr.ontimeout = () => {
        // 超时回调
        console.log('请求超时或失败,重新发送请求...')
    }
    xhr.onerror = () => {
        // 网络异常回调
        console.log('网络异常,请检查网络后重新发送请求!')
    }

    xhr.open('POST', 'http://127.0.0.1:8000/timeout')
    xhr.send()
    xhr.onreadystatechange = () => {
        if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
                result_box.innerHTML = xhr.response
            }
        }
    }
})

服务端设置延时模拟超时、浏览器设置offline模拟断网状态

// 请求超时与异常
// 延时响应
app.post('/timeout', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*')
    setTimeout(() => {
        res.send('请求时间为3s')
    }, 3000)
})

1.8 取消请求(手动)

 xhr.abort() //取消发送

1.9 请求发送重复问题

思路:添加一个全局变量进行判断发送的状态,false时才进行发送。

const submit = document.querySelector('.submit')
let xhr = null
let isSending = null // 判断是否在发送请求
submit.addEventListener('click', () => {
    if (!isSending) {
        xhr = new XMLHttpRequest()
        isSending = true
        xhr.open('GET', 'http://127.0.0.1:8000/repeat')
        xhr.send()
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                isSending = false
            }
        }
    } else {
        console.log('请求正在发送中...');
    }
})

二、JQuery中的Ajax

.get() ​.post() .ajax()

btn[0].onclick = function() {
    /*
         ** 三个参数 url 携带参数 回调函数(响应体)
         */
    let jqxhr = $.get('http://127.0.0.1:7000/getAjax', {
        a: 1,
        b: 2
    }, function(res) {
        console.log(res);
    }).done(() => {
        console.log('success')
    }).always(() => {
        console.log('complete')
    })
    }
btn[1].onclick = function() {
    let jqxhr = $.post('http://127.0.0.1:7000/postAjax', {
        a: 1,
        b: 2
    }, function(res) {
        console.log(res);
    }).done(() => {
        console.log('success')
    }).always(() => {
        console.log('complete')
    })
    }
btn[2].onclick = function() {
    $.ajax({
        type: 'POST',
        url: 'http://127.0.0.1:7000/postAjax',
        data: {
            k: 1,
        },
        dataType: 'json',
        success: data => {
            console.log(data)
        },
        headers: {
            t: 123
        },
    })
}


/*
 ** JQuery-Ajax
 */
app.get('/getAjax', (req, res) => {
    res.setHeaders('Access-Control-Allow-Origin', '*')
    res.send('jquery-get')
})
app.all('/postAjax', (req, res) => {
    res.setHeaders('Access-Control-Allow-Origin', '*')
    res.setHeaders('Access-Control-Allow-Headerss', '*')
    const obj = {
        name: 'szh'
    }
    res.send(JSON.stringify(obj))
})

fetch()

fetch 规范与 jQuery.ajax() 主要有以下的不同:

  • 当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject,即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve(如果响应的 HTTP 状态码不在 200 - 299 的范围内,则设置 resolve 返回值的 ok 属性为 false),仅当网络故障时或请求被阻止时,才会标记为 reject。

  • fetch 不会发送跨域 cookie,除非你使用了

    credentials

    初始化选项

基本语法

// fetch请求
async function fetchData(url = '', data = {}) {
    const response = await fetch(url, {
        method: 'POST',
        mode: "cors",
        cache: "no-cache",
        credentials: "same-origin",
        headers: {
            'Content-Type': 'application/json',
            szh: 123
        },
        redirect: "follow",
        referrerPolicy: "no-referrer",
        body: JSON.stringify(data)
    })
    return response.json()
}
window.addEventListener('keypress', () => {
    fetchData('http://127.0.0.1:7000/fetch', {
        name: 'szh'
    }).then(data => {
        console.log(data.ok)
        console.log(data)
    }).catch(e => {
        console.log(e)
    })
})


// fetch
app.all('/fetch', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*')
    res.setHeader('Access-Control-Allow-Headers', '*')
    const obj = {
        name: 'szh'
    }
    res.send(JSON.stringify(obj))
})

三、跨域问题

3.1 同源策略

同源策略是浏览器的一种安全策略,所谓的同源指:协议域名端口号 必须完全相同

违背同源策略即为跨域

3.2 解决跨域问题

3.2.1 JSONP

  1. 是什么?

    JSONP (JSON with Padding),是一个非官方的跨域解决方案, 只支持get请求

  2. 怎么工作的?

    网页有一些标签具有跨域能力,比如:img、link、iframe、script

    JSONP 就是利用script标签的跨域能力来发送请求的。

  3. 如何使用?

    function handle(obj) {
        console.log(obj);
        if (obj.exit === 1) {
            $('input')[0].style.color = 'red'
        }
    }
    
    $('input').blur(() => {
        let uname = $(this).val
        // 1-动态创建script标签
        const script = document.createElement('script')
        // 2-设置src
        script.src = 'http://127.0.0.1:7000/jsonp'
        // 3-添加节点
        document.body.appendChild(script)
    })
    
    
    // 跨域问题
    app.all('/jsonp', (req, res) => {
        const data = {
            exit: 1,
            msg: '账号已存在'
        }
        let obj = JSON.stringify(data)
        res.end(`handle(${obj})`)
    })
    

3.2.2 CORS

  1. 是什么?

    CORS (Cross-Origin Resource Sharing),跨域资源共享。CORS是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持get和 post 请求。跨域资源共享标准新增了一组HTTP首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源

    developer.mozilla.org/zh-CN/docs/…

  2. 怎么工作的?

    CORS是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。

  3. 如何使用?

    res.setHeader('Access-Control-Allow-Headers', '*')
    // 第二个参数是允许跨域的请求