一、概述

1.1 发展历程

在开始之前先来看一下Ajax的工作原理吧,如下图所示:

Ajax全称Asynchronous javascript and xml(异步 JavaScript 和 XML)的简写,是指围绕【由js向服务器发起http请求】这个功能而开发的一套完整的做法

Ajax中的异步:可以异步地向服务器发送请求,在等待响应的过程中,不会阻塞当前页面,浏览器可以做自己的事,直到成功获取响应后,浏览器才开始处理响应数据

XML:可扩展标记语言,是前后端数据通讯时传输数据的一种格式

XML 现在已经不怎么用了,现在比较常用的是 JSON

Ajax 其实就是浏览器与服务器之间的一种异步通信方式

使用 Ajax 可以在不重新加载整个页面的情况下,对页面的某部分进行更新

补充:Ajax可以发出同步请求(只能按顺序一步一步一个一个执行),也可以发出异步请求(可以多个任务同时进行,互不干扰),但大多数情况下指的是异步请求,因为同步的Ajax请求对浏览器会产生“阻塞效应”

发展历程:

  • 99年Microsoft公司第一次在IE5.0中引入此功能
  • 04年Gmail发布、05年Google Map发布时此功能才被重视
  • 05年2月Ajax正式提出
  • 06年W3C发布其对应的国际化标准

概述:Ajax模块在处理网络请求时包括以下四个步骤:

  • 1.创建xhr对象
  • 2.构建xhr的属性和方法
  • 3.通过xhr对象发送HTTP请求
  • 4.通过xhr对象的方法接收服务器回传的数据
        // 1. 创建 xhr 对象const xhr = new XMLHttpRequest();// 2. 准备发送请求/*接收三个参数:第一个参数:请求方式,例如 GET POST PUT DELETE第二个参数:请求地址URL,例如 https://www.baidu.com第三个参数:同步请求还是异步请求(默认异步请求)即 true调用 open 并不会真正发送请求,而只是做好发送请求的准备工作*/xhr.open();// 3. 调用 send() 正式发送请求/*在发送请求的时候可以通过send携带数据,但是需要注意:send 的参数是通过请求体来携带的数据POST 主要是通过请求体携带数据的GET 不能通过请求体携带,只能通过请求头携带即在URL地址中携带如果 open() 中的第一个参数为 GET,那么 send() 中就不能携带任何数据,只能为空 send() 或者 send(null)*/xhr.send();// 4.监听状态的变化(监听事件)// 当获取到响应后,会触发 xhr 对象的 readystatechange 事件,可以在该事件中对响应进行处理/*一共有 5 种状态:0:初始化。尚未调用 open()1:启动。已经调用 open(),但尚未调用 send()2:发送。已经调用 send(),但尚未接收到响应3:接收。已经接收到部分响应数据。4:完成。已经接收到全部响应数据,而且已经可以在浏览器中使用readystatechange 也可以配合 addEventListener 使用,但是 IE6~IE8 不支持 addEventListener由于兼容性,readystatechange 中不使用 this,而是直接使用 xhr由于兼容性原因,onreadystatechange 事件最好放在 open 之前*/// xhr.addEventListener('readystatechange', () => {}, fasle); // 或xhr.onreadystatechange = () => {// 监听 xhr 对象的请求状态 readState;服务器响应状态 status// if (xhr.readyState == 4 && xhr.status == 200) { // 或if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log("在这里就可以正常使用响应数据了");// console.log(xhr.responseText);}else {console.log("有错的情况下");}};

1.2  初识Ajax

在正式开始进入Ajax之前我们先来看如下几个示例:

例子1:

        const url = 'https://www.imooc.com/api/http/search/suggest?words=js';// 创建 xhr 对象const xhr = new XMLHttpRequest();// 监听状态变化xhr.onreadystatechange = ()=> {if(xhr.readyState !== 4){return;}if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304){console.log(xhr.responseText);console.log(typeof xhr.responseText);}};// 准备发送请求xhr.open('GET',url,true);// 发送请求xhr.send(null);

例子2:

 <script>var xhr = new XMLHttpRequest(); //创建xhr对象xhr.open('get', '01Ajax.php'); //通过xhr对象发出HTTP请求xhr.send();//构建xhr的属性和方法//通过xhr对象的方法接收服务器回传的数据xhr.onreadystatechange = function() {if (xhr.readyState == 4 && xhr.status == 200) {console.log(xhr.responseText);}}</script>
<?php$con=new PDO('mysql:host=localhost;port=3306;dbname=beixidb;charset=utf8','root','');$res= $con->query("select *  from news");if($res){$data=$res->fetchAll(PDO::FETCH_ASSOC);echo  json_encode($data);}
?>

例子3:

 <script>var xhr = new XMLHttpRequest(); //创建xhr对象xhr.open('get', '01Ajax.php?id=1'); //通过xhr对象发出HTTP请求xhr.send();//构建xhr的属性和方法//通过xhr对象的方法接收服务器回传的数据xhr.onreadystatechange = function() {// 监听 xhr 对象的请求状态 readState;服务器响应状态 statusif (xhr.readyState == 4 && xhr.status == 200) {console.log(xhr.responseText);}}</script>
<?php$id=$_GET['id'];$con=new PDO('mysql:host=localhost;port=3306;dbname=beixidb;charset=utf8','root','');$res= $con->query("select *  from news where id={$id}");if($res){$data=$res->fetchAll(PDO::FETCH_ASSOC);echo  json_encode($data);}
?>


二、xhr对象的常用属性和方法

XMLhttpRequest(简称 xhr),是浏览器提供的 Javascript 对象,通过它,可以请求服务器上的数据资源。之前所学的 jQuery 中的 Ajax 函数,就是基于 xhr 对象封装起来的

