前端视频学习(八、AJAX)

目录

  • 1. 原生JS的AJAX
    • 1.1 第一个AJAX代码
    • 1.2 `readyState`状态码
    • 1.3 控制请求、接收响应
    • 1.4 发送GET、POST请求
    • 1.5 同步和异步
    • 1.6 处理JSON和XML
      • a. 处理JSON
      • b. 处理XML
    • 1.7 兼容性——ActiveXObject
    • 1.8 responseType和response属性
    • 1.9 封装ajax代码——回调
    • 1.10 XMLHttpRequest 2.0
      • AJAX的`onload`事件
  • 2. ★JS前端模板——利用script标签
  • 3. JQuery的ajax方法
    • 3.1 底层方法——$.ajax
    • 3.2 快捷方法—— `$.get 、 $.post`、`$.getJSON`
    • 3.3 局部ajax加载—— `$对象.load`
    • 3.4 局部jQuery对象
    • 3.5 JQuery中AJAX的全局事件处理
      • 使用 NProgess来完成进度条
  • 4. ★跨域(源)问题
    • 4.1 使用别的方式发送跨域请求——img、link、script
      • 1. img
      • 2. link
      • 3. script
    • 4.2 ★JSONP
    • 4.3 JQuery发送JSONP请求
    • 4.4 跨域资源共享CORS

浏览器发出请求的方式有:

  1. 地址栏输入地址
  2. 特定标签的 src或者href
  3. 表单提交
  4. css中@import
  5. AJAX(Asynchronous JavaScript and XML)
var img = new Image();  // DOM的image标签对象
img.src = "1.jpg";  // 这句话运行后浏览器就会尝试去发送请求获取图片

AJAX就是浏览器提供的一套API,可以使用JavaScript调用,对服务器进行请求的发送和响应获取。

1. 原生JS的AJAX

1.1 第一个AJAX代码

类比与使用浏览器查看页面的步骤:

  1. 安装浏览器
  2. 打开浏览器,输入网址
  3. 等待响应
  4. 看结果

使用步骤:

  1. 创建发送请求的对象
  2. 打开和目标连接
  3. 发送情趣
  4. 注册处理函数
  5. 处理结果
方法/函数/属性 含义
new XMLHttpRequest() 创建 xhr请求对象
xhr.open('GET', 'url' ) 创建链接
xhr.send() 发送请求
xhr.readyState 获取状态
xhr.responseText 获取请求内容
xhr.getAllResponseHeaders() 获取所有响应头
xhr.getResponseHeader("string") 根据key获取特定响应头内容
xhr.onreadystatechange 状态时间变化的event

var xhr = new XMLHttpRequest()

xhr.open('GET', './time.php')

xhr.send()

// 因为客户端永远不知道服务端何时才能返回我们需要的响应
// 所以 AJAX API 采用事件的机制(通知的感觉)
xhr.onreadystatechange = function () {
  // 这个事件并不是只在响应时触发,XHR 状态改变就触发
  if (this.readyState !== 4) return
  // 获取响应的内容(响应体)
  console.log(this.responseText)
}

JS是单线程的

