AJAX基础

727 阅读2分钟

笔记来源:拉勾教育 - 大前端就业集训营

文章内容:学习过程中的笔记、感悟、和经验

Ajax基础

Ajax概述

背景

可以简单地认为目前JS能力有限,因此在此之前web平台提供的API还全部停留在单机的阶段

这样就造成了我们无法实现某些功能,例如:

  • 无法在实现用户登录功能时,当用户输入邮箱地址显示用户对应的头像
  • 无法在实现用户注册功能时,当用户输入邮箱或者用户名就提示是否存在
  • 无法在实现留言板功能时,实时看到最新的用户留言

数据是存在服务端的,之前的方法无法通过api来获取服务器的数据

已知发送请求方式

  • 地址栏输入、回车、刷新
  • 特定元素的href和src属性
  • 表单提交

但这些方式我们无法通过或者很难通过代码的方式进行编程操作

需求

对服务端发出请求并且接受服务器端返回的响应

如果我们可以通过js直接发送网络请求,那么web就可以实现更多的功能,不再是单机的

Google Suggest

AJAX最早出现于2005年的Google Suggest

  • 是一种正式的技术
  • 在浏览器端进行网络编程(发送请求、接收响应)的技术方案
  • 我们可以通过JS直接获取服务器端的最新内容而不需要重新加载页面
  • 让web更接近桌面应用的体验

实现局部更新的效果

AJAX(Asynchronous Javascript And XML)

是浏览器提供的一套API,可通过JS的调用,实现通过代码控制相应和请求,实现通过js进行网络编程

XML:最早在客户端和服务端传递数据时所采用的数据格式

应用场景

  • 按需获取数据
  • 对用户进行校验
  • 自动更新页面内容
  • 提升用户体验,无刷新的体验

体验AJAX

使用jQuery中封装的方法

免费接口:jsonplaceholder.typicode.com/

// JQ中封装的AJAX方法
$.ajax({
  // 地址
  url: 'https://jsonplaceholder.typicode.com/posts',
  // 请求方式
  type: 'GET',
  // 筛选数据(title为"doloribus ad provident suscipit at"的数据)
  data: {
    'title': "doloribus ad provident suscipit at"
  },
  // 数据格式
  dataType: 'json',
  // 返回数据,data为返回的数据,可以进行操作
  success: function (data) {
    console.log(data)
  }
})

原生AJAX详解

工作中最常用的是封装好的AJAX库

发送AJAX请求步骤

  1. 创建XMLHttpRequest类型的对象
  2. 准备发送,打开一个网址之间的连接
  3. 执行发送动作
  4. 指定xhr状态变化事件处理函数
// 原生AJAX方法
// 创建XMLHttpRequest实例对象对象
const xhr = new XMLHttpRequest()
// 调用对象的open方法准备打开链接
// 参数:请求方法,链接地址
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts')
// 调用send方法发送请求
// 参数:GET方式参数为null
xhr.send(null)
// 监听对象的readystatechange事件
xhr.onreadystatechange = function () {
  //一旦对象readyStat属性变为4表示数据获取成功
  if (this.readyState === 4) {
    // 打印获取到的数据,数据存在对象的responseText属性中
    console.log(this.responseText)
  }
}

XMLHttpRequest对象

AJAX中核心提供了一个XMLHttpRequest类,所有AJAX操作都要依靠这个类(属性和方法)

使用XMLHttpRequest类的时候我们先要创建一个实例对象

const xor = new XMLHttpRequest()

IE6兼容写法

xhr = new ActiveXObject('Microsoft.XMLHTTP')

// 创建XMLHttpRequest对象
// 创建变量
let xhr = null
// 进行浏览器能力判断,判断浏览器是否存在XMLHttpRequest
if (window.XMLHttpRequest) {
  // 如果存在使用XMLHttpRequest类创建
  xhr = new XMLHttpRequest()
} else {
  // 不存在使用IE6方法创建
  xhr = new ActiveXObject('Microsoft.XMLHTTP')
}
// 这是我写的三元表达式创建方法,在新浏览器可以实现,但没有测试IE6
// let xhr = window.XMLHttpRequest !== undefined ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
console.log(xhr)

open()方法

本质上XMLHttpRequest就是js在web平台发送HTTP请求的手段,所以同样符合HTTP约定的格式

