icodingicoding
主页
JavaScript
Vue
React
TypeScript
Node
bug
笔记
时间线
主页
JavaScript
Vue
React
TypeScript
Node
bug
笔记
时间线
  • 一、JavaScript 基础
  • 二、引用数据类型
  • 三、函数
  • 四、对象
  • 五、DOM 与事件
  • 六、AJAX
  • 七、ES6
  • Generator 函数
  • 模块化

六、AJAX

1、什么是 AJAX

Ajax 是一种异步请求数据的 web 开发技术,对于改善用户的体验和页面性能很有帮助。简单地说,在不需要重新刷新页面的情况下,Ajax 通过异步请求加载后台数据,并在网页上呈现出来。常见运用场景有表单验证是否登入成功、百度搜索下拉框提示和快递单号查询等等。

Ajax 的目的是提高用户体验,较少网络数据的传输量。同时,由于 AJAX 请求获取的是数据而不是html文档,因此它也节省了网络带宽,让互联网用户的网络冲浪体验变得更加顺畅。

关于提高用户的体验,可以通过下面来进行体会

下图是普通的请求方式

下图是ajax请求的方式

2、AJAX 原理是什么

Ajax相当于在用户和服务器之间加了一个中间层,使用户操作与服务器响应异步化。并不是所有的用户请求都提交给服务器,像一些数据验证和数据处理等都交给 Ajax 引擎自己来做,只有确定需要从服务器读取新数据时再由 Ajax 引擎代为向服务器提交请求。

Ajax 的原理简单来说通过XmlHttpRequest对象来向服务器发送异步请求,从服务器获得数据,然后用 JavaScript 来操作 DOM 而更新页面。

XMLHttpRequest是ajax的核心机制,它是在IE5中首先引入的,是一种支持异步请求的技术。简单的说,也就是 JavaScript 可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。

3、AJAX 基本的使用

这里主要掌握的是能够手动创建AJAX.

创建步骤:

创建xhr对象

let xhr = null;
if (window.XMLHttpRequest) {
  // 兼容 IE7+, Firefox, Chrome, Opera, Safari
  xhr = new XMLHttpRequest();
} else {
  // 兼容 IE6, IE5
  xhr = new ActiveXObject("Microsoft.XMLHTTP");
}

配置请求地址与发送请求

xhr.open(method, url, async);
send(string); //`POST`请求时才使用字符串参数,否则不用带参数。
// method:请求的类型;GET 或 POST
// url:文件在服务器上的位置
// async:true(异步)或 false(同步)

注意:POST 请求一定要设置请求头的格式内容