发送请求的地址不能使用文件协议访问(file://)

1.2 readyState状态码

onreadystatechange事件会在xhr对象状态变化时触发调用,所以说这个事件会多次触发。

状态码 时机 含义
0 UNSENT 已经完成对象初始化 创建了xhr对象之后,但还未调用open,
1 OEPNED xhr对象调用了open方法之后,已建立了一个与服务端特定端口的连接
2 HEADERS_RECIEVED send已经被调用,且已获取了响应报文的响应头和响应行
3 LOADING xhr正在下载响应报文的响应体,responseText已经可以获取部分响应体
4 DONE xhr处理结束,整个响应报文已经完整下载下来了,可以直接使用responseText

前端视频学习(八、AJAX)_第1张图片

习惯使用addEventListener 注册事件

var xhr = new XMLHttpRequest();
console.log(xhr.readyState);
// => 0
// 初始化 请求代理对象

xhr.open('GET', 'time.php');
console.log(xhr.readyState);
// => 1
// open 方法已经调用,建立一个与服务端特定端口的连接

xhr.send();

xhr.addEventListener('readystatechange', function () {
  switch (this.readyState) {
    case 2:
      // => 2
      // 已经接受到了响应报文的响应头

      // 可以拿到头
      // console.log(this.getAllResponseHeaders())
      console.log(this.getResponseHeader('server'));
      // 但是还没有拿到体
      console.log(this.responseText);
      break;

    case 3:
      // => 3
      // 正在下载响应报文的响应体,有可能响应体为空,也有可能不完整
      // 在这里处理响应体不保险(不可靠)
      console.log(this.responseText);
      break;

    case 4:
      // => 4
      // 一切 OK (整个响应报文已经完整下载下来了)
      console.log(this.responseText);
      break;
  }
})

1.3 控制请求、接收响应

方法/属性 含义
xhr.setRequestHeader(key,value) 设置请求头,一次设置一个
xhr.send(data) 发送的时候设置请求体
xhr.status 响应状态码
xhr.statusText 响应状态描述文本
xhr.responseText 获取请求内容
xhr.getAllResponseHeaders() 获取所有响应头
xhr.getResponseHeader("string") 根据key获取特定响应头内容

根据不同携带的内容,要设置不同的Content-Type
Content-Type ‘application/x-www-form-urlencoded’, 携带的是 Form Data
Content-Type 'text/plain;charset=UTF-8, 携带的是 Request Payload

var xhr = new XMLHttpRequest()

xhr.open('POST', '/add.php') // 设置请求行

xhr.setRequestHeader('Foo', 'Bar') // 设置一个请求头
// 一旦你的请求体是 urlencoded 格式的内容,一定要设置请求头中 Content-Type 'application/x-www-form-urlencoded'
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')

xhr.send('key1=value1&key2=value2') // 以 urlencoded 格式设置请求体

1.4 发送GET、POST请求

  • GET请求的参数直接写在url中
  • POST请求的参数使用send传递,并且要记住设置 Content-Tyeapplication/x-www-form-urlencoded
xhr1.open('GET', 'users.php?id=' + this.id)


// 设置Content-Type
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// xhr.send('username=' + username + '&password=' + password)
xhr.send(`username=${username}&password=${password}`)  // 发送POST请求内容

浏览器可以将id唯一的对象创建出来直接使用:

<input type="button" id="btn" />
<script>
	btn.onclick =  ...
script>

1.5 同步和异步

  • AJAX的open方法还有第三个参数async, 取值为true/false,默认是true
  • 主要阻塞位置是send方法,在同步中,send结束就可以直接获取响应体;而异步中,需要使用事件处理
  • 目前同步使用的方法已废弃*,不推荐使用
   var xhrAsync = new XMLHttpRequest()
    // open 方法的第三个参数是 async 可以传入一个布尔值,默认为 true
    xhrAsync.open('GET', 'time.php', true)
    console.time('async')
    xhrAsync.send()
    console.log(xhrAsync.responseText)
    // console.log('end request')
    console.timeEnd('async')

    // 同步模式 ajax 操作会有楞等的情况
    // 区别在于 send 方法会不会出现等待情况
    // console.log('begin request')
    var xhrSync = new XMLHttpRequest()
    // open 方法的第三个参数是 async 可以传入一个布尔值,默认为 true
    xhrSync.open('GET', 'time.php', false)
    console.time('sync')
    xhrSync.send()
    console.log(xhrSync.responseText)
    // console.log('end request')
    console.timeEnd('sync')

同步阶段,注册onreadystatechange的时机需要在send之前:

    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'time.php', true)
    xhr.send()
    // 一定要注意事件注册时间问题
    xhr.onreadystatechange = function () {
      console.log(this.readyState)
    }

console.time(字符串标识符);
console.timeEnd(字符串标识符); // 结束秒表,打印计时

