ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

AJAX基础学习

2021-11-20 16:05:08  阅读:221  来源: 互联网

标签:浏览器 请求 express 基础 学习 xhr AJAX 数据


Ajax学习记录

该文章仅作为笔者的学习笔记。

参考资料

尚硅谷

AJAX 教程 | 菜鸟教程 (runoob.com)

AJAX 简介 (w3school.com.cn)

AJAX – JavaScript 标准参考教程(alpha) (ruanyifeng.com)

什么是AJAX

AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容,也就是说页面无刷新获取数据。

普通的HTML,我们可以使用form表单来发送请求,但是呢?它会引起页面的跳转,会刷新页面。但是很多时候我们并不需要页面的跳转和刷新。

在日常生活中有很多这样的需求,比如说页面下拉获取新闻,这肯定是不需要刷新界面的。

AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

  • 不刷新页面更新网页
  • 在页面加载后从服务器请求数据
  • 在页面加载后从服务器接收数据
  • 在后台向服务器发送数据

AJAX的工作流程

image-20211117103918242

XML简介

XML 指可扩展标记语言(eXtensible Markup Language)。

XML 被设计用来传输和存储数据

XLM和HTML类似,不同的是HTML都是预定义的标签,而XML中没有预定以的标签,全部都是自定义标签,用来表示一些数据。

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>Don't forget me this weekend!</body>
</note>

可以看到,所以内容都必须被标签包括,而且都是自定义的标签。可以看出xml的体积很大。

现在基本已经被JSON取代了。

{
    "note":[
        "to":"Tove",
        "from":"jani",
        "heading":"Reminder",
        "body":"Don't forget me this weekend!"
    ]
}

用JSON表示结构清晰,数据量也不大,解析也很方便JSON.paser()JSON.stringify()

AJAX的特点

优点

  1. 可以无需刷新页面与服务器进行数据交互
  2. 允许根据用户事件来更新部分界面

缺点

  1. 没有浏览历史,不能回退

    如果使用表单,那么它会跳转到另外一个页面,那么这样就可以形成历史记录,但是对于AJXA来说,它是无刷新获取数据的,那么就没有形成历史记录。

  2. 存在跨域问题

  3. SEO不友好

JavaScript原生AJAX的使用

使用的对象

XMLHttpRequestAJAX的所有操作都是通过该对象进行的。

使用步骤

搭建一个简单的本地服务器

使用express + Nodejs 搭建

const express = require('express');

const app = express();
// 暴露静态资源
// 若想要与服务器交互,不能在使用本地打开或者liveserver打开了。他们使用的协议或者端口是不一样的,会产生跨域问题
app.use(express.static(__dirname + '/src'))

app.get('/test_get',(request,response)=>{
    console.log("有请求接入");
    response.send("Hello 啊");
})

app.listen(80 ,(err)=>{
    if(!err){
        console.log('测试ajax的服务器开启成功');
        console.log('测试地址是:http://localhost/1.ajax小试牛刀.html');
    }
});

初体验

html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ajax初体验</title>
    <style>
        #content {
            margin-top: 10px;
            width: 300px;
            height: 100px;
            border: 1px solid red;
        }
    </style>
</head>
<body>
    <h1>该页面是测试ajax的基础使用</h1>
   
    <button id="btn">点我发送(原生js-ajax-get)</button>
    <div id="content">

    </div>
    <script>
        const btn = document.querySelector("#btn");
        const content = document.querySelector("#content");
        btn.onclick = () =>{
            // 发送ajax请求
            //1. 创建xhr实例对象
            const xhr = new XMLHttpRequest();
            // xhr 内部有5种状态,值分别为 0 1 2 3 4
            // 4 就是数据完整回来
            // xhr实例对象,在实例完成的那一刻,状态为0 
            /* 
             5中状态值:0,1,2,3,4

             实例刚出来就是0
             在发送请求的时候,xhr中的状态一直带改变
                0:初始状态
                1:open已经调用了,但是send还没有调用,此时可以修改请求头内容
                2: send已经调用了,无法修改请求头了 
                3:已近回来一部分数据了, 如果是较小的数据会在此阶段一次性接受
                    ,较大的数据有待进一步接受,但是响应头一定会被接受。
                4: 所有数据响应回来
            
             
            */
            xhr.onreadystatechange = ()=>{
                // 函数体
                if(xhr.readyState === 3 ){
                    console.log("3时接受到的数据",xhr.response);
                    console.log("3时接受到的数据",xhr.getAllResponseHeaders);// 返回响应头的所有信息
                    console.log("3时接受到的数据",xhr.getResponseHeader);// 返回响应头中的一条信息,可以指定
                    console.log(xhr.getResponseHeader("content-length"));//获取响应体数据的信息
                }
                if(xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300){
                    console.log("数据回来了");
                    console.log(xhr.response);
                    content.innerHTML = xhr.response;
                }
            }
            //2. 指定发送请求的url 和 method
            xhr.open("GET","http://localhost/test_get");
            //3. 发送请求
            xhr.send();
        }
    </script>