语法:实例.open( 请求方式 ,链接地址 )

  • 参数1:要使用的HTTP方法,例如:GET获取数据、POST提交新数据、PUT更改、DELETE删除等,大小写皆可
  • 参数2:要向其发送的地址

两个参数都是字符串格式

xhr.open('get', 'https://jsonplaceholder.typicode.com/posts')

send()方法和请求头

发送HTTP请求

语法:实例.send( 数据 )

参数:在XHR请求重要发送的数据体,根据请求头中的类型进行传参数

如果是get方法,不需要传参,或者使用null即可

设置请求头setRequsetHeader()

此方法必须写在open和send方法之间调用

语法:实例.setRequsetHeader( header

数据类型,value具体数据类型 )

  • 参数1:一般设置为'Content-Type',服务器需要我们传送的数据类型
  • 参数2:具体的数据类型,常用’application/x-www-form-urlencoded'和'application/json'
// // 使用get方式,需要查询的内容直接写在网址后面,使用?连接(?id=1)
// xhr.open('get', 'https://jsonplaceholder.typicode.com/users?id=1')
// // get方法不需要设置请求头setRequsetHeader()
// // send方法也不需要传递参数
// xhr.send()



//使用post方法,把需要增加的内容写在send方法内部,而不直接写在open网址上
xhr.open('POST', 'https://jsonplaceholder.typicode.com/users')
// 并且需要设置请求头
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// send内部传递参数只用字符串并且每个参数名=参数值,使用&连接
xhr.send('name=cheng&age=19')

响应状态分析

创建onreadystatechange事件,监听readyState属性的变化

readyState属性返回一个值代表当前XMLHttoRequest当前的状态,而这个值在整个请求响应中对变化多次,而只有当这个值为4的时候,才是我们需要的,所以我们监听readyState变化,一旦值为4我们就获取返回的数据

s6EgXD.png

readyState值状态描述说明
0UNSENT实例被创建但尚未调用open方法
1OPENED调用open方法建立了连接
2HEADERS_RECEIVED调用send方法并且已经可以获状态行和响应头
3LOADING响应体下载中,responseText属性可能已经包含部分数据
4DONE响应体下载完成,可以直接使用responseText里面的数据
xhr.onreadystatechange = function () {
  // 查看一下状态,这里只有2、3、4
  console.log(this.readyState)
  //一旦对象readyStat属性变为4表示数据获取成功
  if (this.readyState === 4) {
    // 打印获取到的数据,数据存在对象的responseText属性中
    console.log(this.responseText)
  }
}

同步和异步

  • 同步:在同一时刻只能做一件事情,后面事情需要等待
  • 异步:执行一个耗时的操作(不需要看管)去做别的事,不等待这件事完成

AJAX中设置同步和异步执行

  • 在open()方法中传递第三个参数
  • 值为布尔值,true(默认值)代表异步,flase代表同步
  • 如果设置同步执行,则代码会卡死在send()这一步

不建议使用同步,甚至浏览器会提醒不建议,因为会卡在send这一步,等待全部数据加载成功了后面的代码才能执行,如果此时监听onreadystatechange事件写在send后面会导致执行失败

建议

  • 先注册onreadystatechange事件,保证事件一定能触发(不论是同步还是异步)
  • 切记不要使用同步

数据格式

我们真正关心的问题就是服务端发出何种格式的数据,这种格式如何在客户端用JavaScript解析。

xml格式

一种数据描述手段,非常古老,但现在不怎么使用了,因为数据冗余太多

json格式 - JavaScript Object Notation

js对象表示法,也是一种数据的描述手段,类似于js中的对象字面量

服务端采用json格式返回数据,客户端使用json格式解析

特点:

  • json格式不需要存储在变量中,可以直接写在.json格式文件中,使用{}包裹全部数据
  • 结束不需要写分号
  • 属性名必须加引号的字符串,属性值可以是任意格式
// 创建一个对象
const obj = {
    name: 'zs',
    age: 19,
    sex: true
  },
  // 创建一个json格式的字符串
  str = '{"name":"ls","age":19,"sex":"flase"}'
// json格式和对象格式可以互相转换
// 使用ES5中新加的json对象的方法stringify转字符串
console.log(JSON.stringify(obj))
// 使用ES5中新加的json对象的方法parse把json转对象
console.log(JSON.parse(str))