1.6 处理JSON和XML

a. 处理JSON

方法
JSON.parse()
var jsonData = JSON.parse(data);

b. 处理XML

方法
xhr.responseXML 返回DOM

但是需要服务端响应头中的 Content-Type 必须是 application/xml

 // this.responseXML 专门用于获取服务端返回的 XML 数据,操作方式就是通过 DOM 的方式操作
      
console.log(this.responseXML.documentElement.children[0].innerHTML)
console.log(this.responseXML.documentElement.getElementsByTagName('name')[0])

1.7 兼容性——ActiveXObject

XMLHttpRequest在老版本IE(5/6)有兼容问题:

var xhr = window.XMLHttpRequest ?new XMLHttpRequest(): new ActiveXObject('Microsoft.XMLHTTP');

1.8 responseType和response属性

xhr.responseType属性是客户端设置的,这样之后就可以通过xhr.response来获取对应的对象:

// 我们通过代码告诉请求代理对象,服务端响应给我们的是 JSON
    xhr.responseType = 'json';
    xhr.onreadystatechange = function () {
      if (this.readyState !== 4) return;
      console.log(this.response);
      // this.response 获取到的结果会根据 this.responseType 的变化而变化
      // this.responseText 永远获取的是字符串形式的响应体
    }

1.9 封装ajax代码——回调

这种异步调用结束时的结果,没法像同步调用一样返回给调用者,只能通过回调函数

    function ajax (method, url, params, done) {
      method = method.toUpperCase()
      var xhr = new XMLHttpRequest()

      if (typeof params === 'object') {
        var tempArr = []
        for (var key in params) {
          var value = params[key]
          tempArr.push(key + '=' + value)
        }
        params = tempArr.join('&')
      }

      if (method === 'GET') {
        url += '?' + params
      }

      xhr.open(method, url, false)

      var data = null
      if (method === 'POST') {
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        data = params
      }

      xhr.onreadystatechange = function () {
        if (this.readyState !== 4) return
        // 不应该在封装的函数中主观的处理响应结果
        // console.log(this.responseText)
        // 你说我太主观,那么你告诉我应该做什么
        done(this.responseText)
      }

      xhr.send(data)
    }

1.10 XMLHttpRequest 2.0

http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html

AJAX的onload事件

HTML提供的 XMLHttpRequest Version 2.0 onload事件,就是专门用来处理当请求已经完成时候的处理:

var xhr = new XMLHttpRequest();
xhr.open("GET", "url");
xhr.send();
xhr.onload = function(){
	console.log(this.readyState);   // 永远是 4 
	console.log(this.responseText);
}

2. ★JS前端模板——利用script标签

有很多前端的js渲染模板,art-template就是一个国产的。

步骤:

  1. 选择一个模板引擎:

https://github.com/tj/consolidate.js#supported-template-engines

  1. 下载模板引擎JS文件
  2. 引入到页面中
  3. 准备一个模板
  4. 准备一个数据
  5. 通过模板引擎的JS提供的一个函数将模板和数据整合得到渲染结果HTML
  6. 将渲染结果的HTML 设置到 默认元素的 innerHTML 中

重点:

  1. 利用的是,在script标签中写模板代码,只要script标签的type不是text/javascript,这样不会被解释执行:
  2. script标签在被使用做为模板的时候,type属性建议写跟模板相关的内容,比如x-art-template, 其中x代表自定义

