前端网络编程:AJAX

文章目录

    • 谷歌模拟网站加载的方法(可略过)
    • AJAX简介
    • AJAX快速上手
      • AJAX发送请求
      • AJAX接收响应
        • readState
    • 如何在ajax设置请求、响应
    • 具体用法:
      • 数据接口的概念
      • GET请求及实例
      • POST请求及实例
    • 同步模式和异步模式
    • 响应的数据格式XML、JSON
    • 客户端如何处理服务端响应的数据(处理响应数据渲染)
      • 以往操作:代码方式
      • 模板方式
        • 使用模板引擎动态渲染表格实例
    • XMLHttpRequest的兼容方案(面试)
    • AJAX的封装
      • 最终版
    • 回调
      • 回调的嵌套情况
    • JQ的AJAX
    • JQ的load方法(局部刷新)
      • 全局事件处理函数
      • 最常见的加载进度方式
    • 跨域
      • JSONP技巧
      • JSONP封装
      • AJAX跨域(CORS)

谷歌模拟网站加载的方法(可略过)

如何用谷歌来模拟不同网速下的网站加载速率?

右击检查,打开谷歌调试工具的Network栏->右上的下标Add…->Add custom profile…->文件名、上传/下载/延迟速度->返回点右上下标中的该文件名,可以在该模式测试当前网页加载速度。

学习核心:基本使用、使用封装API、跨域

AJAX简介

1.什么是AJAX:浏览器提供的一套 API

2.AjAX的作用:可以通过 JavaScript 直接发送请求接收响应,从而实现网络编程(发送请求、接收响应)。

3.为什么要有AJAX:解决让js在浏览器不刷新时的情况下,就能拿到数据。

4.学习核心:基本使用、使用封装API、跨域

5.学习重点:不是在于我要发个请求,而在于要如何通过代码来发请求(网络编程)

AJAX快速上手

不考虑兼容就只有4行代码。不过我们暂时还不知道这该怎么用,先跳过这块代码,看下面的发送请求、接收响应。

// 1. 创建一个 XMLHttpRequest 类型的对象 —— 相当于打开了一个浏览器 
var xhr = new XMLHttpRequest() 
// 2. 打开与一个网址之间的连接 —— 相当于在地址栏输入访问地址 
xhr.open('GET', './time.php') 
// 3. 通过连接发送一次请求 —— 相当于回车或者点击访问发送请求 
xhr.send(null) 
// 4. 指定 xhr 状态变化事件处理函数 —— 相当于处理网页呈现后的操作 
//onload是HTML5中提供的XMLHttpRequest2.0定义的,否则用onreadystatechange
xhr.onload = function () {   
	// 通过 xhr 的 readyState 判断此次请求的响应是否接收完成   
  if (this.readyState !== 4) return;     
	// 通过 xhr 的 responseText 获取到响应的响应体   
    //到这ajax操作就结束了。到这里,就是放我们之前所熟悉的代码js。进行发送请求动态获取数据呈现到页面上等
    console.log(this.responseText)  
 }

AJAX发送请求

AJAX提供的核心类型:XMLHttpRequest。作用——模拟平时上网过程:这三行代码就是ajax的核心

// 涉及到 AJAX 操作的页面“不能”使用文件协议访问(文件的方式访问,某些情况可以;可以http方式访问)
// 1. 安装浏览器(用户代理)
    //  xhr js用户代理对象,就类似于浏览器的作用(发送请求接收响应)
    var xhr = new XMLHttpRequest()
    // 2. 打开浏览器 输入网址
    //参数:请求方式 网址/相对路径/绝对路径(用/或./表示根目录)
    xhr.open('GET', 'http://day-11.io/time.php')
    // 3. 敲回车键 开始请求
    xhr.send()
    // 4. 等待响应(较复杂,先不写)

    // 5. 看结果

在js中类型就是构造函数,可以通过new出来。

通过Network的XHR可以看到ajax请求,发送完了之后重点就是:接收响应——如何把服务端响应给我们的数据显示/打印出来(怎样把它从js里面拿到)。

AJAX接收响应

首先注意:不能通过变量去接收xhr.send(),原因:因为响应需要时间,所以无法通过返回值的方式返回响应。

AJAX是通过onreadystatechange事件获取响应(再通过xhr.readState获取响应的内容)。在HTML5中可用onload事件代替。