说明:XMLHttpRequest对象用来在【浏览器】与【服务器】之间传递数据,通俗上来说将此对象成为request请求对象、请求对象或请求

2.1 onreadystatechange属性--重点

描述:onreadystatechange属性指向一个回调函数。当页面的加载状态发生改变时,readyState属性就会跟随发生变化,而这时onreadystatechange属性所对应的回调函数就会自动被调用

语法:xhr.onreadystatechange = function(){};

注意:onreadystatechange事件也可以配合addEventListener使用,但是IE6~IE8不支持addEventListener,再者为了兼容性,onreadystatechange中不使用this,而是直接使用xhr

xhr.addEventListener('readystatechange', () => {}, fasle);

2.2 readyState属性

 描述:一个只读属性,用一个整数和对应的常量来表示XMLHttpRequest请求当前所处的状态,一般会在onreadystatechange事件的回调函数中,通过判断readyState属性的值,进而执行不同状态对应的函数

 属性值:

0 对应常量 unsnet 表示XMLHttpRequest实例已经生成,但是open()方法还没有被调用

1 对应常量opened 表示open()方法还没有被调用,仍然可以使用setRequestHeader()设定HTTP请求头

2 对应常量 headers_received 表示send()方法已经执行并且头信息和状态码已经收到

3 对应常量 loading 表示正在接收服务器传来的body部分的数据,如果responseType属性是text或者空字符串,responseText就会包含已经收到的部分信息

4 对应常量 done 表示服务器数据已经完全接收,或者本次接收已经失败了

语法:xhr.onreadystatechange = function() {

if(xhr.readyState == n) {

//执行对应的函数

}

}


2.3 status属性--重点

描述:表示本次请求所得到的HTTP状态码是一个整数

语法:if(xhr.readyState == n) {

if(xhr.status == 200) {

//通信成功

}

}

说明:

  • a.本属性是只读属性
  • b.本属性有以下可能值
  • 200 ,OK 访问正常;一般来说认为200就是通信成功的标志
  • 301 Moved Permanently 永久移动
  • 302 Moved temporarily 暂时移动
  • 304 Not Modified 未修改
  • 307 Temporary Redirect 暂时重定向
  • 401 Unauthorized 未授权
  • 403 Forbidden  禁止访问
  • 404 Not Found 未发现指定网址
  • 500 Internal Server Error 服务器发生错误

2.4 statusText属性

描述:表示服务器发送的状态提示,是一个只读字符串

语法:xhr.statusText

说明:不同于status属性,该属性返回状态码所对应的状态信息,比如 OK


2.5  responseType和response属性--重点

 <script>const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState != 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {// 文本形式的响应内容// responseText 只能在没有设置 responseType 或者 responseType = '' 或 'text' 的时候才能使用// console.log('responseText:', xhr.responseText);// 可以用来替代 responseTextconsole.log('response:', xhr.response);// console.log(JSON.parse(xhr.responseText));}};xhr.open('GET', url, true);//responseType设置响应的类型// xhr.responseType = '';// xhr.responseType = 'text';//xhr.responseType = 'json';xhr.send(null);
</script>

这是使用responseType时的结果:

这是使用response时的结果:

会发现它们好像并没有什么区别

但是如果这样做:

 <script>const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState != 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {// 文本形式的响应内容// responseText 只能在没有设置 responseType 或者 responseType = '' 或 'text' 的时候才能使用// console.log('responseText:', xhr.responseText);// 可以用来替代 responseTextconsole.log('response:', xhr.response);// console.log(JSON.parse(xhr.responseText));}};xhr.open('GET', url, true);//responseType设置响应的类型// xhr.responseType = '';// xhr.responseType = 'text';xhr.responseType = 'json';xhr.send(null);
</script>

如果这里再用 console.log('responseText:', xhr.responseText); 则会出现如下错误:

出现这个错误的原因其实很简单,因为在这个时候已经不再是字符串了,那么再像 console.log('responseText:', xhr.responseText); 写就会出现如上错误

正确的结果:

但是对于responseType和response属性 IE6~9 不支持,IE10 开始支持


2.6 responseText属性--重点

描述:返回从服务器接收到的字符串内容,该属性为只读。如果本次请求没有成功或者数据不完整,该属性就会等于null;如果服务器返回的数据格式是JSON,就可以使用responseText属性来进行数据解析

语法:xhr.responseText


2.7  请求超时timeout属性与超时监听ontimeout事件--重点

描述:timeout属性等于一个整数,用来设置当请求发出后等待接收响应的时间;ontimeout()方法则是当等待超时后,自动执行的回调方法

语法:

xhr.timeout = xxx;

xhr.ontimeout=function(){

console.error("The request for"+url地址+"timeout");

};

说明:timeout属性单位是毫秒,表示当请求发出后等待响应的时间;如果在设置的时间内没能接收到后台响应的内容,则认为请求超时(执行ontimeout)

补充:

  1. 如果该属性等于0,就表示没有事件限制
  2. IE6~7 不支持,IE8 开始支持

例子:

<button>发送请求</button>
<script>document.querySelector('button').onclick = function() {var xhr = new XMLHttpRequest();xhr.onreadystatechange = function() {if (xhr.readyState != 4) return;if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {console.log(xhr.responseText);}};xhr.timeout = 5000; //单位毫秒xhr.ontimeout = function() {console.log('连接超时,页面加载失败,请刷新页面');};xhr.open('get', '04timeout.php', true);xhr.send(null);}
</script>
<?php//timeout.phpsleep(10);//单位秒$success=array('msg'=>'ok');echo  json_encode($success);
?>

当点击“发送请求”按钮超过5s未得到响应时:


2.8 open()方法--重点

描述:表示要将请求发往某处,只是设置而不是真的发送

语法:xhr.open('请求类型','url地址','是否异步');

  1.  说明:
  2. 第一个参数是用get/post请求
  3. 第二个参数是用来设置请求发送到的url地址
  4. 三个参数是布尔值用来设置是否异步请求,默认false表示同步

补充:

目前我们的页面都采用localhost方式在本地主机上直接访问,因此url直接写出php文件的相对路径即可

而如果通过其他方式打开可能会引起js跨域问题,就会报错


2.9 withCredentials 属性

  • 指定使用 Ajax 发送请求时是否携带 Cookie
  • 使用 Ajax 发送请求,默认情况下,同域时,会携带 Cookie;跨域时,不会
  • 跨域携带 cookie的写法:xhr.withCredentials = true;
  • 最终能否成功跨域携带 Cookie,还要看服务器同不同意
  • IE6~9 不支持,IE10 开始支持

例子1:

<script>const url = './index.html'; //同域下const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState != 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}};xhr.open('GET', url, true);xhr.send(null);
</script>

设置cookie:

结果如下:

例子2:

 <script>const url = 'https://www.imooc.com/api/http/search/suggest?words=js'; //不同域下// const url = './index.html'; //同域下const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState != 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}};xhr.open('GET', url, true);xhr.withCredentials = true;xhr.send(null);
</script>


2.10 abort()--重点

终止当前请求

一般配合 abort 事件一起使用

 <script>const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState != 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}};xhr.open('GET', url, true);xhr.send(null);// xhr.abort();
</script>

此时如果想取消这个请求:

 <script>const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState != 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}};xhr.open('GET', url, true);xhr.send(null);// 终止当前请求xhr.abort();