xhr.open("POST", "test.html", true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send("fname=Henry&lname=Ford"); //`POST`请求参数放在send里面,即请求体

处理响应

xhr.onreadystatechange = function () {
  if (xhr.readyState == 4 && xhr.status == 200) {
    document.GetElementById("mydiv").innerHTML = xhr.responseText;
  }
};
什么是 readyState?
readyState是XMLHttpRequest对象的一个属性,用来标识当前XMLHttpRequest对象处于什么状态。
readyState总共有5个状态值,分别为0~4,每个值代表了不同的含义:

0:未初始化 — 尚未调用.open()方法;
1:启动 — 已经调用.open()方法,但尚未调用.send()方法;
2:发送 — 已经调用.send()方法,但尚未接收到响应;
3:接收 — 已经接收到部分响应数据;
4:完成 — 已经接收到全部响应数据,而且已经可以在客户端使用了;
什么是 status?
HTTP状态码(status)由三个十进制数字组成。HTTP状态码共分为5种类型:

1xx(临时响应):表示临时响应并需要请求者继续执行操作的状态码。
2xx(成功):表示成功处理了请求的状态码。
3xx(重定向):表示要完成请求,需要进一步操作。通常,这些状态代码用来重定向。
4xx(请求错误):这些状态码表示请求可能出错,妨碍了服务器的处理。
5xx(服务器错误):这些状态码表示服务器在尝试处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错。

4、AJAX 优缺点分析

优点

(1)无刷新更新数据

AJAX最大的优点是在不需要刷新浏览器的情况下,能够与服务器保持通信,并且能够减少不必要的数据数据传输,降低网络数据流量,这样可以加快响应用户的请求,避免不必要的等待时间,提高用户的体验。

(2)前后端分离

前端人员只关注前端页面逻辑的开发,通过ajax获取后端数据然后进行展示处理,而后端专注于请求的获取,响应的处理,职责明确。

缺点:

(1) 破坏浏览器的后退功能

浏览器有一个比较重要的功能就是历史记录的功能,通过后退按钮可以后退到浏览器之前访问的页面,但是使用了ajax后无法后退,也就是破坏了浏览器的后退机制。

(2)不利于搜索引擎的优化

百度,谷歌在进行搜索引擎优化的时候(SEO),会获取页面中的内容,而通过ajax请求返回的数据是通过javascript动态添加到页面上的,而百度等搜索引擎无法抓取到这些通过javascript动态生成的内容,所以不利于SEO

(3) 破坏了URL统一资源定位的功能。

由于AJAX的请求并不会修改浏览器中地址栏的URL,因此对于相同的URL,不同的用户看到的内容是不一样的,例如,你访问某个电商网站,在该电商网站中搜索到一件非常好的商品,你现在把地址发给你的同学,而你的同学打开这个地址后,却看不到这件商品。

所以网站的搜索的实现,一般不是通过ajax来发送请求。

5、Get 和 Post 请求数据的区别

(1)参数传递

get请求会将参数添加到URL地址的后面,在调用ajax的send方法的时候,传递的参数是null,即xhr.send();

post请求的数据会放在请求体中,用户是无法通过URL地址直接看到的,调用send方法的时候,需要指定要发送到服务端的数据,即xhr.send(data)

(2)服务端的处理

针对get请求与post请求,在服务端的处理也是不一样的。如果以Express来作为服务端,get的请求需要通过Request.query来获取参数,而post请求的处理,需要通过Request.body来获取数据。

(3)传递的数据量

get请求的数据量小,对于不同的浏览器是有差异 ,谷歌浏览器限制8k.post请求传递的数据量比较大,一般默认不受限制。但是服务器一般会做限制。

(4)安全性

get请求的安全性比较低,因为请求的数据会出现在url上,通过浏览器的缓存或者是历史记录很容易获取到请求的数据。post请求是将数据放在请求体中进行传递,数据不会出现在URL,安全性比较高。

6、Get 和 Post 请求的应用场景

在了解了get方式和post请求方式的区别以后,下面看一下它们的应用场景。

get的应用场景

(1)数据的搜索,单击搜索按钮,搜索网站中指定的数据。

(2) 传递的数据量小,适合用于url方式进行传递

(3) 数据安全性要求不高的情况

post请求的应用场景

(1) 传递数据量比较大的情况,例如上传文件

(2) 表单提交,例如用户登录,注册,要求数据安全性比较高的情况。

(3) 请求会修改数据库中数据的情况,例如,添加数据,修改数据等。

7、浏览器同源策略

浏览器同源策略是浏览器最基本也是最核心的安全功能,它规定客户端脚本在没有明确授权的情况下,不能读写不同源的目标资源。

所谓的同源指的是相同协议,域名和端口号,如果两个资源路径在协议,域名,端口号上有任何一点不同,则它们就不属于同源的资源,

另外在同源策略上,又分为两种表现形式:

第一:禁止对不同页面进行DOM操作

第二:禁止使用XMLHttpRequest向不是同源的服务器发送ajax请求。

8、为什么浏览器会有跨域限制的问题?

什么是跨域呢?

访问同源的资源是被浏览器允许的,但是如果访问不同源的资源,浏览器默认是不允许的。访问不同源的资源那就是我们所说的跨域。

如下表格所示:

从表中可以看出域名,子域名,端口号,协议不同都属于不同源,当脚本被认为是来至不同源时,均被浏览器拒绝请求。

浏览器对跨域访问的限制,可以在很大的程度上保护用户数据的安全。

第一:假如没有Dom同源策略的限制,就有可能会出现如下的安全隐患

黑客做了一个假的的网站,通过iframe嵌套了一个银行的网站,然后把iframe的高度宽度调整到占据浏览器的可视区域 ,这样用户进入这个假的网站后,看到就是和真正的银行网站是一样的内容。如果用户输入了用户名和密码,这个假的网站就可以跨域访问到所嵌套的银行网站的DOM节点,从而黑客就可以获取到用户输入的用户名和密码了。

第二:如果浏览器没有XMLHttpRequest同源策略限制,黑客可以进行跨站请求伪造(CSRF)攻击,具体方式如下:

(1)用户登录了个人银行页面A,页面A会在Cookie中保存用户信息

(2)后来用户又访问了一个恶意的页面B,在该页面中执行了恶意Ajax请求的代码

(3)这时页面B会向页面A发送Ajax请求,该请求会默认发送用户Cookie信息。

(4)页面A会从请求的Cookie中获取用户信息,验证无误后,就会返回用户的一系列相关的数据,而这些数据就会被恶意的页面 B 所获取,从而造成用户数据的泄漏。

正是存在这些危险的场景存在,所以同源策略的限制就显得非常总要。

9、跨域问题演示

创建一个文件夹,在该文件夹中创建index.html文件,该文件中的代码如下:

<!DOCTYPE html>
<html lang="en">
  <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>Document</title>
    <script>
      window.onload = function () {
        var btn = document.getElementById("btnLogin");
        btn.addEventListener("click", function () {
          sendRequest();
        });
      };
      function sendRequest() {
        var userName = document.getElementById("userName").value;
        //这里为了简单,暂时不考虑浏览器兼容性问题
        var xhr = new XMLHttpRequest();
        let url = "http://localhost:3000/getUserNameInfo?name=" + userName;
        xhr.open("get", url, true);
        xhr.send();
        xhr.onreadystatechange = function () {
          if (xhr.readyState === 4 && xhr.status === 200) {
            console.log(xhr.responseText);
          }
        };
      }
    </script>
  </head>
  <body>
    用户名:<input type="text" id="userName" /> <br />
    <button id="btnLogin">登录</button>
  </body>
</html>

在该文件夹下面安装express

npm install express

同时创建server.js文件,该文件的代码如下:

var express = require("express");
var app = express();
app.get("/getUserNameInfo", function (req, res) {
  var userName = req.query.name;
  var result = {
    id: 10001,
    userName: userName,
    userAge: 21,
  };
  var data = JSON.stringify(result);
  res.writeHead(200, { "Content-type": "application/json" });
  res.write(data);
  res.end();
});
app.listen(3000, function () {
  console.log("服务端启动....");
});

下面启动服务端

同时index.html文件也通过vscode自带的服务器进行访问。

这时会出现如下错误:

Access to XMLHttpRequest at 'http://localhost:3000/getUserNameInfo?name=admin' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

通过以上错误可以发现,现在的程序出现 跨域的问题,

下面看一下具体的解决方案

10、CORS

通过上面的错误,我们明白了,客户端不能发送跨域请求是因为服务端并不接收跨域的请求,所以为了解决跨域请求的问题,我们可以将服务端设置为可以接收跨域请求。

这里我们需要使用CORS('跨域资源共享'),来解决跨域请求的问题。CORS主要的实现方式是服务端通过对响应头的设置,接收跨域请求的处理。

服务端修改后的代码如下:

var express = require("express");
var app = express();
app.all("*", function (req, res) {
  //设置可以接收请求的域名
  res.header("Access-Control-Allow-Origin", "http://127.0.0.1:5500");
  res.header("Access-Control-Allow-Methods", "GET, POST,PUT");
  res.header("Access-Control-Allow-Headers", "Content-Type");
  res.header("Content-Type", "application/json;charset=utf-8");
  req.next();
});
app.get("/getUserNameInfo", function (req, res) {
  var userName = req.query.name;
  console.log("userName=", userName);
  var result = {
    id: 10001,
    userName: userName,
    userAge: 21,
  };
  var data = JSON.stringify(result);
  res.writeHead(200, { "Content-type": "application/json" });
  res.write(data);
  res.end();
});
app.listen(3000, function () {
  console.log("服务端启动....");
});

在原有的代码中,我们主要是添加了如下的代码:

app.all("*", function (req, res) {
  //设置可以接收请求的域名
  res.header("Access-Control-Allow-Origin", "http://127.0.0.1:5500");
  res.header("Access-Control-Allow-Methods", "GET, POST,PUT");
  res.header("Access-Control-Allow-Headers", "Content-Type");
  res.header("Content-Type", "application/json;charset=utf-8");
  req.next();
});

在上面的代码中,最主要的是res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');这行代码,

这行代码是必须的,表示服务器可以接收哪个域发送的请求,可以用通配符*,表示接收全部的域,但是为了安全,我们最好设置特定的域。我们这里测试的是http://127.0.0.1:5500(注意:如果客户端地址是127.0.0.1,这里不能写成localhost,同时还需要注意,这里地址最后没有/)

后面请求头信息可以根据情况进行选择设置,例如接收请求的方法,数据传输的格式等。

通过对服务端的处理不会对前端代码做任何的处理,但是由于不同系统服务端采用的语言与框架是不同的,所以导致服务端的处理方式不同。

11、JSONP

JSONP是客户端与服务端进行跨域通信比较常用的解决办法,它的特点是简单,兼容老式浏览器,并且对服务器影响小。

JSONP的实现的实现思想可以分为两步:

第一:在网页中动态添加一个script标签,通过script标签向服务器发送请求,在请求中会携带一个请求的callback回调函数名。

第二: 服务器在接收到请求后,会进行相应处理,然后将参数放在callback回调函数中对应的位置,并将callback回调函数通过json格式进行返回。

前端代码:

<!DOCTYPE html>
<html lang="en">
  <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>Document</title>
    <script>
      window.onload = function () {
        var btn = document.getElementById("btnLogin");
        btn.addEventListener("click", function () {
          sendRequest();
        });
      };
      function sendRequest() {
        var userName = document.getElementById("userName").value;
        //请求参数,其中包含回调函数
        var param = "name=" + userName + "&callback=successFn";
        //请求的url
        var url = "http://localhost:3000/getUserNameInfo?" + param;
        var script = document.createElement("script");
        script.src = url;
        document.body.appendChild(script);
      }
      function successFn(result) {
        console.log("result=", result);
      }
      //   function sendRequest() {
      //     var userName = document.getElementById("userName").value;
      //     //这里为了简单,暂时不考虑浏览器兼容性问题
      //     var xhr = new XMLHttpRequest();
      //     let url = "http://localhost:3000/getUserNameInfo?name=" + userName;
      //     xhr.open("get", url, true);
      //     xhr.send();
      //     xhr.onreadystatechange = function () {
      //       if (xhr.readyState === 4 && xhr.status === 200) {
      //         console.log(xhr.responseText);
      //       }
      //     };
      //   }
    </script>
  </head>
  <body>
    用户名:<input type="text" id="userName" /> <br />
    <button id="btnLogin">登录</button>
  </body>
</html>

在上面的代码中,我们重新改造了sendRequest方法,在该方法中构建了param参数,该参数的内容包括了用户输入的用户名以及回调函数名。下面构建好所要请求的服务端的url地址,将该url地址交给script标签的src属性,通过该属性向服务器发送请求。

同时定义回调函数successFn,接收服务端返回的数据。可以对服务端返回的数据做进一步的处理。

这里需要注意的一点就是:回调函数必须设置为全局的函数。因为服务端返回响应后,会在全局环境下查找回调函数。

下面看一下服务端的处理:

var express = require("express");
var app = express();
// app.all('*', function (req, res) {
//     //设置可以接收请求的域名
//     res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
//     res.header('Access-Control-Allow-Methods', 'GET, POST,PUT');
//     res.header('Access-Control-Allow-Headers', 'Content-Type');
//     res.header('Content-Type', 'application/json;charset=utf-8');
//     req.next();
// })
app.get("/getUserNameInfo", function (req, res) {
  var userName = req.query.name;
  //获取请求的回调函数
  var callbackFn = req.query.callback;
  console.log("callbackFn==", callbackFn);
  console.log("userName=", userName);
  var result = {
    id: 10001,
    userName: userName,
    userAge: 21,
  };
  var data = JSON.stringify(result);
  res.writeHead(200, { "Content-type": "application/json" });
  //返回值是对对回调函数的调用
  res.write(callbackFn + "(" + data + ")");
  // res.write(data);
  res.end();
});
app.listen(3000, function () {
  console.log("服务端启动....");
});

在服务的代码中,需要接收回调函数的名称。

同时返回的内容中,包含了回调函数的名称,以及传递给该回调函数的具体数据。

这样当回调函数返回给浏览器后,浏览器可以从全局的环境中查找该回调函数,并进行执行。

使用JSONP的优点与缺点:

优点:

简单,不存在浏览器兼容性的问题

缺点:

只能实现get请求,如果是post请求则无法进行跨域的处理。

最后编辑:
上一页
五、DOM 与事件
下一页
七、ES6