WEB前端Ajax

Ajax初识

Web 程序最初的目的就是将信息(数据)放到公共的服务器,让所有网络用户都可以通过浏览器访问

什么是Ajax

  • 在此之前,我们可以通过以下几种方式让浏览器发出对服务端的请求,获得服务端的数据:
    • 地址栏输入地址,回车,刷新
    • 特定元素的 href 或 src 属性
    • 表单提交
  • 这些方案都是我们无法通过或者很难通过代码的方式进行编程(对服务端发出请求并且接受服务端返回的响应),如果我们可以通过JavaScript直接发送网络请求,那么Web的可能就会更多,随之能够实现的功能也会更多,至少不再是单机游戏
  • AJAX(Asynchronous JavaScript and XML),最早出现在 2005 年的 Google Suggest。Ajax是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。是在浏览器端进行网络编程(发送请求、接收响应)的技术方案,它使我们可以通过js直接获取服务端最新的内容而不必重新加载页面。让 Web 更能接近桌面应用的用户体验。
  • 说白了,AJAX就是浏览器提供的一套API,可以通过JavaScript调用,从而实现通过代码控制请求与响应,实现网络编程。

HelloWorld

// 1.xhr类似于浏览器的作用(发送请求接收响应)
const xhr = new XMLHttpRequest();
// 2.打开浏览器 输入网址
xhr.open('GET', 'http://www.baidu.com');
// 3.敲回车键 开始请求
xhr.send();
// 4.等待响应:客户端不知服务端何时才能返回我们需要的响应 -> Ajax API采用事件机制
xhr.onreadystatechange = function() {
    // 这个事件并不是只在响应时触发,XHR状态改变就触发 -> 触发3次
    if(this.readyState !== 4) return;
    // 5.看结果,xhr的responseText获取响应内容(响应体)
    console.log(this.responseText);	
};
  • 涉及到Ajax操作的页面不能使用文件(协议)的方式访问。

  • 兼容方案:XMLHttpRequest 在老版本浏览器(IE5/6)中有兼容问题,可以通过另外一种方式代替

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

同步与异步

在这里插入图片描述

在这里插入图片描述

  • 同步:一个人在同一个时刻只能做一件事情,在执行一些耗时的操作不去做别的事,只是等待

  • 异步:在执行一些耗时的操作去做别的事,而不是等待

  • xhr.open()第3个参数可传入一个bool值,作用是设置此次请求是否采用异步方式,默认为异步true

    console.log('Pre Ajax...');
    var xhr = new XMLHttpRequest();
    // 默认第3个参数为true,异步执行
    xhr.open('GET', '/user/list', true);
    xhr.send()
    xhr.onreadystatechange = function () {
        if (this.readyState !== 4) return;
        // 这里的代码最后执行
        console.log('Response Done...');
    }
    console.log('Post Ajax...');
    
  • 如果采用同步方式,代码会卡死在xhr.send()这一步

    console.log('Pre Ajax...');
    var xhr = new XMLHttpRequest();
    // 同步方式,第3个参数传入false
    xhr.open('GET', '/user/list', false);
    // 【注意】同步需要先注册事件再调用send,否则readystatechange无法触发
    xhr.onreadystatechange = function () {
        if (this.readyState !== 4) return;
        // 这里的代码最后执行
        console.log('Response Done...');
    }
    xhr.send();
    console.log('Post Ajax...');
    

readyState

  • 由于 readystatechange 事件是在 xhr 对象状态变化时触发(不单是在得到响应时),也就意味着这个事件会被触发多次,所以我们有必要了解每一个状态值代表的含义

    readState状态描述说明
    0UNSENT代理(XHR)被创建,但尚未调用 open() 方法
    1OPENEDopen() 方法已经被调用,建立了连接。
    2HEADERS_RECEIVEDsend() 方法已经被调用,并且已经可以获取状态行和响应头
    3LOADING响应体下载中, responseText 属性可能已经包含部分数据
    4DONE响应体下载完成,可以直接使用 responseText
  • 时间轴:

    在这里插入图片描述

  • 等待响应时,如果需要捕获状态1的变化,需注意代码执行顺序不要出现来不及的情况)

  • 一般情况下,加载完成的逻辑我们只处理readyState=4的情况,此时onreadystatechange事件可以简写成H5当中的onload事件

    xhr.onload = function () {
        console.log(this.responseText);
    }
    