</script>


2.11 setRequestHeader()方法--重点

描述:用于设置HTTP请求头信息

语法:xhr.setRequestHeader("key",'value');

说明:

本方法必须在open()之后、send()之前被调用

本方法用来设置在请求发送时,一并被发送出的一些补充信息

 例子1:

 <script>const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState != 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}};xhr.open('POST', url, true);// 请求头中的 Content-Type 字段用来告诉服务器,浏览器发送的数据是什么格式的xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');xhr.send(null);
</script>

例子2:

<script>//const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const url = 'https://www.imooc.com/api/http/json/search/suggest?words=js';const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState != 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}};xhr.open('POST', url, true);// 请求头中的 Content-Type 字段用来告诉服务器,浏览器发送的数据是什么格式的// xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');xhr.setRequestHeader('Content-Type', 'application/json');// xhr.send(null);// xhr.send('username=alex&age=18');xhr.send(JSON.stringify({username: 'alex'}));
</script>

此时如果为  const url = 'https://www.imooc.com/api/http/search/suggest?words=js';,则会报如下错

应该改为 const url = 'https://www.imooc.com/api/http/json/search/suggest?words=js';

例子3:

 <form action="https://www.imooc.com/api/http/search/suggest?words=js" method="post" >
<!--
enctype="application/x-www-form-urlencoded"其实就是form表单里entype的默认属性值
--><input type="text" name="username" /><input type="password" name="password" /><input type="submit" value="提交" /></form>

当我点击“提交”按钮后:

例子4:

<script>const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState != 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}};xhr.open('POST', url, true);// 请求头中的 Content-Type 字段用来告诉服务器,浏览器发送的数据是什么格式的xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');// xhr.setRequestHeader('Content-Type', 'application/json');xhr.send('username=alex&age=18');
</script>


2.12 send()方法--重点

描述:用于实际发出HTTP请求

语法:xhr.send(formData);

说明:

send()方法的参数是表单数据,为post请求准备

如果是get请求则参数直接写成null即可


2.13 综合示例

综合案例1:

 <script>//1.创建xhr对象,准备发送ajax请求var xhr = new XMLHttpRequest();//2.目的是监听后台有没有真的接收到请求xhr.onreadystatechange = function() {//3.readyState属性值,用来判断当前请求进行到哪种状态if (xhr.readyState == 4) {//4.如果进入if结构,后台已经接收到前台的请求//通过status属性的状态,来判断前端是否准确收到后台反馈的数据if (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {//5.真正收到数据,使用responseText属性获取后台反馈的准确数据console.log(xhr.responseText);console.log(typeof xhr.responseText);console.log(JSON.parse(xhr.responseText));}}}//6.当准备好接收后台数据的回调函数后,着手准备发送ajax请求xhr.open('get', '02.xhr.php?name=beixi&password=123456', true);//7.设置完毕后,通过send方法发送到后台xhr.send(null);
</script>
<?php$success=array('msg'=>"ok","info"=>$_GET);echo  json_encode($success);
?>

综合案例2:

 <script>// 使用 Ajax 完成前后端通信const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState !== 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.responseText);console.log(typeof xhr.responseText);}};xhr.open('GET', url, true);xhr.send(null);
</script>


三、XHR的常用事件

3.1 load事件--掌握

响应数据可用时触发

IE6~8 不支持 load 事件

<script>const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();//写法1xhr.onload = () => {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}};//写法2// xhr.addEventListener(//     'load',//     () => {//         if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {//             console.log(xhr.response);//         }//     },//     false// );xhr.open('GET', url, true);xhr.send(null);
</script>


3.2 error事件--掌握

请求发生错误时触发

IE10 开始支持

例子1:

 <script>// const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const url = 'https://www.iimooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();//写法2 xhr.addEventListener('load',() => {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}},false);xhr.open('GET', url, true);xhr.send(null);
</script>

例子2:

<script>// const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const url = 'https://www.iimooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();//写法2 xhr.addEventListener('load',() => {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}},false);//写法2xhr.addEventListener('error',() => {console.log('error');},false);xhr.open('GET', url, true);xhr.send(null);
</script>


3.3 abort事件--掌握

调用 abort() 终止请求时触发

IE10 开始支持

例子1:

<script>const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();xhr.addEventListener('load',() => {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}},false);xhr.addEventListener('abort',() => {console.log('abort');},false);xhr.open('GET', url, true);xhr.send(null);</script>

例子2:

<script>const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();xhr.addEventListener('load',() => {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}},false);xhr.addEventListener('abort',() => {console.log('abort');},false);xhr.open('GET', url, true);xhr.send(null);xhr.abort();
</script>


3.4 timeout事件--掌握

请求超时后触发

IE8 开始支持

<script>const url = 'https://www.imooc.com/api/http/search/suggest?words=js';const xhr = new XMLHttpRequest();xhr.addEventListener('load',() => {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.response);}},false);xhr.addEventListener('timeout',() => {console.log('timeout');},false);xhr.open('GET', url, true);xhr.timeout = 10;xhr.send(null);
</script>


四、xhr对象发送post请求

4.1 概述

描述:post请求和get请求的差异就在于多了一个表单数据,在xhr对象中可以通过FormData进行构建;POST 请求主要通过请求体携带数据,同时也可以通过请求头携带

步骤:

① 创建  xhr  对象
② 调用  xhr.open()  函数
设置  Content-Type  属性 (固定写法)
④ 调用  xhr.send()  函数, 同时指定要发送的数据
⑤ 监听  xhr.onreadystatechange  事件

在传递参数的时候必须以 查询字符串 的形式

示例:

// 1. 创建 xhr 对象
var xhr = new XMLHttpRequest()
// 2. 调用 open()
xhr.open('POST', 'http://www.liulongbin.top:3006/api/addbook')
// 3. 设置 Content-Type 属性(固定写法)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// 4. 调用 send(),同时将数据以查询字符串的形式,提交给服务器
xhr.send(`bookname=${encodeURIComponent('水浒传')}&author=${encodeURIComponent('施耐庵')}&publisher=${encodeURIComponent('天津图书出版社')}`)
// 5. 监听 onreadystatechange 事件
xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {console.log(xhr.responseText)}
}

4.2 FormData(HTML5新增)

语法:var formData = new FormData([表单元素]);

formData.append('ket':value); //value如果是字符串类型则要加引号--添加数据

xhr.send(formData); // 发送数据

说明:至于formData的创建时机和位置,只要你能够在请求发送出去之前也就是xhr.send()语句被写出之前添加给xhr对象,那么你愿把formData放哪就放哪

 示例1:使用 Ajax 提交表单

 <form id="login" action="https://www.imooc.com/api/http/search/suggest?words=js" method="POST" enctype="application/x-www-form-urlencoded"><input type="text" name="username" placeholder="用户名" /><input type="password" name="password" placeholder="密码" /><input id="submit" type="submit" value="登录" />
</form>
<script>const login = document.getElementById('login');// console.log(login.username);// console.log(login.password);// 通过 解构赋值 来获取相关元素const {username,password} = login;const btn = document.getElementById('submit');const url = 'https://www.imooc.com/api/http/search/suggest?words=js';btn.addEventListener('click',e => {// 阻止表单自动提交e.preventDefault();// 表单数据验证//代码略// 发送 Ajax 请求const xhr = new XMLHttpRequest();xhr.addEventListener('load',() => {if ((xhr.status >= 200 && xhr.status < 300) ||xhr.status === 304) {console.log(xhr.response);}},false);xhr.open('POST', url, true);//xhr.send('username=alex&password=12345');// 组装数据 添加数据方式2用到const data = `username=${username.value}&password=${password.value}`;// FormData 可用于发送表单数据const data = new FormData(login);//添加数据方式1//data.append('age', 18);//data.append('sex', 'male');// console.log(data);//遍历添加数据方式2 for (const item of data) {console.log(item);}xhr.send(data);}, false);
</script>

例子2:

<script>const login = document.getElementById('login');// console.log(login.username);// console.log(login.password);const {username,password} = login;const btn = document.getElementById('submit');const url = 'https://www.imooc.com/api/http/search/suggest?words=js';btn.addEventListener('click',e => {// 阻止表单自动提交e.preventDefault();// 表单数据验证// 发送 Ajax 请求const xhr = new XMLHttpRequest();xhr.addEventListener('load',() => {if ((xhr.status >= 200 && xhr.status < 300) ||xhr.status === 304) {console.log(xhr.response);}},false);xhr.open('POST', url, true);// 组装数据// const data = `username=${username.value}&password=${password.value}`;// FormData 可用于发送表单数据const data = new FormData(login);data.append('age', 18);data.append('sex', 'male');console.log(data);for (const item of data) {console.log(item);}xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');xhr.send(data);// xhr.send('username=alex&password=12345');},false);
</script>

 示例3:

<label for="username"><span>用户名:</span><input type="text"  id="username"  class="username"/>
</label><br/><label for="password"><span>密码:</span><input type="text"  id="password"  class="password"/>
</label><br/><button>登陆</button>
 <script>var usernameInput = document.querySelector('.username');var passwordInput = document.querySelector('.password');var btn = document.querySelector('button');btn.onclick = function() {//兼容性问题,考虑兼容IE使用 ActiveXObject()  非IE使用XMLHttpRequest()//        xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject("");var xhr = new XMLHttpRequest();xhr.onreadystatechange = function() {if (xhr.readyState == 4) {if (xhr.status == 200) {console.log(JSON.parse(xhr.responseText));}}};xhr.open('post', '03post请求.php', true);// 设置 Content-Type 属性(固定写法)xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')//构建post表单数据,使用FormData类构建数据var datas = new FormData();datas.append('uname', usernameInput.value);datas.append('upass', passwordInput.value);//如果想发送数据,直接写在 send() 的参数位置,一般是字符串xhr.send(datas);}</script>
<?php$success = array('msg'=>"ok","info"=>$POST);echo json_encode($success);
?>

示例4:获取网页表单的值

    <form id="form1"><!--autocapitalize:阻止自动填充行为--><input type="text" name="uname" autocapitalize="off" /><br><input type="password" name="upwd" /><button type="submit">提交</button></form>
        //1. 通过DOM操作,获取 from 表单元素const form = document.querySelector("#form1");form.addEventListener('submit',function(e){//阻止表单的默认提交行为e.preventDefault();//创建 FormData 快速获取表单里的数据const fd = new FormData(form);const xhr = new XMLHttpRequest();xhr.open('POST','http://www.liulongbin.top:3006/api/formdata');xhr.send(fd);xhr.onreadystatechange = function(){if(xhr.readyState === 4 && xhr.status === 200){console.log(JSON.parse(xhr.responseText));}};});

注意:不能直接传递对象,需要先将对象转换成字符串的形式

xhr.send({

username: 'alex',

age: 18

});

IE10 及以上可以支持FormData

对所发送信息的数量没有限制

POST请求用于提交敏感数据和大数据


4.3  封装Ajax

myAjax2,

function obj2str(data) {data = data || {}; // 如果没有传参, 为了添加随机因子,必须自己创建一个对象data.t = new Date().getTime();var res = [];for(var key in data){// 在URL中是不可以出现中文的, 如果出现了中文需要转码// 可以调用encodeURIComponent方法// URL中只可以出现字母/数字/下划线/ASCII码res.push(encodeURIComponent(key)+"="+encodeURIComponent(data[key])); // [userName=lnj, userPwd=123456];}return res.join("&"); // userName=lnj&userPwd=123456
}
function ajax(option) {// 0.将对象转换为字符串var str = obj2str(option.data); // key=value&key=value;// 1.创建一个异步对象var xmlhttp, timer;if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safarixmlhttp=new XMLHttpRequest();}else{// code for IE6, IE5xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}// 2.设置请求方式和请求地址/*method:请求的类型;GET 或 POSTurl:文件在服务器上的位置async:true(异步)或 false(同步)*/if(option.type.toLowerCase() === "get"){xmlhttp.open(option.type, option.url+"?"+str, true);// 3.发送请求xmlhttp.send();}else{xmlhttp.open(option.type, option.url,true);// 注意点: 以下代码必须放到open和send之间xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");xmlhttp.send(str);}// 4.监听状态的变化xmlhttp.onreadystatechange = function (ev2) {/*0: 请求未初始化1: 服务器连接已建立2: 请求已接收3: 请求处理中4: 请求已完成,且响应已就绪*/if(xmlhttp.readyState === 4){clearInterval(timer);// 判断是否请求成功if(xmlhttp.status >= 200 && xmlhttp.status < 300 ||xmlhttp.status === 304){// 5.处理返回的结果// console.log("接收到服务器返回的数据");option.success(xmlhttp);}else{// console.log("没有接收到服务器返回的数据");option.error(xmlhttp);}}}// 判断外界是否传入了超时时间if(option.timeout){timer = setInterval(function () {console.log("中断请求");xmlhttp.abort();clearInterval(timer);}, option.timeout);}
}
      window.onload = function (ev) {const oBtn = document.querySelector("button");oBtn.onclick = function(ev1){ajax({url: './ajax-get.php',data: {"userName": "lwj","userPwd": "123456"},type: "POST",timeout: 3000,success:function(xhr){alert(xhr.responseText);},error:function(){alert("请求失败");}})};};

五、xhr对象发送 GET 请求

5.1 概述

GET 请求不能通过请求体携带数据,但可以通过请求头携带

使用 xhr 对象发起带参数的 GET 请求时,只需在调用 xhr.open() 期间,为 URL 地址指定参数即可,这种在 URL 地址后面拼接的参数,叫做查询字符串

什么是查询字符串?

查询字符串(URL参数)是指在 URL 的末尾加上用于向服务器发送信息的字符串(变量)

格式:将英文的 ? 放在 URL 的末尾,然后加上 参数=值,想加上多个参数的话,使用 & 符号进行分割。以这个形式,可以将想要发送给服务器的数据添加到 URL 中

GET请求携带参数的本质:

无论使用 $.ajax(),还是使用 $.get(),又或者直接使用 xhr 对象发起 GET 请求,当需要携带参数的时候,本质上,都是直接将参数以查询字符串的形式,追加到 URL 地址的后面,发送到服务器的。

示例:

  <script>const url ='https://www.imooc.com/api/http/search/suggest?words=js&username=alex&age=18';const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState != 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.responseText);}};xhr.open('GET', url, true);xhr.send(null);
</script>

    <form action="https://www.imooc.com/api/http/search/suggest" method="get"><input type="text" name="username"><br><input type="text" name="words"><br><input type="password" name="password"><br><input type="submit" value="提交"></form>


5.2 注意事项

URL 地址中,只允许出现英文相关的字母、标点符号、数字,因此,在 URL 地址中不允许出现中文字符

如果 URL 中需要包含中文这样的字符,则必须对中文字符进行编码(转义),可以使用 encodeURIComponent() 进行编码

URL编码的原则:使用安全的字符(没有特殊用途或者特殊意义的可打印字符)去表示那些不安全的字符

URL编码原则的通俗理解:使用英文字符去表示非英文字符。

如果携带的数据是非英文字母的话,比如说汉字,就需要编码之后再发送给后端,不然会造成乱码问题