<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>AJAX 动态表格渲染title>
head>
<body>
  <table id="demo">table>

  
  
  <script id="tmpl" type="text/x-art-template">
    {{each comments}}
    <!-- each 内部 $value 拿到的是当前被遍历的那个元素 -->
    <tr>
      <td>{{$value.author}}</td>
      <td>{{$value.content}}</td>
      <td>{{$value.created}}</td>
    </tr>
    {{/each}}
  script>
  <script src="template-web.js">script>
  <script>

    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'test.php')
    xhr.send()
    xhr.onreadystatechange = function () {
      if (this.readyState !== 4) return
      var res = JSON.parse(this.responseText)

      // 模板所需数据
      var context = { comments: res.data }
      // 借助模板引擎的API 渲染数据
      var html = template('tmpl', context)
      console.log(html)

      document.getElementById('demo').innerHTML = html


//       1. 选择一个模板引擎
//        https://github.com/tj/consolidate.js#supported-template-engines
//       2. 下载模板引擎JS文件
//       3. 引入到页面中
//       4. 准备一个模板
//       5. 准备一个数据
//       6. 通过模板引擎的JS提供的一个函数将模板和数据整合得到渲染结果HTML
//       7. 将渲染结果的HTML 设置到 默认元素的 innerHTML 中 
      
      
      //
      // 为什么不在JS变量中写模板?
      // 1. 如果将模板写到JS中,维护不方便,不能换行,没有着色
      // 为什么使用script标记
      // 1. script不会显示在界面

    }

  script>
body>
html>

3. JQuery的ajax方法

$.ajax方法是jQuery封装的ajax, 底层也是 XMLHttpRequest。 jQuery也提供了其他更加方便的ajax方法,它们的底层都是$.ajax

方法
$.ajax
$.get
$.post
$.getJSON

https://api.jquery.com/jQuery.ajax/

3.1 底层方法——$.ajax

// 关于参数
 $.ajax({
	url: 'json.php',
   type: 'get',
   // 设置的是请求参数
   data: { id: 1, name: '张三' },
   // 用于设置响应体的类型 注意 跟 data 参数没关系!!!
   dataType: 'json',
   success: function (res) {
     // 一旦设置的 dataType 选项,就不再关心 服务端 响应的 Content-Type 了
     // 客户端会主观认为服务端返回的就是 JSON 格式的字符串
     console.log(res)
   }
 })

// 关于回调
    $.ajax({
      url: 'time.php',
      type: 'get',
      beforeSend: function (xhr) {
        // 在所有发送请求的操作(open, send)之前执行
        console.log('beforeSend', xhr)
      },
      success: function (res) {
        // 隐藏 loading
        // 只有请求成功(状态码为200)才会执行这个函数
        console.log(res)
      },
      error: function (xhr) {
        // 隐藏 loading
        // 只有请求不正常(状态码不为200)才会执行
        console.log('error', xhr)
      },
      complete: function (xhr) {
        // 不管是成功还是失败都是完成,都会执行这个 complete 函数
        console.log('complete', xhr)
      }
    })

3.2 快捷方法—— $.get 、 $.post$.getJSON

$.get('json.php', { id: 1 }, function (res) {
  console.log(res)
})

$.post('json.php', { id: 1 }, function (res) {
  console.log(res)
})

$.getJSON('json.php', { id: 1 }, function (res) {
  console.log(res)
})

3.3 局部ajax加载—— $对象.load

ajax发送数据,然后将获取到的结果,直接替换元素的内容

$( "#result" ).load( "ajax/test.html #container" );

上面代码,会像 ajax/test.html发送请求,然后将获取到的元素解析,拿到其中id="container"的元素,将其替换成 原页面中#result选择器选中元素的内容。

3.4 局部jQuery对象

$(function($){
 // jquery 的入口可以传入一个 $, 代表就是jquery对象
 // 这样以后就可以调用使用这个局部 jq对象,而不是全局的 jq对象了
});

3.5 JQuery中AJAX的全局事件处理

为所有ajax请求来注册事件,会让每次发送请求的特定时机都触发相同的handler。

Global events are never fired for cross-domain script or JSONP requests, regardless of the value of global

事件处理函数
.ajaxComplete()
.ajaxError()
.ajaxSend()
.ajaxStart()
.ajaxStop()
.ajaxSuccess()
$( document ).ajaxComplete(function() {
  $( ".log" ).text( "Triggered ajaxComplete handler." );
});

所有这些全局注册事件,都必须绑定在document上面。

使用 NProgess来完成进度条