</body>
</html>

创建XMLHttpRquest对象

在IE7+及其其他浏览器创建此对象

const xhr = new XMLHttpRequest();

在IE5和IE6中创建此对象

const xhr=new ActiveXObject("Microsoft.XMLHTTP");
var xmlhttp;
if (window.XMLHttpRequest)
{
    //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
    xmlhttp=new XMLHttpRequest();
}
else
{
    // IE6, IE5 浏览器执行代码
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}

AJAX向服务器发送请求

如果想要向服务器发送请求,那么就需要使用open() 和 send() 方法

例如

xhr.open("GET","http://localhost/test_get");
xhr.send();
open(method,url,async) and send(string)
方法描述
open(method,url,async)规定请求的类型、URL 以及是否异步处理请求。method: 请求的类型,GET或者POST或其他
url: 文件在服务器上的问题(也就是路径)
async:true(异步)、false(异步)
send(string)将请求发送到服务器。对于string来说它是当请求方法时POST的时候的请求体字符串。

image-20211117144501061

GET请求

对于GET的请求有两种参数:query 和 params

对于GET请求来说它是没有请求体的

querystring 是查询字符串:它的编码方式是 urlencoded 编码方式,也就是 key=value&key=value的方式。

使用express获取查询字符串对象的方式

requset.query

params也是get 的参数,它的格式是 /xx/xxx, 这很容易与路径相混淆,在express中它是这样处理的

app.get('/:id/:name',(req,res)=>{
    console.log(req.params.id);    			            console.log(req.params.name);
})

里面也可以使用正则表达式,或者- 和 .

'/:name-:dd' => /fujiaxu-dasd\
'/:name.dd' = > /fujiaxu.xxx
'/\^s\' => /s

一般代码

const btn = document.querySelector("#btn");
const content = document.querySelector("#content");
        btn.onclick = () =>{
            // 发送ajax请求
            //1. 创建xhr实例对象
            const xhr = new XMLHttpRequest();
            
            // 绑定监听
            // on 当。。。。。的时候
            // ready 准备
            // state 状态
            // change 改变
            // 当准备的状态发生改变
            // xhr 内部有5种状态,值分别为 0 1 2 3 4
            // 4 就是数据完整回来
            // xhr实例对象,在实例完成的那一刻,状态为0 
            xhr.onreadystatechange = ()=>{
                // 函数体
                if(xhr.readyState === 4){
                    console.log("数据回来了");
                    console.log(xhr.response);
                    content.innerHTML = xhr.response;
                }
            }
            //2. 指定发送请求的url 和 method
            xhr.open("GET","http://localhost/test_get");
            //3. 发送请求
            xhr.send();
        }

POST请求

POST的请求也有query、params,但是最常用的还是请求体。在AJAX中使用send(string)方法发送。

请求体最常用的类型是 urlencoded 和 JSON

对于发送urlendcoded类型的请求体时,请使用 setRequestHeader() 来添加 HTTP 头

xhr.setRequestHeader("Content-type","application/x-www-form-ur");
xhr.send("name=fujiaxu&&age=17");

对于发送JSON类型的请求体时,请使用 setRequestHeader() 来添加 HTTP 头

xhr.setRequestHeader("Content-type","application/json");
let obj = {
    "name":"fujiaxu",
    "age" : 18
}
xhr.send(JSON.stringify(obj));

对于使用express框架开启的服务需要加载一些中间件或者使用body-parser。要不然无法解析。

app.use(express.urlencoded({extended:true}));// 解析urlencoded的请求体
app.use(express.json());// 解析JSON类型的请求体。