遵循http

概述

  • 本质上 XMLHttpRequest 就是 JavaScript 在 Web 平台中发送 HTTP 请求的手段,所以我们发送出去的请求任然是HTTP 请求,同样符合 HTTP 约定的格式
const xhr = new XMLHttpRequest();
// 设置请求报文的请求行
xhr.open('GET', './mock/user.json');
// 设置请求头
xhr.setRequestHeader('Accept', 'text/plain');
// 设置请求体
xhr.send(null);
xhr.onreadystatechange = function () {
    if (this.readyState !== 4) return;
    // 获取响应状态码
    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形式,了解即可
}

GET请求

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

    const xhr = new XMLHttpRequest();
    // GET 请求传递参数通常使用的是问号传参
    xhr.open('GET', '/user/get?id=3');
    // 一般GET请求无需设置响应体,传null或者不传
    xhr.send(null);
    xhr.onreadystatechange = function () {
        if (this.readyState !== 4) return;
        console.log(this.responseText);
    }
    

POST请求

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

    const xhr = new XMLHttpRequest();
    // open方法的第一个参数就是设置method
    xhr.open('POST', '/user/add');
    // 设置请求头中的Content‐Type为 application/x‐www‐form‐urlencoded
    // 标识此次请求的请求体格式为 urlencoded 以便于服务端接收数据
    xhr.setRequestHeader('Content‐Type', 'application/x‐www‐form‐urlencoded');
    // 需要提交的数据通过send方法传递
    // 格式: key1=value1&key2=value2
    xhr.send('account=90217&password=123abc');
    xhr.onreadystatechange = function () {
        if (this.readyState !== 4) return;
        console.log(this.responseText);
    }
    
  • 【注意】一旦请求体是urlencoded格式,一定要在请求头设置Content-Type'application/x-www-form-urlencoded'

响应数据格式

XML

  • 一种数据描述手段,老掉牙的东西,简单演示一下,不在这里浪费时间,基本现在的项目不用了。
  • 淘汰的原因:数据冗余太多

JSON

  • 也是一种数据描述手段,类似于 JavaScript 字面量方式。服务端采用 JSON 格式返回数据,客户端按照 JSON 格式解析数据。
  • 不管是 JSON 也好,还是 XML,只是在 AJAX 请求过程中用到,并不代表它们之间有必然的联系,它们只是数据协议罢了

response和responseText

  • this.response:获取到的结果会根据this.responseType的变化而变化
  • this.responseText:获取字符串形式的响应体

模板引擎

处理响应数据渲染

  • 模板引擎实际上就是一个 API,模板引擎有很多种,使用方式大同小异,目的为了可以更容易的将数据渲染到HTML

  • 使用模板引擎的步骤:

    1. 选择一个模板引擎
    2. 下载模板引擎JS文件
    3. 引入到页面中:<script src="template-web.js"></script>
    4. 准备一个模板:
      <script id="tmpl" type="text/x-art-template">{{模板内容}}</script>
    5. 准备一个数据:let data = {userList:res.data};
    6. 过模板引擎的JS提供的一个函数将模板和数据整合得到渲染结果HTML:
      var html = template('tmpl', data);
    7. 将渲染结果的HTML设置到默认元素的innerHTML中:
      document.getElementById('tbody').innerHTML = html;
  • 注意:

    • 为什么不在JS变量中写模板 -> 如果将模板写到JS中,维护不方便,不能换行,没有着色
    • 为什么使用script标记 -> script不会显示在界面。

Ajax封装

回调函数