不建议用on的方式注册事件,而是用xhr.addEventListener(‘事件名’, function () {…}) 防止事件注册多次,虽然该事件一般只注册一次但仍建议这样。

    //发送响应过程:1.创建浏览器(代理用户)
    var xhr = new XMLHttpRequest()

    // 如果需要补货第一个状态的变化,需要注意代码执行顺序的问题(不要出现来不及的情况)
    // xhr.onreadystatechange = function () {
    //   // 这个事件并不是只在响应时触发,状态改变就触发
    //   // console.log(1)
    //   console.log(this.readyState)
    // }
    
	//2.打开一个地址(以get的方式请求一个地址,请求响应可以用到相对/绝对地址,原因:在同一个网站下面)
    xhr.open('GET', './time.php')
	//3.发送请求
    xhr.send()

	//接收响应过程:4.接收响应
    // 因为客户端永远不知道服务端何时才能返回我们需要的响应,所以 AJAX API 采用事件的机制(通知的感觉)。
    xhr.onreadystatechange = function () {
      // 这个事件并不是只在响应时触发,而是XHR 状态改变就触发。注册事件时间的早晚决定接收的数据,所以如果需要补货第一个状态的变化,需要注意代码执行顺序的问题(不要出现来不及的情况)。
      // console.log(1)确认事件能否执行
 //我们只关心readState=4接收完响应的状态。写出非的形式代码就不会嵌套了
      if (this.readyState !== 4) return
      // 获取响应的内容responseText(即响应体),获取响应头的情况较少
      console.log(this.responseText)
    }

readState

readyState 状态描述 说明
0 UNSENT 初始化:(请求)代理对象
1 OPENED open方法已经调用,建立一个与服务端特定端口的连接
2 HEADERS_RECEIVED send() 方法已经被调用,且已接受到了响应报文的响应头,但没拿到响应体(xhr.getAllResponseHeaders(’指定键可省略‘)可拿到,注意是否用到分割)
3 LOADING 响应体下载中(在这里处理响应体不可靠)
4 DONE 响应体下载完成,可以直接使用 responseText 。

出现阶段:0(代码中写new后)、1(写open后、send后)、(2、3、4)(写接收响应时)

如何在ajax设置请求、响应

本质上 XMLHttpRequest 就是 JavaScript 在 Web 平台中发送 HTTP 请求的手段,所以我们发送出去的请求仍然是 HTTP 请求,同样符合 HTTP 约定的格式

请求:仍然是HTTP请求

// 设置请求报文的请求行(协议版本是自动设置的)
xhr.open('GET', './time.php') 
// 设置请求头(设置多个请求头:多执行几次即可)
xhr.setRequestHeader('Accept', 'text/plain') 
// 一旦你的请求体是 urlencoded 格式的内容,一定要设置请求头中 Content-Type 'application/x-www-form-urlencoded'。   json的:'application/json'
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// 设置请求体,不设置就为null或不写参数 xhr.send('请求体') 
xhr.send('key1=value1&key2=value2') // 以 urlencoded 格式设置请求体 '{"foo": "123"}'json格式
xhr.onreadystatechange = function () {   if (this.readyState === 4) {     
	// 获取响应状态码     
	console.log(this.status)    
    // 获取响应状态描述     
    console.log(this.statusText)     
    // 获取响应头信息     
  console.log(this.getResponseHeader('Content‐Type')) // 指定响应头     
  console.log(this.getAllResponseHeader())// 全部响应头     
    // 获取响应体     
   console.log(this.responseText) // 文本形式   
   console.log(this.responseXML) // XML 形式,了解即可不用了   
    } 
}

参考链接:
https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest

https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest

具体用法:

数据接口的概念

接口(API)概念:只要能提供特定能力、有输入有输出都可以叫接口。2种:编程语言的一些函数、web API(对于返回数据的地址我们称为接口或数据接口,只不过形式上是Web形式,如api.douban.com/v2/movie/top250,可以给它?后传参数。可以用自己写的,也可以用别人提供的(前提:别人让你用),如github.com/zce/weapp-demo就是用这个豆瓣接口做的app)。

而ajax操作大部分就是以这种接口的地址作为我们需要去请求的(xhr.open的第二个参数),因为一般是拿数据。

GET请求及实例