AJAX响应

ajax获取和解析数据

获取响应体数据

xhr.response;
xhr.responseText	//获得字符串形式的响应数据。
xhr.responseXML	//获得 XML 形式的响应数据。

解析JSON

可以使用JSON.parse(xhr.response),但是如果返回的数据不是JSON,那么将会报错**。**

我们可以这样做xhr.responseType = “json”;必须是在返回数据前就设定好

这样有如果返回的数据不是json,那么将返回结果null,不会报错

readyState

直接看教程。

AJAX – onreadystatechange 事件 | 菜鸟教程 (runoob.com)

解决IE中get请求缓存问题

304状态码

(5条消息) HTTP 304状态码的详细讲解_胡杰的专栏-CSDN博客_304状态码

一文读懂http缓存(超详细) - 简书 (jianshu.com)

304状态码的意思是浏览器走的是协商缓存。但是IE浏览器如果看到请求的同一个东西,那么它将不会询问服务器是否是同一内容,直接走缓存,这么造成服务器修改内容无效。

解决方法:

首先ie不支持模板字符串和箭头函数。

既然ie是通过每次请求的url是否相同来判断请求内容是否相同,那么我们可以给它加一个时间戳的参数

"http://localhost/xxx?t="+Date.now()

这样就可以解决了。

POST请求是不会有这个问题的,这是当时设计的问题。ie认为既然GET所有的参数(query和params)都在url中,那么url没有变化就意味着内容没有变化。

AJAX请求异常和超时的处理

错误处理

xhr有一个处理错误的api

xhr.onerr = ()=>{
    
}

超时处理

这样说还等待返回结果,但是等待时间位置,我们也不可能无限等待,所以要设置一个最大的等待时间。

image-20211118140545506

xhr.timeout = 2000;// 2s  // 设置超时等待的时间
// 设置超时的回调函数
xhr.ontimeout = ()=>{
    alert("你的网网速实在是太垃圾了");
}

如果等了2s,数据还没有回来,那么就不再等待了,直接取消

image-20211118140513556

取消请求

必须要在数据全完返回之前取消请求,要不然无法起作用。

xhr.abort();// 终止请求

避免多次重复请求

如果多次请求,那么将会出现重复请求,消耗大量的资源,我们可以通过一些设置来取消最后一次之前的所有请求。使用flag来判断上次请求是否成功,若还没有成功,则取消上一次的请求,只需要本次请求的结果。

Jquery中的AJAX

[$.ajax(url,settings]) | jQuery API 3.2 中文文档 | jQuery API 在线手册 (cuishifeng.cn)

jQuery ajax - ajax() 方法 (w3school.com.cn)

回调地狱:回调函数里面套回调函数,使用promise技术可以解决。

同源策略(same origin policy)

浏览器的同源策略 - Web 安全 | MDN (mozilla.org)

浏览器同源政策及其规避方法 - 阮一峰的网络日志 (ruanyifeng.com)

跨域资源共享 CORS 详解 - 阮一峰的网络日志 (ruanyifeng.com)

同源策略规定了不同域

不能获取Cookie、LocalStorage 和 IndexDB 无法读取。

对于cookie来说可以使用 document.domain 来允许子域安全访问其父域时,您需要在父域和子域中设置 document.domain 为相同的值。这是必要的,即使这样做只是将父域设置回其原始值。不这样做可能会导致权限错误。

不能获取ajax返回的数据,可以发送请求,但是响应被拦截

不能获取DOM

解决ajax跨域问题

JSONP

这其实是一种规定跨域问题的方法,而没有真正的解决问题。不需要调用XMLHttpRquest对象来发送请求了。

只能用于get,因script标签只能使用get。

前端定义函数,后端调用函数。

利用的原理:

<script> <img> <link> <style> <form>等这些标签不会受到同源策略的限制,可以发送请求,并取得响应的结果。

它的基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

前端页面写法:

	<body>
	  <button id="btn">按钮</button>
	  <script type="text/javascript">
	    var btn = document.getElementById('btn');
	    btn.onclick = function () {
	      //1. 创建一个script标签
	      var script = document.createElement('script');
	      //2. 设置回调函数
	      window.getData = function (data) {
	        console.log(data);//拿到数据
	      }
	      //3. 设置script标签src属性,填写跨域请求的地址
	      script.src = 'http://localhost:3000/jsonp?callback=getData';// 在参数中给出回调函数的名字
	      //4. 将script标签添加到body中生效
	      document.body.appendChild(script);
	      //5.不影响整体DOM结构,删除script标签
	      document.body.removeChild(script);
	    }
	  </script>
	</body>