凡是需要得到一个函数内部异步操作的结果,必须使用回调函数,如以下情况:

  • setTimeout, readFile, writeFile, ajax,例子如下:

    function add(x, y, callback) {
        // 异步操作(等待1s)
        setTimeout(function () {		
            let ret = x + y;
            callback(ret);
        }, 1000);
    }
    add(10, 20, function (ret) {
        console.log(ret);
    });
    

封装Ajax的get方法

function get(url, callback) {
	const xhr = new XMLHttpRequest();
	xhr.onload = function () {
		callback(oReq.responseText);	//得到响应的Text
	};
	xhr.open("get", url, true);
	xhr.send();
}
get('data.json', function (data) {
	console.log(data);
});

委托

  • 函数就可以理解为一个想要做的事情,函数体中约定了这件事情做的过程,直到调用时才开始工作。

  • 将函数作为参数传递就像是将一个事情交给别人,这就是委托的概念

    在这里插入图片描述

    在这里插入图片描述

封装原生Ajax

/** 封装Ajax
* 发送一个 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

最基础封装

$.ajax({
	// 参数
	url: './get.php',		// 请求url
	type: 'get',			// 请求方式
	dataType: 'json',		// 响应的数据格式
	data: { id: 1 },		// 参数
	// 回调函数
	beforeSend: function (xhr) {// 在请求发生(open和send)之前执行的函数
		console.log('before send');
	},
	success: function (data) {		// 请求成功(200)执行的函数
		console.log(data);
	},
	error: function (err) {		// 请求失败(非200)的函数
		console.log(err);
	},
	complete: function () {			// 只要请求完后就执行的函数
		console.log('request completed');
	}
});

参数:

  • url:请求地址

  • type:请求方法,默认为 get

  • dataType:服务端响应数据类型

  • contentType:请求体内容类型,默认 application/x-www-form-urlencoded

  • data:需要传递到服务端的数据,如果 GET 则通过 URL 传递,如果 POST 则通过请求体传递

  • timeout:请求超时时间

  • beforeSend:请求发起之前触发,可以设置请求头,例如:

    $.ajax({
        url: '/user.php', 
        type: 'post',
        data: {id:1},
        success: function(res) {
       		console.log(res);
    	},
        beforeSend: function (xhr) {
            // 发送ajax请求前在Header中携带jwt的tokern认证信息,服务端做校验
    		xhr.setRequestHeader("Authorization", localStorage.token);
    	},
    })
    
  • success:请求成功之后触发(响应状态码 200)

  • error:请求失败触发

  • complete:请求完成触发(不管成功与否)

针对http的进一步封装

  • GET请求$.get(url,[data],[callback],[type])

    $.get('/user.php', {id:1}, function(res) {
        console.log(res);
    },'json');
    
  • POST请求$.post(url,[data],[callback],[type])

    $.post('/user.php', {id:1}, function(res) {
        console.log(res);
    })
    
  • 转化为json的GET请求:

    // 无论客户端是什么数据类型,接收时都转换为json类型
    $.getJSON('/user.php', {id:1}, function(res) {
        console.log(res);
    });
    
  • **缺点:**无法自定义很多回调函数(error,complete,beforeSend…) -> 只能调用底层的$.ajax

  • 自学:$(selector).load()$.getScript()

Ajax执行时机监听

  • 请求开始:

    // 只要有Ajax请求发生,就会执行
    $(document).ajaxStart(function() {
        $('.loading').fadeIn();
        NProgress.start();
        console.log('Ajax start...');
    });
    
  • 请求结束:

    // 只要有Ajax请求结束,就会执行
    $(document).ajaxStop(function() {
        $('.loading').fadeOut();
        NProgress.done();
        console.log('Ajax done...');
    });
    

跨域问题及解决

同源及跨域

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

  • 同源策略: 不同源地址之间,默认不能相互发送Ajax请求

  • 什么是同源?例如 http://www.example.com/detail.html 与一下地址对比

    对比地址是否同源原因
    http://api.example.com/detail.html不同源域名不同
    https://www.example.com/detail.html不同源协议不同
    http://www.example.com:8080/detail.html不同源端口不同
    http://api.example.com:8080/detail.html不同源域名、端口不同
    https://api.example.com/detail.html不同源协议、域名不同
    https://www.example.com:8080/detail.html不同源端口、协议不同
    http://www.example.com/other.html同源只是目录不同
  • **跨域请求:**同源或者不同源说的是两个地址之间的关系,不同源地址之间请求就称之为跨域请求。不同源地址之间如果需要相互请求,必须服务端和客户端配合才能完成。

跨域解决方案

  • 注意:
    • 其实Ajax可以请求非同源的数据,也能响应,但是浏览器的策略不支持接受数据。
    • link标签必须有rel属性才能发送请求。
    • script标签只能发get请求,不能发post请求,所以传参只能是url中的?之后。
  • img标签:可以发送不同源地址之间的请求,但无法拿到响应结果
    config img = new Image();
    img.src = 'https://www.bjtu.edu.cn/images/img2019/logo_01.png';
    
  • link标签:可以发送不同源地址之间的请求,但无法拿到响应结果
    const link = document.createElement('link');
    // link标签必须写rel属性才能发请求
    link.rel = 'stylesheet';
    link.href = 'http://locally.uieee.com/categories';
    document.body.appendChild(link);
    
  • script标签:可以发送不同源地址之间的请求,无法拿到响应结果。但可借助于能够作为JS执行
    const script = document.createElement('script');
    script.src = 'http://localhost/time2.php';
    // 开始发起请求
    document.body.appendChild(script);
    // 相当于请求的回调
    function foo(res){
        console.log(res);
    }
    

jsonp

  • 因为XMLHttpRequest 无法发送不同源地址之间的跨域请求,所以可借助script标签进行跨域请求,这种方式称为jsonp(JSON with Padding)。其原理就是在客户端借助 script 标签请求服务端的一个动态网页(php 文件),服务端的这个动态网页返回一段带有函数调用的 JavaScript 全局函数调用的脚本,将原本需要返回给客户端的数据传递进去。

  • 客户端:http://www.zce.me/users-list.html

    <script src="http://api.zce.me/users.php?callback=foo"></script>
    
  • 服务端:http://api.zce.me/users.php?callback=foo

    foo(['我', '是', '你', '原', '本', '需', '要', '的', '数', '据'])
    
  • 缺点:1. JSONP 需要服务端配合,服务端按照客户端的要求返回一段 JavaScript 调用客户端的函数;2.只能发送 GET 请求

  • jQuery 中使用 jsonp 就是将 dataType 设置为 'jsonp'

    // jQuery的封装
    $.ajax({
        url: 'http://localhost/jsonp/server.php',
        // 指定此参数,直接发送jsonp请求
        dataType: 'jsonp',
        success: function (res) {
            console.log(res);
        }
    });
    

CORS

  • Cross Origin Resource Share,跨域资源共享,一行代码解决跨域请求。这种方案无需客户端作出任何变化、不用改代码,只是在被请求的服务端响应时添加Access-Control-Allow-Origin响应头,表示这个资源是否允许指定域请求

  • **语法:**服务端php文件

    // 允许远端访问
    header('Access‐Control‐Allow‐Origin: *');
    

XMLHttpRequest 2.0

更易用,更强大;暂作了解,无需着重看待

Ajax渲染

  • 优点:性能更好,提高用户体验
  • 缺点:不利于seo,搜索引擎收录的网页源码。
  • 解决方案:同时会去更改地址栏,注意地址栏在服务端一定有对应的页面

onload / onprogress

const xhr = new XMLHttpRequest();
xhr.open('GET', './time.php');
xhr.send();
xhr.onload = function () {
    // onload readyState === 4
    console.log(this.readyState);
}
xhr.onprogress = function () {
    // onload readyState === 3
    console.log(this.readyState);
}

FormData

  • 以前 AJAX 只能提交字符串,现在可以提交二进制数据
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值