就是一个库,用于在浏览器最顶端显示进度条

  <link rel="stylesheet" href="../nprogress.css">
  <script src="../nprogress.js">script>

<script>
 $(document)
        .ajaxStart(function () {
          NProgress.start()
        })
        .ajaxStop(function () {
          NProgress.done()
        })

script>

4. ★跨域(源)问题

  • 同源策略:是浏览器的一种安全策略,所谓同源是指域名、协议、端口完全相同,只有同源的地址才能可以互相发送AJAX请求
  • 不同源地址之间的请求成为跨域请求
  • 而实际上请求能发送出去,响应也返回了,但是浏览器不允许获取这些响应结果,因为同源策略是浏览器的
  • 不同源地址之间需要相互发送ajax请求,需要服务端和客户端配合才可以。

除了ajax,还有其他可以发送请求的方式:

  1. img
  2. link
  3. script
  4. iframe

4.1 使用别的方式发送跨域请求——img、link、script

1. img

var img = new Image()
img.src = 'http://locally.uieee.com/categories'
  1. 可以发送不同源地址之间的请求
  2. 无法拿到响应结果

2. link

var link = document.createElement('link')
link.rel = 'stylesheet'
link.href = 'http://locally.uieee.com/categories'
document.body.appendChild(link)   // 要加入到文档中,才能发送出去
  1. 可以发送不同源地址之间的请求
  2. 无法拿到响应结果

3. script

var script = document.createElement('script')
script.src = 'http://localhost/time2.php'
document.body.appendChild(script) // 开始发起请求
  1. 可以发送不同源地址之间的请求
  2. 无法拿到响应结果
  3. 借助于能够作为 JS 执行

用js创建script标签发送请求,这是一个异步操作,无法获取到结果,更别说json的结果了。

解决办法

让服务器端返回一段JavaScript代码,将数据传入一个事先约定好的函数中当参数,然后客户端定义这个函数,就可以处理了。

客户端:

var script = document.createElement('script')
script.src = 'http://localhost/time2.php'
document.body.appendChild(script) // 开始发起请求

// 相当于请求的回调
function foo (res) {
  console.log(res)
}

服务器端:

  1. 返回类型为 application/javascript
  2. 返回数据为: foo(data)

这样当客户端接收到服务器端响应后,就会解析其中的js代码,然后调用 foo函数,由于foo已经在客户端定义了,所以就能够接收到服务器给的参数

4.2 ★JSONP

JSONP就是 JSON with Padding, 是一种借助script标签发送跨域请求的技巧

其原理就是在客户端借助script标签请求服务端的一个动态网页,服务器端返回一段带有函数调用的JavaScript全局函数调用的脚本,将原本需要返回给客户端的数据传递进去。

以后绝大多数都是采用JSONP的手段完成不同源地址之间的跨域请求。

例子:
客户端:

<script src="http://api.xxxx.xxx/a.php?callback=foo">script>

服务器端发送

foo(data)

总结:

  1. 跨域问题只是因为XMLHttpRequest无法发送不同源地址之间的请求,所以才导致使用script标签来解决跨越办法,这种方式成为JSONP
  2. JSONP需要服务器端配合,要求服务器端按照客户端的要求,返回一段JS代码来调用客户端提供的函数
  3. 只能发送GET请求
  4. JSONP本质使用的是script标签,和AJAX没有任何关系
  5. JQuery中使用JSONP,就是用$.ajax,然后设置dataTypejsonp

4.3 JQuery发送JSONP请求

  $.ajax({
      url: 'http://localhost/jsonp/server.php',
      dataType: 'jsonp',
      success: function (res) {
        console.log(res)
      }
    })

4.4 跨域资源共享CORS

Cross Origin Resource Share

跨域问题以前无法解决,而现在是可以解决的了,只要在服务器返回响应的时候设置一个头:

Access-Control-Allow-Origin: *    # *代表允许所有IP地址的请求

这样就可以用AJAX跨域了

你可能感兴趣的:(前端)