后端写法(express框架):

app.get('/jsonp', (req, res) => {
  //解构赋值获取请求参数
  const {callback} = req.query
  //去数据库查找对应数据
  const data = [{name: 'tom', age: 18}, {name: 'jerry', age: 20}];
  res.send(callback + '(' + JSON.stringify(data) + ')');
})

其原理就是在页面中先设置一个全局的函数来得到数据,在script标签中得到后端发来的数据(也就是JavaScript代码),自动执行这样就可以调用预先设置好的函数来会的内容了。

作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。这是为什么呢?因为本来发过来就是一堆字符串,一种猜测,浏览器解析的时候它就变成了js代码,那么JSON.stringify(data)的数据在里面自然就变成了对象了。

比如说

“fun({“a”:123})” 在js 代码里面就是 fun({“a”:123}) 这肯定就是一个对象啊,

所以我们再返回数据的时候应该想象到在js代码中,我们的数据会变成什么样子。

Websocket方式解决

以下文字来自于:浏览器同源政策及其规避方法 - 阮一峰的网络日志 (ruanyifeng.com)

WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。

下面是一个例子,浏览器发出的WebSocket请求的头信息(摘自维基百科)。

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

上面代码中,有一个字段是Origin,表示该请求的请求源(origin),即发自哪个域名。

正是因为有了Origin这个字段,所以WebSocket才没有实行同源政策。因为服务器可以根据这个字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应。

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

CORS

跨域资源共享 CORS 详解 - 阮一峰的网络日志 (ruanyifeng.com)

CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求,CORS允许任何类型的请求。

它的特点是:不需要要在客户端进行任何的操作,完全在服务器中进行。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

image-20211120155411366

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

如何解决的?

cors就是利用了响应头,设置响应头来告诉浏览器,改请求允许跨域,浏览器收到申请后对该响应放行。而不是对request拦截。

image-20211120131356553

出现跨域问题,可以看到返回的信息中,有一句话说的是没有Access-Control-Allow-Origin头。

在服务器端设置响应头(express框架下)

app.get("/test",(request,response)=>{
    repsonse.setHeader("Access-Control-Allow-Origin","具体网站的地址");
	repsonse.setHeader("Access-Control-Allow-		Origin","http://127.0.0.1:5500");// 协议+主机名+端口号
	repsonse.setHeader("Access-Control-Allow-Origin","*");//任何网站都可以在此路由中拿到数据
})

这样设置只能解决一部分的问题,对于复杂请求起不到作用,而且使用xhr.getAllResponseHeaders无法所以的响应头,Access-COntrol-Allow-Origin","http://127.0.0.1:5500,也无法拿到。

image-20211120154141240

所以要设置

repsonse.setHeader("Access-Control-Expose-Header","*");// 暴露所以的响应头

对于http的复杂请求,比如PUTDELETE这两种请求,或者Content-Type字段的类型是application/json

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

请求的方式是

image-20211120143722400

image-20211120143611138

就是OPTIONS请求。在后端的路由中我们需要设置

app.options("/test",(requset,response)=>{
    repsonse.setHeader("Access-Control-Allow-Origin","具体网站的地址");
	repsonse.setHeader("Access-Control-Allow-		Origin","http://127.0.0.1:5500");// 协议+主机名+端口号
	repsonse.setHeader("Access-Control-Allow-Origin","*");//任何网站都可以在此路由中拿到数据
    repsonse.setHeader("Access-Control-Allow-Methods","*");//任何请求都可以在此路由中拿到数据
})

当然在express还可以使用一个中间件cors来简化这些步骤。

var express = require('express')
var cors = require('cors')
var app = express()
 
app.use(cors())
 
app.get('/products/:id', function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for all origins!'})
})
 
app.listen(80, function () {
  console.log('CORS-enabled web server listening on port 80')
})

具体的使用方法cors - npm (npmjs.com)

标签:浏览器,请求,express,基础,学习,xhr,AJAX,数据
来源: https://blog.csdn.net/fujiaxu666/article/details/121440847

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有