浏览器提供了 URL 编码与解码的 API,分别是:

  • encodeURI()  编码的函数
  • decodeURI()  解码的函数

示例:

<script>const url = `https://www.imooc.com/api/http/search/suggest?words=${encodeURIComponent('前端')}`;const xhr = new XMLHttpRequest();xhr.onreadystatechange = () => {if (xhr.readyState != 4) return;if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {console.log(xhr.responseText);}};xhr.open('GET', url, true);xhr.send(null);
</script>

GET对所发信息的数量也有限制,限制在大于2000个字符

用于发送非敏感的数据和小数据


5.3 Ajax封装

myAjax.js,

function objToStr(obj){let res = [];obj.t = new Date().getTime();for(let key in obj){// res.push(key+"="+obj[key]);res.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));// [userName=lwj,userPwd=123456]}return res.join("&"); // userName=lwj&userPwd=123456
};function ajax(url,obj,timeout,success,error){//0. 将对象转换成字符串let str = objToStr(obj); // key=value&key=value;//1.创建xhr对象let xhr,timer;//判断当前浏览器是否支持 XMLHttpRequestif (window.XMLHttpRequest) {xhr = new XMLHttpRequest();} else {// code for IE6, IE5xhr = new ActiveXObject("Microsoft.XMLHTTP");}// 2.设置请求方式和请求地址// 2.1 method:GET/POST// 2.2 url:文件在服务器上的位置// 2.3 async:true(异步) false(同步)xhr.open("GET", url+"?"+str, true);// 3.发送请求xhr.send();// 4.监听状态变化xhr.onreadystatechange = (ev2) => {if(xhr.readyState === 4){//请求完成clearInterval(timer);//判断是否请求成功if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304){// 5.处理返回的结果//  console.log("接收到服务器返回的数据");success(xhr);}else{//  console.log("未接收到服务器的数据");error(xhr);}}};//判断外界是否传入了超时时间if(timeout){timer = setInterval(function(){//  console.log("请求中断");xhr.abort(); // 请求超时,中断请求clearInterval(timer); // 清除定时器},timeout);}
};
<script src="./myAjax.js"></script><script>window.onload = function (ev) {const oBtn = document.querySelector("button");oBtn.onclick = function(ev1){ajax('./ajax-get.php',{"userName": "lwj","userPwd": "123456"},3000,function(xhr){alert(xhr.responseText);},function(xhr){alert("请求失败");});};};</script>
<button>发送请求</button>

ajax-get.php,

<?php// sleep(5);// echo "haha";echo $_GET["userName"];echo $_GET["userPwd"];
?>


 5.4 promise封装Ajax(POST/GET)

myAjax,

        function objTostr(data) {data = data || {};data.t = new Date().getTime();var res = [];for (var key in data) {res.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));}return res.join("&");}function ajax(option) {return new Promise(function(resolve, reject) {// 0.将对象转换为字符串var str = objTostr(option.data);// 1.创建一个异步对象var xmlhttp, timer;if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();} else {xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}// 2.设置请求方式和请求地址if (option.type.toLowerCase() === "get") {xmlhttp.open(option.type, option.url + "?" + str, true);// 3.发送请求xmlhttp.send();} else {xmlhttp.open(option.type, option.url, true);// 注意点: 以下代码必须放到open和send之间xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");xmlhttp.send(str);}// 4.监听状态的变化xmlhttp.onreadystatechange = function(ev2) {if (xmlhttp.readyState === 4) {clearInterval(timer);// 判断是否请求成功if (xmlhttp.status >= 200 && xmlhttp.status < 300 ||xmlhttp.status === 304) {// 5.处理返回的结果// console.log("接收到服务器返回的数据");// option.success(xmlhttp);resolve(xmlhttp);} else {// console.log("没有接收到服务器返回的数据");// option.error(xmlhttp);reject(xmlhttp);}}};// 判断外界是否传入了超时时间if (option.timeout) {timer = setInterval(function() {console.log("中断请求");xmlhttp.abort();clearInterval(timer);}, option.timeout);}});}
    <script>ajax({// type: "get/post",type: "post",url: "40.php",}).then(function(xhr) {let str = xhr.responseText;let json = JSON.parse(str);console.log(json);}).catch(function(xhr) {console.log(xhr.status);});</script>

40.php,

<?php
$arr = array("name"=>"lwj", "age"=>"2");
$data = json_encode($arr);
echo $data;
?>


六、xhr对象的兼容性问题

描述:xhr对象的获取方式在IE和非IE下是需要使用不同方法的

语法:

标准浏览器支持的方法:XMLHttpRequest()

IE浏览器支持的方法:ActiveXObject()

例子:

if(window.XMLHttpRequest){

xhr = new XMLHttpRequest();

}else if(window.ActiveXObject){

xhr = new ActiveXObject();

}

xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject();

例子:

<label for="username"><span>用户名:</span><input type="text"  id="username"  class="username"/>
</label><br/><label for="password"><span>密码:</span><input type="text"  id="password"  class="password"/>
</label><br/><button>登陆</button>
 <script>var usernameInput = document.querySelector('.username');var passwordInput = document.querySelector('.password');var btn = document.querySelector('button');btn.onclick = function() {//兼容性问题,考虑兼容IE使用 ActiveXObject()  非IE使用XMLHttpRequest()//        xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject("");var xhr = new XMLHttpRequest();xhr.onreadystatechange = function() {if (xhr.readyState == 4) {if (xhr.status == 200) {var jsonData = JSON.parse(xhr.responseText);if (jsonData.infoCode == 0) {alert('登陆成功');} else if (jsonData.infoCode == 1) {alert('登陆失败');} else {alert('网络异常');}console.log(JSON.parse(xhr.responseText));}}};xhr.open('post', '03post请求.php', true);//构建post表单数据,使用FormData类构建数据var datas = new FormData();datas.append('uname', usernameInput.value);datas.append('upass', passwordInput.value);xhr.send(datas);}</script>
<?php$username=$_POST['uname'];$password=$_POST['upass'];$success=array('msg'=>"ok");$con=mysqli_connect('localhost','root','','beixidb');if($con){mysqli_query($con,'set names utf8');mysqli_query($con,'set character_set_client=utf8');mysqli_query($con,'set character_set_results=utf8');$sql="select * from userinfo where 1";$res=$con->query($sql);if($res->num_rows>0){$info=[];for($i=0;$row=$res->fetch_assoc();$i++){$info[$i]=$row;}}//判断是否登陆成功$flag=false;  //默认false表示登陆失败,如果登陆成功,则变成truefor($j=0;$j<count($info);$j++){if($info[$j]['username']==$username){if($info[$j]['password']==$password){$success['infoCode']=0;$flag=true;break;}}if($flag==false){$success['infoCode']=1;}}}else{$success['infoCode']=2;//  0代表成功;1 代表失败;  2代表数据库连接失败}
?>

七、数据交换格式(XML、JSON)

7.1 什么是数据交换格式?

数据交换格式,就是服务器端与客户端之间进行数据传输与交换的格式。

前端领域,经常提及的两种数据交换格式分别是 XML 和 JSON。其中 XML 用的非常少,所以,我们重点要学习的数据交换格式就是 JSON。


7.2 XML

XML 的英文全称是 EXtensible Markup Language,即可扩展标记语言。因此,XML 和 HTML 类似,也是一种标记语言。


XML和HTML的区别

XML 和 HTML 虽然都是标记语言,但是,它们两者之间没有任何的关系。

l HTML 被设计用来描述网页上的 内容 ,是网页内容的载体
l XML 被设计用来 传输和存储数据 ,是数据的载体

XML的缺点:

①XML 格式臃肿,和数据无关的代码多,体积大,传输效率低
②在 Javascript 中解析 XML 比较麻烦


7.3 JSON

概念:JSON 的英文全称是 JavaScript Object Notation,即“JavaScript 对象表示法”。简单来讲,JSON 就是 Javascript 对象和数组的字符串表示法,它使用文本表示一个 JS 对象或数组的信息,因此,JSON 的本质是字符串

作用:JSON 是一种轻量级的文本数据交换格式,在作用上类似于 XML,专门用于存储和传输数据,但是 JSON 比 XML 更小、更快、更易解析

现状:JSON 是在 2001 年开始被推广和使用的数据格式,到现今为止,JSON 已经成为了主流的数据交换格式


JSON的三种结构

JSON 就是用字符串来表示 Javascript 的对象和数组。所以,JSON 中包含对象数组两种结构,通过这两种结构的相互嵌套,可以表示各种复杂的数据结构。

简单值形式:

一般放在一个以 .json 结尾的文件中

    <script>// 创建 xhr 对象const xhr = new XMLHttpRequest();// 监听状态变化xhr.onreadystatechange = ()=> {if(xhr.readyState !== 4){return;}if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304){console.log(xhr.responseText);console.log(typeof xhr.responseText);}};// 准备发送请求xhr.open('GET','./demo.json',true);// 发送请求xhr.send(null);</script>

demo.json

"str"

对象结构:对象结构在 JSON 中表示为 { } 括起来的内容。数据结构为 { key: value, key: value, … } 的键值对结构。其中,key 必须是使用英文的双引号包裹的字符串,value 的数据类型可以是数字、字符串、布尔值、null、数组、对象6种类型。

数组结构:数组结构在 JSON 中表示为 [ ] 括起来的内容。数据结构为 [ "java", "javascript", 30, true … ] 。数组中数据的类型可以是数字、字符串、布尔值、null、数组、对象6种类型。


JSON语法注意事项

①属性名必须使用双引号包裹

②字符串类型的值必须使用双引号包裹

③JSON 中不允许使用单引号表示字符串

④JSON 中不能写注释

⑤JSON 的最外层必须是对象或数组格式

⑥不能使用 undefined 或函数作为 JSON 的值

JSON 的作用:在计算机与网络之间存储和传输数据。

JSON 的本质:用字符串来表示 Javascript 对象数据或数组数据


7.4 JSON 的常用方法

JSON和JS对象的关系

JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。例如:

要实现从 JSON 字符串转换为 JS 对象,使用 JSON.parse() 方法:

要实现从 JS 对象转换为 JSON 字符串,使用 JSON.stringify() 方法:

JSON序列化和反序列化:

把数据对象转换为字符串的过程,叫做序列化,例如:调用 JSON.stringify() 函数的操作,叫做 JSON 序列化。

把字符串转换为数据对象的过程,叫做反序列化,例如:调用 JSON.parse() 函数的操作,叫做 JSON 反序列化。

Ajax请求json示例:

原理(思路):

  • json字符串-->json对象  JSON.parse(json字符串);
  • json对象-->js字符串  JSON.stringify(json字符串);
 <button id="btn">发送请求</button><table border="1" cellpadding="0" cellspacing="0" id="table" width="600"><tr><th>ID</th><th>姓名</th><th>性别</th></tr></table>
<script>var str = '<tr><th>ID</th><th>姓名</th><th>性别</th>';document.querySelector('#btn').onclick = function(){window.beixiAjax({type: 'get',url: 'student.json',success: function(res){for (var i = 0; i < res.length; i++) {str += "<tr><td>" + res[i]['id'] + "</td><td>" + res[i]['name'] + "</td><td>" + res[i]['sex'] + "</td></tr>";}table.innerHTML = str;}})}
</script>
[{"name":"常石磊","sex":"男","id":"1"},{"name":"金志文","sex":"男","id":"2"},{"name":"周笔畅","sex":"女","id":"3"},{"name":"白举纲","sex":"男","id":"4"},{"name":"白安","sex":"女","id":"5"}
]

当鼠标单击“发送请求”按钮后:

使用 JSON.parse() 和 JSON.stringify() 封装 localStorage:

localStorage 只能存储字符串格式的数据,但是如果要存储一些比较复杂的格式数据时是不能的,在学了 JSON.parse() 和 JSON.stringify() 之后就可以实现存储一些复杂类型的数据格式了

storage.js,

const storage = window.localStorage;// 设置
const set = (key,value) => {storage.setItem(key,JSON.stringify(value));
};// 获取
const get = key => {return JSON.parse(storage.getItem(key));
};// 移除
const remove = key => {storage.removeItem(key);
};// 清空
const clear = () => {storage.clear();
};export {set, get, remove, clear};

demo.html

    <script type="module">import {get, set, remove, clear} from './js/storage.js';set('username','alex');console.log(get('username'));</script>

Ajax--概述、xhr对象的常用属性和方法、xhr的常用事件、xhr对象发送POST请求、xhr对象发送GET请求、xhr对象的兼容性问题、数据交换格式(XML、JSON)相关推荐

  1. 前端与服务器通讯的数据交换格式XML 、JSON

    数据交换格式 就是服务器端与客户端之间进行数据传输与交换的格式. 前端领域,经常提及的两种数据交换格式分别是XML(使用较少)和JSON(主要) XML XML和HTML的区别 都是标记语言,但是,他 ...

  2. Ajax学习(3)XMLHttpRequest的使用+数据交换格式(JSON)

    XMLHttpRequest的使用 XMLHttpRequest(简称 xhr)是浏览器提供的 Javascript 对象,可以请求服务器上的数据资源.(jQuery 中的 Ajax 函数,就是基于 ...

  3. 于数据交换格式XML和JSON的比较

    目前,在web开发领域,主要的数据传输格式有XML和JSON,对于XML相信每一个web developer都不会感到陌生:相比之下,JSON可能对于一些新步入开发领域的新手会感到有些陌生,也可能你之 ...

  4. XML和JSON 数据交换格式

    为什么80%的码农都做不了架构师?>>>    一.什么是数据交换格式? 客户端与服务器常用数据交换格式xml.json.html 二.数据交换格式应用场景 移动端(安卓.IOS)通 ...

  5. xml的数据交换以及xml和json数据交换的比较

    一.什么是Web Service ? Web Services 是有企业发布的完成其特定商务需求的在线应用服务,其他公司或应用软件能够通过internet来访问并使用这项在线服务 WebServicc ...

  6. 数据交换格式Json与XML

    什么是数据交换格式: 主流的有Json.XML.HTML. 数据交换格式的应用场景: 移动端(安卓,IOS)通讯方式采用http协议+Json格式的restful风格. 很多互联网公司都是用Http协 ...

  7. XML和JSON两种数据交换格式的比较

    目前,在web开发领域,主要的数据交换格式有XML和JSON,对于XML相信每一个web developer都不会感到陌生: 相比之下,JSON可能对于一些新步入开发领域的新手会感到有些陌生,也可能你 ...

  8. 数据交换格式 - PB(protocol buffer),xml,json,array

    目录 PB PB vs xml PB vs json json json vs xml xml 数组 PB 扩充容易, 二进制 序列化简单 ProtocolBuffers-Google's data ...

  9. selenium提取数据之driver对象的常用属性和方法

    selenium提取数据之driver对象的常用属性和方法 在使用selenium过程中,实例化driver对象后,driver对象有一些常用的属性和方法 driver.page_source 当前标 ...

最新文章

  1. Matlab编程与数据类型 -- 奇数阶魔方矩阵的编程
  2. 使用Ptrace跟踪进程收到的异常信号(信号SIGTRAP是通过traceme后wait得到的)
  3. [转载]每个极客都应该知道的Linux技巧
  4. linux串口发送键值,c51:串口通信,按键发送字符串
  5. elsa-core——1.Hello World:Console
  6. C++map容器应用举例
  7. oracle分组后伪列,Oracle伪列和伪表和分组函数(row_number,Rank)
  8. 干货!639页《深度学习:Deep Learning》硬核课程PPT
  9. java8获取当前时间并格式化
  10. ptyhon的列表与字典操作
  11. 一篇文章快速搞懂排序算法(含实现源码)
  12. 数据结构—二叉树的存储结构
  13. 【题解】Inspection UVa 1440 LA 4597 NEERC 2009
  14. 电子技术课程设计—交通灯控制系统设计
  15. 数据挖掘实战 —— 泰坦尼克号
  16. 逻辑学中的思维规律:同一律,不矛盾律,排中律,充足理由律
  17. Andriod Studio安装教程
  18. 575. 分糖果【我亦无他唯手熟尔】
  19. AURIX Development Studio软件介绍
  20. Java基础之刨根问底第1集——JVM的结构

热门文章

  1. 分享几个我常用的私活接单渠道
  2. delphi文件操作
  3. 『递推』[AGC043D] Merge Triplets
  4. 3dMax 光标丢失,无法正常显示
  5. 服务器上验证码不显示
  6. 返璞归真-删除文件默认打开方式
  7. PID控制器的优缺点和周期
  8. 二十几岁女孩该有的想法
  9. 对PhD一年级新生有什么建议?
  10. 【轉】一個在台積電工作3年的工程師寫給學弟學妹們的信