AJAX发送GET请求并传递参数:

通常在一次 GET 请求过程中,参数传递都是通过 URL 地址中的 ? 参数传递。

var xhr = new XMLHttpRequest() 
// GET 请求传递参数通常使用的是问号传参 
// 这里可以在请求地址后面加上参数,从而传递数据到服务端 
xhr.open('GET', './delete.php?id=1') 
// 一般在 GET 请求时无需设置响应体,可以传 null 或者干脆不传 
xhr.send(null) xhr.onreadystatechange = function () {   if (this.readyState === 4) {     console.log(this.responseText)   
	} 
}   
// 一般情况下 URL 传递的都是参数性质的数据,而 POST 一般都是业务数据

实例:要求在界面上把所有用户的名字打印到列表中,然后点击用户名可以再发送个请求——alert拿到用户年龄。

步骤:1.拿到所有的数据2.拿到想要的结果

//07-ajax-get.html的



  
  AJAX发送GET请求并传递参数


  //3.的
  
    //users.php的
     id 为 1 的用户信息
    
    $data = array(
      array(
        'id' => 1,
        'name' => '张三',
        'age' => 18
      ),
      array(
        'id' => 2,
        'name' => '李四',
        'age' => 20
      ),
      array(
        'id' => 3,
        'name' => '二傻子',
        'age' => 18
      ),
      array(
        'id' => 4,
        'name' => '三愣子',
        'age' => 19
      )
    );
    
    
    if (empty($_GET['id'])) {
      // 没有 ID 获取全部
      // 因为 HTTP 中约定报文的内容就是字符串,而我们需要传递给客户端的信息是一个有结构的数据
      // 这种情况下我们一般采用 JSON 作为数据格式
      $json = json_encode($data); // => [{"id":1,"name":"张三"},{...}]
      echo $json;
    } else {
      // 传递了 ID 只获取一条
      foreach ($data as $item) {
        if ($item['id'] != $_GET['id']) continue;
        $json = json_encode($item); // => [{"id":1,"name":"张三"},{...}]
        echo $json;
      }
    }
    

    POST请求及实例

    POST 请求过程中,都是采用请求体承载需要提交的数据。

    var xhr = new XMLHttpRequest() 
    // open 方法的第一个参数的作用就是设置请求的 method 
    xhr.open('POST', './add.php') 
    // 设置请求头中的 Content‐Type 为 application/x‐www‐form‐urlencoded 
    // 标识此次请求的请求体格式为 urlencoded 以便于服务端接收数据 
    xhr.setRequestHeader('Content‐Type', 'application/x‐www‐form‐urlencoded') // 需要提交到服务端的数据可以通过 send 方法的参数传递 // 格式:key1=value1&key2=value2 
    xhr.send('key1=value1&key2=value2') xhr.onreadystatechange = function () {   if (this.readyState === 4) {     console.log(this.responseText)   
    	} 
    }
    

    注意:实际开发中不能一会写js一会写php,一般先写好结果先测试下防止服务端出问题再写客户端功能。

    (但用浏览器直接去请求它,浏览器发的是get请求。怎么测试post请求?用个小工具Postman,它可以专门用来发各种类型请求、调试接口等。

    测试post请求:输入.php所在网址,选post请求,多出来个body选项,点对应请求体格式,添加键值对如用户名、密码等,按send提交即可测试)

    调试接口:输入接口地址,添加?后的参数按send

    实例:要求页面不刷新把数据提交到服务端去(用form肯定会刷新,所以用table,技术用js+ajax)。

    这里的loading代码块可以去掉:在网络请求慢时做加载效果

    //08-ajax-post.html
    
    
    
      
      AJAX发送POST请求
      
    
    
      
    用户名
    密码
    //login.php
    

    同步模式和异步模式

    同步:一个人在同一个时刻只能做一件事情。在执行一些耗时的操作(不需要看管)不去做别的事,只是等 待

    异步:在执行一些耗时的操作(不需要看管)去做别的事,而不是等待

    open 方法的第三个参数是 async (异步)可以传入一个布尔值,默认为 true

    //同步、异步涉及到这段代码执行时间的长短不一样:如在这段代码前后都用console.log输出,则异步会同时执行这2个输出,同步会出现愣等(阻塞)情况导致时间慢。可以用开始秒表console.time('秒表名')、结束秒表console.timeEnd('秒表名')来计时(计算中间代码执行的时候)
    
    var xhrAsync= new XMLHttpRequest()
    xhrAsync.open('GET', 'time.php', true)
    console.time('async')
    xhrAsync.send()
    console.log(xhrAsync.responseText)
    console.timeEnd('async')
    
    // 同步模式 ajax 操作会有楞等的情况
    // 区别在于 send 方法会不会出现等待情况
    // 同步模式下send方法会直接给我们结果。一定要注意事件注册时间问题:若有事件注册,会来不及,而异步则是发send信号时,就去注册事件。
    //不建议用同步模式,会降低用户体验。只有在封装时才有可能会使用
    var xhrSync= new XMLHttpRequest()
    xhrSync.open('GET', 'time.php', true)
    console.time('sync')
    xhrSync.send()
    console.log(xhrSync.responseText)
    console.timeEnd('sync')
    

    响应的数据格式XML、JSON

    提问:如果希望服务端返回一个复杂数据,该如何处理?

    只要涉及到响应类型不同时,服务端应就要设置上合理的Content-Type。(告诉客户端;用上JQ等时,JQ会帮忙转换,若遇到不设置的JQ应对方法如$.getJSON)

    不管服务端采用XML还是JSON,本质上都是将数据返回给客户端

    • XML

    一种数据描述手段,老掉牙的东西。淘汰的原因:数据冗余太多

    用途:ajax里、或一些程序的配置文件里有用到

    XML也可以设样式,最早的网页就是用XML做的,如网易rss

    问题在于:如果你客户端发一个AJAX请求,服务端返回个XML,那客户端怎么去解析它?了解即可

    //xml.php
    //如果想返回XML,一定要设Content-Type。否则客户端会当HTML解析
    
    //XML的文档头
    
    <student>
        <name>张三name>
        <age>18age>
        <girl_friend>
            <name>李四name>
        girl_friend>
    student>
    
    //11-ajax-xml.html:AJAX请求XML格式的数据
    	var xhr = new XMLHttpRequest()
        xhr.open('GET', 'xml.php')
        xhr.send()
        xhr.onreadystatechange = function () {
          if (this.readyState !== 4) return
          //console.log(this.responseText)到这,能成功打印,问题是:怎么把xml中的数据解析拿出来。应对:只打印this时this还包含了responsexml:document
          // this.responseXML 专门用于获取服务端返回的 XML 数据,操作方式就是通过 DOM 的方式操作
          // 但是需要服务端响应头中的 Content-Type 必须是 application/xml。否则Content-Type会被客户端认为是text/html,则responsexml:null
          console.log(this.responseXML.documentElement.children[0].innerHTML)//张三      console.log(this.responseXML.documentElement.getElementsByTagName('name')[0])//张三
        }
    
    • JSON

    一种数据描述手段,类似于 JavaScript 字面量方式

    服务端采用 JSON 格式返回数据,客户端按照 JSON 格式解析数据。

    {
     "name": "张三",
     "age": 18,
     "girl_friend": {
    	"name": "李四"
     }
    }
    

    不管是 JSON 也好,还是 XML,只是在 AJAX 请求过程中用到,并不代表它们之间有必然的联系,它们只是 数据协议罢了

    客户端如何处理服务端响应的数据(处理响应数据渲染)

    学习目的:一个(有结构的)数据怎么从服务端拿回来:通过json方式。拿回来过后,在客户端这边处理很麻烦(过往:大量DOM或拼接),可以拿模板引擎去处理。

    以往操作:代码方式

    动态渲染数据到表格中案例:

    //12-ajax-table.html
    
      

    到了以后学到agule、vue就不会写这些东西了:大量DOM操作、拼接,连整个document都不用再写了

    模板方式

    一个(有结构的)数据怎么从服务端拿回来:通过json方式。拿回来过后,在客户端这边处理很麻烦,可以拿模板引擎去处理。

    采用技术:模板引擎。目的:更容易将数据渲染到html中

    核心:给我个数据,给我个模板,我根据一个规则帮你执行完得到一个结果

    实际并不陌生,php就好像个大号的模板引擎(与HTML混编才不觉得恶心),在服务端做的。

    客户端开发技术:也有个模板引擎——如artTemplate:https://aui.github.io/art-template/,国内的,最重要的一点:有中文文档(切换语言再点文档按钮)。自己去熟悉

    ​ 用法:把一个js文件引到页面当中,先写一个模板,写完模板之后准备一个数据,把数据跟模板同时扔到一个js文件所提供的一个方式里,就可以出结果了。

    ​ 中文文档-安装-在浏览器中实时编译-下载js-保存起来。然后到页面当中去引用这个文件,再用script的方式去定义模板。

    学东西的套路:照着手册一步步学

    常见的流行模板引擎:Jade/pug、Handlebars、ejs、Nunjucks。用法都一样,看的就是一个套路。(一般选用一个东西:根据它在github上的活跃程度、协议、加星程度、关注程度一系列数字去决定)

    参考:github.com/tj/consolidate.js#supported-template-engines(做node开发一个比较出名的作者做的常见模板引擎列表)

    使用模板引擎动态渲染表格实例

    1.选择一个模板引擎 https:github.com/tj/consolidate.js#supported-template-engines
    2.下载模板引擎JS文件
    3.引入到页面中
    4.准备一个模板
    5.准备一个数据
    6.通过模板引擎的JS提供的一个函数将模板和数据整合得到渲染结果HTML
    7.将渲染结果HTML 设置到 默认元素的 innerHTML 中

    注意:
    一、script 标签的特点是(模板引擎盯上了)
    1. innerHTML 永远不会显示在界面上
        2. 如果 type 不等于 text/javascript 的话,内部的内容不会作为 JavaScript 执行
    

    二、为什么不在JS变量中写模板?
    如果将模板写到JS中,维护不方便,不能换行,没有着色。如var tmpl = ‘{{if user}}

    {{user.name}}

    {{/if}}’
    三、为什么使用script标记写模板?
    script不会显示在界面

    
      

    XMLHttpRequest的兼容方案(面试)

    XMLHttpRequest 在老版本浏览器(IE5/6)中有兼容问题,可以通过另外一种方式代替(三元表达式判断一下支持、不支持)

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

    面试时考ajax时一定把这点写上,ajax考的就是这点

    AJAX的封装

    封装的套路:

    1. 写一个相对比较完善的用例

    2. 写一个空函数,没有形参,将刚刚的用例直接作为函数的函数体

    3. 根据使用过程中的需求抽象参数

    接下来开始进行封装

    1.写一个相对比较完善的用例

    var xhr = new XMLHttpRequest()
    xhr.open('GET','time.php')
    xhr.send(null)
    xhr.onreadstatechange = function () {
    	if (this.readyState !==4) return
    	console.log(this.responseText)
    }
    

    2.写一个空函数funcion ajax(){},没有形参,将刚刚的用例直接作为函数的函数体。到这就能调用ajax()了,但到这只是最基本的封装,差得还很远

    funcion ajax(){
    	var xhr = new XMLHttpRequest()
        xhr.open('GET','time.php')
        xhr.send(null)
        xhr.onreadstatechange = function () {
            if (this.readyState !==4) return
            console.log(this.responseText)
        }
    }
    //调用
    ajax()
    

    3.根据使用过程中的**需求 **抽象参数

    版本1:

    根据不同的需求:open的请求方式、地址会发生变化;当请求方式为POST,send的参数会变化;所以要把它们从写死的状态换成一个传入的参数。method(问题:当open请求方式是小写也能传入,我们的封装代码却不能正常执行,所以要转大写。), url, params(问题1:当传入第三个参数时,下面的调用1也要有对应的参数null。但调用1要也要写参数麻烦->最好有个默认值var params=params||null。问题2:params传入urlencoded型数据时需要告诉服务端Content-Type,要设置响应头->必须要有个设置操作xhr.setRequestHeader(‘Content-Type’, ‘application/x-www-form-urlencoded’)。问题2.1:不为POST请求时,不设置响应头->给个判断,但到这里发现send参数的设置就不大好了,所以优化掉,进入版本2)

    funcion ajax(method, url, params){
    	method = method.toUpperCase()
    	var xhr = new XMLHttpRequest()
        xhr.open(method, url)
        if (method === 'POST') {
        	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        }
        
        var params = params || null
        
        xhr.send(params)
        xhr.onreadstatechange = function () {
            if (this.readyState !==4) return
            console.log(this.responseText)
        }
    }
    //调用
    ajax('GET', 'time.php')
    ajax('POST', 'add.php', 'key1=value1&key2=value2')
    ajax('get', 'time.php')
    ajax('post', 'add.php', 'key1=value1&key2=value2')
    

    版本2:也是基本封装,只是相对完善

    参数params的问题1.1:params设置优化——如果不是POST请求就永远是null,是POST就取params;如果是get请求,除了null也有可能传参数(如id这参数放url里),所以要对url进行处理。

    funcion ajax(method, url, params){
    	method = method.toUpperCase()
    	var xhr = new XMLHttpRequest()
    	
    	if (method === 'GET') {
          url += '?' + params
        }
    	
        xhr.open(method, url)
        
        var data = null
        if (method === 'POST') {
        	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        	data = params
        }
        
        xhr.send(data)
        xhr.onreadstatechange = function () {
            if (this.readyState !==4) return
            console.log(this.responseText)
        }
    }
    //调用
    ajax('GET', 'time.php','id=1')
    ajax('POST', 'add.php', 'key1=value1&key2=value2')
    

    版本3:

    问题1:上个版本中params传入参数是像key1=value1&key2=value2这样结构,看着太恶心写起来也麻烦,所以改进:希望params传入的是个对象而不是字符串。而调用时直接传入对象查看Network请求不行,所以要把params(的对象实参)转成key1=value1&key2=value2这样格式

    funcion ajax(method, url, params){
    	method = method.toUpperCase()
    	var xhr = new XMLHttpRequest()
    	
    	if (typeof params === 'object') {
    		var tempArr = []
    		//遍历对象的每一个键
    		for (var key in params) {
    		   //拿到它每一个值
               var value = params[key]
               //把每对键值拼接起来:tempArr => [ 'key1=value1', 'key2=value2' ]
               tempArr.push(key + '=' + value)
               //要得到格式:params => 'key1=value1&key2=value2'。所以要join连接起来
               params = tempArr.join('&')
            }
    	}
    	
    	if (method === 'GET') {
          url += '?' + params
        }
    	
        xhr.open(method, url)
        
        var data = null
        if (method === 'POST') {
        	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        	data = params
        }
        
        xhr.send(data)
        xhr.onreadstatechange = function () {
            if (this.readyState !==4) return
            console.log(this.responseText)
        }
    }
    //调用
    ajax('GET', 'time.php',{ id: 1 })
    ajax('POST', 'add.php', { key1: 'value1',key2: 'value2' })
    

    版本4:

    核心问题:不应该在封装的函数中主观地处理响应结果(把我们之前写的console.log()去掉,alert()也不能写),所以要有返回值。

    但无法在内部包含的函数中通过return 给外部函数的调用返回结果,所以要用作用链的方式拿到返回值:

    function foo () {
    var res
    function bar () {
    res = 123
    }
    bar()
    return res
    }

    但又引起来了新的问题:代码执行顺序:

    funcion ajax(method, url, params){
    	var res = null
    
    	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)
        
        var data = null
        if (method === 'POST') {
        	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        	data = params
        }
        
        xhr.send(data)
        xhr.onreadstatechange = function () {
            if (this.readyState !==4) return
            //不应该在封装的函数中主观的处理响应结果
         	//console.log(this.responseText)
            //无法在内部包含的函数中通过 return 给外部函数的调用返回结果
            // return this.responseText
    		// 由于异步模式下(这儿代码刚开始执行外部代码比这儿早且仍在执行) 这里的代码最后执行 所以不可能在外部通过返回值的方式返回数据
    		//用同步:oepn参数加个false,且事件移到send前。但强烈拒绝使用,所以到这代码就写不下去了。就要用到js的一个操作:委托(回调)
    		res = this.responseText
        }
    	return res
    }
    //调用
    ajax('GET', 'time.php',{ id: 1 })
    ajax('POST', 'add.php', { key1: 'value1',key2: 'value2' })
    

    版本5:最终版,未成形

    由于版本4的方向错误,所以回到版本4前,回到开始的问题是:不应该在封装的函数中主观的处理响应结果(即封装者不能去主观地处理调用者的事情,如console.log就不行)->版本4用返回值的做法不行,无法返回->怎么样不主观:你告诉我该做什么事情。用委托(js中叫回调):即调用者委托(让)封装者拿到数据后,再告诉封装者拿着数据去做什么事情->参数4:done

    // 封装者
    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)
        }
    
       // 调用者,这儿分步骤好理解
       var onDone = function (res) {
         console.log('hahahahaha')
         console.log('hohohohoho')
         console.log(res)
         console.log('做完了')
       }
    
       ajax('get', 'time.php', {}, onDone)
       //实际调用写
       ajax('get', 'time.php', {}, function (res) {
         console.log('hahahahaha')
         console.log('hohohohoho')
         console.log(res)
         console.log('做完了')
       })
    

    最终版

     /**  * 发送一个 AJAX 请求  * @param  {String}   method 请求方法  * @param  {String}   url    请求地址  * @param  {Object}   params 请求参数  * @param  {Function} done   请求完成过后需要做的事情(委托/回调)  */ function ajax (method, url, params, done) {   // 统一转换为大写便于后续判断   method = method.toUpperCase()   
     
     // 对象形式的参数转换为 urlencoded 格式   var pairs = []   
     for (var key in params) {     pairs.push(key + '=' + params[key])   
     }   
     var querystring = pairs.join('&')     
     	var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new  ActiveXObject('Microsoft.XMLHTTP')  
     		 xhr.addEventListener('readystatechange', function () {     
     	if (this.readyState !== 4) return      
     	// 尝试通过 JSON 格式解析响应体     
     	try {       
     		done(JSON.parse(this.responseText))     } catch (e) {       
     		done(this.responseText)     
     		}   
     	})     
     
     // 如果是 GET 请求就设置 URL 地址 问号参数   if (method === 'GET') {     url += '?' + querystring   
      }  
      
      xhr.open(method, url)  
      
      // 如果是 POST 请求就设置请求体   
      var data = null  
      if (method === 'POST') {     xhr.setRequestHeader('Content‐Type', 'application/x‐www‐form‐urlencoded')     data = querystring   
      }   
      xhr.send(data) 
    } 
    

    调用

    ajax('get', './get.php', { id: 123 }, function (data) {   
    	console.log(data) 
    })  
    ajax('post', './post.php', { foo: 'posted data' }, function (data) {   
    	console.log(data)
    })
    

    但一般我们都是使用别人封装好的,功能比较完善。

    jQuery 中有一套专门针对 AJAX 的封装,功能十分完善,经常使用,需要着重注意。1.几版兼容性最好,3.几最新。简单使用可看code,详细看下面官方手册。

    这些(原生、封装)API记住使用过程就好。

    参考:
    http://www.jquery123.com/category/ajax/

    http://www.w3school.com.cn/jquery/jquery_ref_ajax.asp

    回调

    简单实例

    
    

    js核心:回调

    回调的嵌套情况

    以后的js代码可能会出现这种情况:需要请求一个a地址,拿到响应的结果之后再去请求b地址->那么代码中会出现回调函数中可能会再出现ajax,如下面这种恶心的结构:

    console.log(1)//第1次执行
    ajax('time.php', function (res) {
      console.log(3)//第3次执行
      ajax('time.php', function (res) {
        ajax('time.php', function (res) {
          ajax('time.php', function (res) {
            ajax('time.php', function (res) {
              ajax('time.php', function (res) {
    
              })
            })
          })
        })
      })
      console.log(4)//第4次执行
    })
    console.log(2)//第2次执行
    

    那么可能会有这样的方案:(then然后)

    ajax('time.php')
      .then(function (res) {
        return ajax('time.php')
      })
      .then(function (res) {
        return ajax('time.php')
      })
      .then(function (res) {
        return ajax('time.php')
      })
      .then(function (res) {
        return ajax('time.php')
      })
    //注意回调黑洞(死循环)问题,如:
    function load () {
      ajax('time.php', load)
    }
    

    JQ的AJAX

    . a j a x 、 .ajax、 .ajax.get、 . p o s t 、 .post、 .post.getJSON、load方法、$.getScript

    $.ajax('./time.php', {
      type: 'post', // method 请求方法
      success: function (res) {
        // res => 拿到的只是响应体
        console.log(res)
      }
    })
    

    JQ的load方法(局部刷新)

    实现页面的 局部刷新 效果,能节省请求,如不用再请求css、js等文件

    在jQuery封装的ajax下。跟ajax关系不大,但是用ajax实现的

    在某一个div或元素中面去载入另外一个页面的另外一个区域

    实例:局部刷新(加载)、在页面上做一些加载提示

    如何做得更好?切换时希望知道在加载过程中。看下栏

    全局事件处理函数

    全局事件处理函数作用:如将所有ajax请求的公共操作注册成ajax的全局事件,让它只要发ajax请求就去自动帮我们执行

    参考:http://www.jquery123.com/category/ajax/global-ajax-event-handlers/

    
    

    最常见的加载进度方式

    配合NProgress显示加载进度。

    github、知乎等都有在用:只在网址栏下面走进度条——请求响应,响应结果完就一下过了。

    要想用:有个NProgress库——使用其中的js、css文件。且最好都在页头引入。

    跨域

    跨域实际跟ajax无关。

    过去不支持不同源地址之前的ajax请求,现在支持了(存在兼容、需要服务端进行一些配置),但我们仍要了解。

    同源策略是浏览器的一种安全策略。所谓同源是指域名,协议,端口完全相同,只有同源的地址才可以相互通过 AJAX 的方式请求。

    同源或者不同源说的是两个地址之间的关系,不同源地址之间请求我们称之为跨域请求

    之前学的能自动发送请求的方式:img、link、script、iframe。表单、a连接要点或做些操作才能

    iframe:浏览器自带的功能,在页面中挖一个坑去载入另一个页面,跟load类似(而load是通过ajax、自己代码实现的)。

    如何解决:通过JSONP技巧

    JSONP技巧

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

    原理:客户端通过 script 标签请求服务端的 一个动态网页(如php文件)。该文件返回一段 能调用一个函数的JS。该JS作用:调用我们事先定义好的一个函数,从而将服务端想要给客户端发过去的数据发送给客户端

    注意:1. JSONP 需要服务端配合,服务端按照客户端的要求返回一段 JavaScript 调用客户端的函数
    2.只能发送 GET 请求
    3.jQuery 中使用 JSONP 就是将 dataType 设置为 jsonp
    4.其他常见的 AJAX 封装 库:Axios

    实例:server.php、client.html(较多)

    //server.php
    
    //client.html JSONP基本使用
    //在客户端发起跨域请求
    
    

    JSONP封装

    有了个用例之后,开始封装。1.封装的参数跟$.get一样 2.url替换 3.callback替换 4.get会传入如id等参数——params,解析下。

    function jsonp (url, params, callback) {
      var funcName = 'jsonp_' + Date.now() + Math.random().toString().substr(2, 5)
      //解析url传入的参数
      if (typeof params === 'object') {
        var tempArr = []
        for (var key in params) {
          var value = params[key]
          tempArr.push(key + '=' + value)
        }
        params = tempArr.join('&')
      }
    
    
      var script = document.createElement('script')
      //2的
      script.src = url + '?' + params + '&callback=' + funcName
      document.body.appendChild(script)
    
      window[funcName] = function (data) {
        //3的
        callback(data)
        //每次用完了就删除随机函数,然后移除scirpt标记
        delete window[funcName]    	document.body.removeChild(script)
      }
    }
    
    //调用
    jsonp('http://localhost/jsonp/server.php', { id: 123 }, function (res) {
      console.log(res)
    })
    
    jsonp('http://localhost/jsonp/server.php', { id: 123 }, function (res) {
      console.log(res)
    })
    

    JQ也有封装JSONP,使用跟$.get类似

    基本使用如:

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

    总结:因为 XMLHttpRequest 这个对象不支持对不同源地址之间的跨域请求,所以我们找到了script标记

    面试回答:它实际上是借用了script标记可以发送不同源地址请求的一个特性去完成的一个跨域请求

    AJAX跨域(CORS)

    Cross Origin Resource Share,跨域资源共享

    现在支持不同源地址之前的ajax请求,不过存在兼容问题、需要进行一些服务端的配置。

    之前有个问题:涉及到AJAX操作的页面”不能“通过文件方式访问——就是跨域问题,现在也可以解决了。

    1.服务端只需一行代码搞定:允许跨域请求header(‘Access-Control-Allow-Origin: *’);

    2.客户端不用做任何修改

    
      
    

    你可能感兴趣的:(前端网络编程:AJAX)