注意事项:

  • 不管是json还是XML,只不过是AJAX过程中用到的数据格式,和AJAX没有必然关系
  • 不管是XML还是json,本质上都是把数据返回给客户端
  • 服务器端应该根据相应内容格式设置一个合理的Content-Type

json server使用

需求:平时我们自己写一些数据,希望通过AJAX方式获取,就需要在本地搭建一个临时的服务器

json-server是一个node模块,运行Express服务器,可以指定一个json文件作为api数据源,可以使用它快速搭建一个服务器

网址:github.com/typicode/js…

步骤:

下载安装node.js

  1. 打开命令行(mac终端),输入(mac加sudonpm install -g json-server,等待完成即在本地全局安装了json-server

  2. 新建db.json文件,在文件内书写json代码

    //注意:json-server内部首层只支持数组或者对象类型,再往里可以书写其他类型
    {
      "posts": [
        { "id": 1, "title": "json-server", "author": "typicode" }
      ],
      "comments": [
        { "id": 1, "body": "some comment", "postId": 1 }
      ],
      "profile": { "name": "typicode" }
    }
    
  3. 在VScode中点击终端 -- 新终端即可在VScode内新建终端(命令行)

  4. 在终端输入json-server --watch db.json回车,即可开启本地服务器,监听db.json文件

  5. 命令行下面会显示打开的端口,及可以访问的资源,按住control单击鼠标即可访问(home为主页,其他后缀为db.json中的所有可访问资源)

sgMKJg.png

json文件书写方法

{
  "students":[
    {"id":1,"name":"zs","age":"19","sex":1},
    {"id":2,"name":"ls","age":"20","sex":0},
    {"id":3,"name":"ww","age":"18","sex":1}
  ],
  "tiezis":[
    {"id":1,"studentId":1,"title":"123","content":"123123123"},
    {"id":2,"studentId":3,"title":"456","content":"456456456"},
    {"id":3,"studentId":2,"title":"789","content":"789789789"},
    {"id":4,"studentId":1,"title":"000","content":"000000000"}
  ],
  "pingluns":[
    {"id":1,"tieziId":1,"content":"123真好看"},
    {"id":2,"tieziId":2,"content":"456真好看"},
    {"id":3,"tieziId":1,"content":"123真好看"},
    {"id":4,"tieziId":3,"content":"789真好看"},
    {"id":5,"tieziId":3,"content":"789真好看"},
    {"id":6,"tieziId":4,"content":"000真好看"}
  ],
  "zhushi":[
    {"id":1,"content":"我是注释,貌似json不支持注释,我就写写在路由里了"},
    {"id":2,"content":"json内部第一层应该是对象或者数组格式,因为这后期这个名字会被作为路由的名字来使用"},
    {"id":3,"content":"如果路由是数组,可以加s代表多个"},
    {"id":4,"content":"id属性是必须的,因为这是相当于索引(类似数组下标),id从1开始"},
    {"id":5,"content":"如果想做关联,可以在其他路由中使用  关联路由名字+Id  的方式关联到路由,例如 studentId,注意可以去掉关联路由的s,后期可以使用关联调用,例如:http://localhost:3000/students/1/tiezis   可以访问students的第一个id(zs)的所有帖子"},
    {"id":6,"content":"json内部全都是双引号书写,不要使用单引号,使用英文状态下符号"}
  ]
}

原生AJAX使用

get方法

  • 在get请求中,参数通过url地址传递,使用?连接需要传递的参数
  • 在get请求中无需设置请求头,无需设置响应体,send无需传参或者传null即可

使用get获取上面index.json内容(首先使用jsons-erver打开一个本地服务器)

// 创建实例对象
const xhr = new XMLHttpRequest()
// 调用open方法,使用get方法,并且筛选所有age=18的成员
xhr.open('get', 'http://localhost:3000/students?age=18')
// 调用send方法
xhr.send()
// 监听状态变化
xhr.onreadystatechange = function () {
  // 状态值等于4执行打印获取结果
  if (this.readyState === 4) {
    console.log(this.responseText)
  }
}

post方法

  • 请求体承载需要提交的数据
  • 需要设置请求头,一边服务器接收数据
  • 需要使用send方法传递参数
// 创建实例对象
const xhr = new XMLHttpRequest()
// 调用open方法,使用post方法,向students内部添加数据
xhr.open('post', 'http://localhost:3000/students')
// 设置请求头,以url的形式进行添加
// xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// // send内部使用字符串的方式添加数据
// // 是不是很类似于get的数据请求?没错,就是类似于get
// xhr.send("name=lulu&age=19&sex=1")

// 使用json格式设置请求头,就可以使用对象字面量的格式传递数据了
xhr.setRequestHeader('Content-Type', 'application/json')
// 下两种方法都可以实现添加数据的效果
// 方法1:使用对象字面量的模版字符串
// xhr.send(`{
//   "name":"lala",
//   "age":19,
//   "sex":0
// }`)
// 方法2:使用JSON的stringify方法把对象转换为字符串
xhr.send(JSON.stringify({
  name: "heihei",
  age: 19,
  sex: 0
}))
xhr.onreadystatechange = function () {
  if (this.readyState === 4) {
    console.log(this.responseText)
  }
}


// 注意:id不需要用添加,程序会自动添加一个id

处理相应数据的渲染

把客户端返回来的数据呈现到界面上

  • 如果数据较简单,可以使用字符串拼接操作
  • 如果数据比较复杂可以使用模版字符串或者模版引擎
  // 获取元素
  const box = document.getElementById('box')
  // 创建实例对象并使用get方法获得数据
  const x = new XMLHttpRequest()
  x.open('get', 'http://localhost:3000/students')
  x.send()
  // 添加状态改变事件
  x.onreadystatechange = function () {
    if (this.readyState === 4) {
      // 调用添加元素函数,并把获取到的数据转为对象
      add(JSON.parse(this.responseText))
    }
  }

  function add(data) {
    // 遍历数据
    for (const i of data) {
      // 创建新的tr元素,并且更改trinnerHTML
      const newTr = document.createElement('tr')
      newTr.innerHTML = `<td>${i.id}</td><td>${i.name}</td><td>${i.age}</td><td>${i.sex === 0?'女':'男'}</td>`
      // 把tr添加给box
      box.appendChild(newTr)
    }
  }

封装AJAX函数(库)

  • 这里主要是为了了解封装的过程,一般情况在开发中都是使用第三方提供的AJAX库,因为它们可能更加严谨。
  • 为了在后续的开发过程中可以更方便的使用这套API,一般的做法都是将其封装到一个函数中以便调用。
// AJAX库函数
// 功能:根据传递参数,向数据库获取或者添加数据
// 参数1:请求方法(get和post)
// 参数2:请求url地址
// 参数3:请求参数
// 参数4:回调函数
function ajax(method, url, obj, fun) {
  // 将参数1转大写,方便后期进行判断
  method = method.toUpperCase()
  // 创建一个ajax的实例对象,并且使用IE6兼容写法
  const xhr = window.XMLHttpRequest ?
    new XMLHttpRequest() :
    new ActiveXObject('Microsoft.XMLHTTP')
  // 创建一个空数组,用于接收参数
  let att = []
  // 遍历参数,将参数的每一项添加进数组
  for (const i in obj) {
    att.push(`${i}=${obj[i]}`)
  }
  // 数转字符串,使用=连接,模拟传参格式
  const str = att.join('&')
  // 调用open方法,参数使用三元表达式,判断是否是get方法,方法不同传参不同
  xhr.open(method, method === 'GET' ? url + '?' + str : url)
  // 判断是否为post方法,如果是设置请求头
  if (method === 'POST') {
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
  }
  // 调用send方法,根据方法不同使用不同的参数
  xhr.send(method === 'GET' ? null : str)
  // 监听readyState变化事件,当数值等于4执行
  xhr.onreadystatechange = function () {
    if (this.readyState === 4) {
      // 调用回调函数,参数使用JSON.parse转回对象方便使用
      fun(JSON.parse(this.responseText))
    }
  }
}

// 验证函数是否能正常执行
ajax('get', 'http://localhost:3000/students', {
  age: 19
}, function (data) {
  console.log(data)
})
ajax('post', 'http://localhost:3000/students', {
  age: 19,
  name: 'laolao',
  sex: 0
}, function (data) {
  console.log(data)
})