如何用谷歌来模拟不同网速下的网站加载速率?
右击检查,打开谷歌调试工具的Network栏->右上的下标Add…->Add custom profile…->文件名、上传/下载/延迟速度->返回点右上下标中的该文件名,可以在该模式测试当前网页加载速度。
学习核心:基本使用、使用封装API、跨域
1.什么是AJAX:浏览器提供的一套 API
2.AjAX的作用:可以通过 JavaScript 直接发送请求接收响应,从而实现网络编程(发送请求、接收响应)。
3.为什么要有AJAX:解决让js在浏览器不刷新时的情况下,就能拿到数据。
4.学习核心:基本使用、使用封装API、跨域
5.学习重点:不是在于我要发个请求,而在于要如何通过代码来发请求(网络编程)
不考虑兼容就只有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提供的核心类型: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里面拿到)。
首先注意:不能通过变量去接收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)
}
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)(写接收响应时)
本质上 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的第二个参数),因为一般是拿数据。
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 请求过程中,都是采用请求体承载需要提交的数据。
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')
提问:如果希望服务端返回一个复杂数据,该如何处理?
只要涉及到响应类型不同时,服务端应就要设置上合理的Content-Type。(告诉客户端;用上JQ等时,JQ会帮忙转换,若遇到不设置的JQ应对方法如$.getJSON)
不管服务端采用XML还是JSON,本质上都是将数据返回给客户端
一种数据描述手段,老掉牙的东西。淘汰的原因:数据冗余太多
用途: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])//张三
}
一种数据描述手段,类似于 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 在老版本浏览器(IE5/6)中有兼容问题,可以通过另外一种方式代替(三元表达式判断一下支持、不支持)
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
面试时考ajax时一定把这点写上,ajax考的就是这点
封装的套路:
写一个相对比较完善的用例
写一个空函数,没有形参,将刚刚的用例直接作为函数的函数体
根据使用过程中的需求抽象参数
接下来开始进行封装
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)
}
. 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)
}
})
实现页面的 局部刷新 效果,能节省请求,如不用再请求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技巧
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基本使用
//在客户端发起跨域请求
有了个用例之后,开始封装。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标记可以发送不同源地址请求的一个特性去完成的一个跨域请求
Cross Origin Resource Share,跨域资源共享
现在支持不同源地址之前的ajax请求,不过存在兼容问题、需要进行一些服务端的配置。
之前有个问题:涉及到AJAX操作的页面”不能“通过文件方式访问——就是跨域问题,现在也可以解决了。
1.服务端只需一行代码搞定:允许跨域请求header(‘Access-Control-Allow-Origin: *’);
2.客户端不用做任何修改