Ajax笔记
浏览器提供的一套方法在不刷新整个网页的情况下更新局部的数据
初识Ajax
- Ajax需要运行在网站环境中才能生效
- Ajax相当于浏览器发送请求与接收请求的代理人,以实现在不影响用户访问页面的情况下更新局部数据
Ajax的实现
- 创建Ajax对象
var xhr = new XMLHttpRequest();
- 配置请求地址以及请求方式
xhr.open('get', 'http://www.XXX.com')
- 发送请求
xhr.send()
- 接收响应数据
xhr.onload = function(){console.log(xhr.responseText)}
- 在真实的项目中,服务器端大多数情况下都会以JSON对象格式作为响应数据的格式。当客户端接收到响应数据时,要将JSON数据和HTML字符串进行拼接,然后呈现在页面中
- 在http请求与响应的过程中,无论是请求参数还是响应内容,如果是对象类型,最终都会被转换为对象字符串进行传输
- 将json字符串解析为json对象
JSON.parse(json字符串)
- 将json字符串解析为json对象
请求参数传递
-
get请求参数需要与open方法中参数二也就是请求地址拼接后再发送请求
-
post请求需要将请求参数放在send方法的请求参数中,注意:在post请求中必须在请求头中设置请求参数内容的类型,也就是
content-type
的属性- 设置请求头
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
- 发送参数
xhr.send(params);
- 代码实例
Document const express = require('express'); const bodyParser = require('body-parser'); const path = require('path'); const app = express(); app.use(bodyParser.urlencoded()); app.use(express.static(path.join(__dirname, 'public'))); app.post ( '/test', function(req,res) { console.log(req.body); res.send(req.body); } ) app.listen(80, () => {console.log('服务器启动成功!');});
- 设置请求头
-
请求参数的格式
- application/x-www-form-urlencoded对应的请求参数的格式
name=tom&age=12&···
- application/json对应的请求参数的格式
{name: 'tom', age: '12',···}
,也就是告诉服务器当前请求参数的格式为json- 将json对象转换为json字符串
JSON.stringify(json对象)
- 对应的使用body-parse模块的设置为
app.use(bodyParser.json());
,同样使用请求对象下的body属性查看请求参数 - 在传递请求参数的时同样需要在send中传递但是参数形式需为json字符串格式
- get请求是无法提交json格式的请求参数的,传统的网页表单也不支持json数据格式
- 将json对象转换为json字符串
- application/x-www-form-urlencoded对应的请求参数的格式
Ajax状态码
- 在创建ajax对象,配置Ajax对象,发送请求,以及接收完服务器响应的数据,这些过程中的每一个步骤都会对应一个数值,这个数值就是Ajax状态码
- 0:请求初始化(未调用open方法)
- 1:请求已经建立,但是还没有发送(未调用send方法)
- 2:请求已经发送
- 3:请求正在处理,通常响应中已经有部分数据可以使用了
- 4:响应数据已经接收完成,可以获取并使用服务器的响应了
- 获取ajax状态码
xhr.readyState
- 当Ajax状态码发生改变的时候会触发
onreadystatechange
事件
Ajax错误处理
-
网络畅通,服务器端能接收到请求,服务器端返回的结果不是预期的结果
- 可以判断服务器返回的状态码,分别进行处理
xhr.status
获取http状态码
xhr.onload = function() { if(xhr.status == XXX) { ··· } }
- 可以判断服务器返回的状态码,分别进行处理
-
网络畅通但是服务器没有接收到请求,返回404
- 检查请求地址
-
网络畅通,服务器端可以接收到请求,但是服务端程序错误返回500
- 500 (Internal Server Error)
-
网络中断,请求无法发送(不会触发xhr对象的onload事件)
- 会触发xhr对象下的onerror事件
xhr.onerror = function() { alert('网络中断'); ··· }
-
Ajax状态码表示的是ajax请求过程中的每个阶段,是Ajax对象返回的
-
HTTP状态码表示的是请求的处理结果,是服务器端返回的
问题:在低版本的ie浏览器中,ajax请求有严重的缓存问题,即在请求地址不发生改变的情况下,只有第一次请求会真正的发送到服务器端,后续的请求都会从浏览器中的缓存中查找结果,即使服务器端更新了数据但是看用户端拿到的还是旧数据
解决方案:在发送请求的时候在请求地址的后面添加请求参数,确保请求地址与之前的请求地址不同xhr.open('get', 'http://localhost:3000/cache?updateUrl=' + Math.random());
Ajax封装
-
实例代码
Document - 可以进一步完善该函数的封装,也就是设置默认参数,将其指定为一个对象格式,然后将参数对象覆盖默认值对象
客户端的模板引擎(art-template)
-
网址 http://aui.github.io/art-template/zh-cn/docs/installation.html
-
使用
- 在HTML中引用模板引擎库文件
- 准备art-template模板(带有指定id标识的script标签包裹的html格式代码片段)
- 将指定的模板与对应的数据进行拼接
let html = template('XXX', {···})
(XXX为模板中指定的script标签的id) - 将拼接好的html字符串输出到页面中的指定容器内部
document.getElementById('container').innerHTML = html
- 通过模板语法告诉模板引擎数据和html字符串要如何拼接
- 在HTML中引用模板引擎库文件
-
示例代码
Document
同源政策
Ajax只允许向自己的服务器发送请求。也就是说A网站只能向A的服务器发送请求,B网站只能向B服务器发送请求,但是A客户端不能向B服务器发送请求。但是这种限制可以解决
什么是同源
- 如果两个页面拥有相同的协议/域名和端口,那么这两个页面就属于同一个源,有一项不同就是不同源。
同源政策的目的
- 保证信息的安全,防止恶意网站窃取数据。最初的同源政策指的是A网站再客户端设置的cookie,B网站是不能访问的。
JSONP(json with padding)解决非同源限制的问题
-
在网页中的script标签中的src属性具有发送请求的能力且不受限制(就比如在使用jquery时,向提供该js文件的服务器请求该js文件时就可以正常的获取到)
-
src中的请求地址可以时任意合法请求地址,但是必须返回正确的js代码。原因是script在接收到响应内容后,需要将响应的内容当作js代码来执行
-
也就是说服务器端实际响应的内容是一段js代码,接收到代码后再在当前的网页运行
-
示例代码
- 服务器1
const express = require('express'); const app = express(); app.use(express.static(__dirname + '/public')); app.get ( '/index', function(req, res, next) { res.send('服务器1'); } ) app.get ( '/index2', function(req, res) { let message = 'fun("来自服务器1响应的数据")'; res.send(message); } ) app.listen(3000, function(){ console.log('服务器启动成功!') });
- 客户端1
Document - 服务器2
const express = require('express'); const app = express(); app.use(express.static(__dirname + '/public')); app.get ( '/index', function(req, res, next) { res.send('服务器2'); } ) app.listen(3001, function(){ console.log('服务器启动成功!') });
- 客户端2
Document -
点击按钮向非同源服务器发送请求(客户端2)
Document -
在客户端上传函数名称,避免与服务器端提供的函数名称不一致
const express = require('express'); const app = express(); app.use(express.static(__dirname + '/public')); app.get ( '/index2', function(req, res) { let x = req.query.callback; let message = x + '("非同源服务器响应的内容")'; res.send(message); } ) app.listen(3000, function(){ console.log('服务器启动成功!') });
Document
封装解决非同源问题函数
-
实例
function jsonp(options) { let params = ''; for(let i in options.data) { params += '&' + i + '=' + options.data[i]; } let funName = 'funNo_' + Math.random().toString().replace('.', ''); window[funName] = options.callback1; // 注意:.后面不能跟变量!要使用[]代替 let ajaxScript = document.createElement('script'); // 创建请求script标签 ajaxScript.src = options.url + '?callback=' + funName + params; document.body.appendChild(ajaxScript); ajaxScript.onload = function() { document.body.removeChild(ajaxScript); } } options = {url,data,callback1}
const express = require('express'); const app = express(); app.use(express.static(__dirname + '/public')); app.get ( '/index2', function(req, res) { res.jsonp({user: 'admin', id: 00211254}); //服务器端express框架提供了一个jsonp方法来响应该种ajax请求 } ) app.listen(3000, function(){ console.log('服务器启动成功!') });
FormData对象的使用
- 模拟HTML表单,相当于将HTML表单映射成表单对象,自动将表单中的数据拼接成请求参数的格式
- 异步上传二进制文件
注意:formdata对象不能用于get请求
注意:bodyparse模块不能处理客户端发送的该formdata类型的数据,使用的模块为formidable
使用
-
示例
Document const express = require('express'); const formidable = require('formidable'); const app = express(); app.use(express.static(__dirname + '/public')); app.post ( '/sendForm', function(req, res) { const parseFormData = new formidable.IncomingForm(); // 创建表单解析对象 parseFormData.parse // 解析客户端传递的表单对象 ( req, // 请求对象 function(error, fields, files) //回调函数参数:1错误对象,2表单中的普通请求参数,3文件上传相关的一些信息 { res.send(fields); } ) } ) app.listen(3000, () => {console.log('服务器启动成功');});
FormData对象的实例方法
- 获取表单对象中属性的值
formdata.get(key)
key也就是表单控件中的name的值(这里的formdata指的是创建的formdata对象) - 设置表单控件中的值
formdata.set(key, value)
key同上,value就是需要设置的值(若没有对应值的表单控件,则会自动添加) - 删除表单对象中指定的属性值
formdata.delete(key)
- 向表单对象中追加属性值
formdata.append(key, value)
,set方法也可以实现相同效果的操作,但是不同的是:若当前追加的属性值已经存在那么set方法会覆盖掉已经存在的值,而append则会在保留已存在值的前提下再次添加。(值得注意的是,若不进行服务器端的配置那么将默认会接受到最后一次相同属性的值)
二进制文件上传
-
使用post请求
-
实例代码
Document const express = require('express'); const formidable = require('formidable'); const app = express(); app.use(express.static(__dirname + '/public')); app.post ( '/sendForm', function(req, res) { const parseFormData = new formidable.IncomingForm(); // 创建表单解析对象 parseFormData.uploadDir = __dirname; // 设置上传文件保存的位置 parseFormData.keepExtensions = true; // 保存上传文件的后缀名 parseFormData.parse // 解析客户端传递的表单对象 ( req, // 请求对象 function(error, fields, files) //回调函数参数:1错误对象,2表单中的普通请求参数,3文件上传相关的一些信息 { res.send('ok'); } ) } ) app.listen(3000, () => {console.log('服务器启动成功');});
-
设置上传文件进度展示
-
xhr.upload
中保存了和文件上传相关的各种事件,其中xhr.upload.onprogress事件会在文件上传过程中持续触发 -
xhr.upload.onprogress事件的处理函数中的事件event保存了当前上传文件的进度分别为loaded(已经上传的多少)total(文件的总大小)
-
实例代码
Document
-
-
即时预览
-
原理就是将用户上传的文件保存成功后服务器将保存好的文件相对应的链接响应到客户端,然后由客户端显示连接对应的内容
-
在formidable模块的实例对象中的回调函数中的files参数中
files.XXX.path
可以得到服务器保存文件后的链接路径 -
实例
Document const express = require('express'); const formidable = require('formidable'); const app = express(); app.use(express.static(__dirname + '/public')); app.post ( '/sendForm', function(req, res) { const parseFormData = new formidable.IncomingForm(); // 创建表单解析对象 parseFormData.uploadDir = __dirname + '/public'; // 设置上传文件保存的位置 parseFormData.keepExtensions = true; // 保存上传文件的后缀名 parseFormData.parse // 解析客户端传递的表单对象 ( req, // 请求对象 function(error, fields, files) //回调函数参数:1错误对象,2表单中的普通请求参数,3文件上传相关的一些信息 { res.send({path: files.userfile.path.split('public')[1]}); } ) } ) app.listen(3000, () => {console.log('服务器启动成功');});
-
向腾讯天气服务器请求资源数据
-
示例
Document
CORS跨域资源共享
- 全称为:cross-origin-resource-sharing,即跨域资源共享,他允许浏览器向跨域服务器发送Ajax请求。
- 未进行配置的服务器报错 :cors.html:1 Access to XMLHttpRequest at 'http://localhost:3002/test2' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
- 在进行跨域请求访问时,浏览器会解析与跨域访问相关的响应头信息,若未查询到指定的信息,就会报错
使用
-
通过设置响应头信息实现跨域资源访问
-
示例
-
设置允许访问的网站
res.header('Access-Control-Allow-Origin', '*');
(允许所有网站跨域访问) -
设置允许访问的方式
res.header('Access-Control-Allow-Method', 'get, post');
(允许get和post)
- 这里设置的仅仅是一个路由内部的响应头,若要实现全部路由跨域访问可以使用express中的use方法设置
- 拦截所有请求
app.use( (req, res, next) => { res.header('Access-Control-Allow-Origin', '*');res.header('Access-Control-Allow-Method', 'get, post');next() } )
即可
- 拦截所有请求
-
访问非同源数据 服务器端解决方案
-
原理:当客户端访问A服务器获取非同源B服务器中的数据时,由A服务器端向B发送相应的请求,当B服务器端相应完成后,获取相应的信息响应回客户端
-
依赖于模块request
-
示例(在服务器中发送请求访问非同源服务器中的信息)
const express = require('express'); const request = require('request'); const app = express(); app.use(express.static(__dirname + '/public')); app.get ( '/test', function(req, res) { request('http://localhost:3002/test2', function(error, response, body){res.send(body);}); } ) app.listen(3000, () => {console.log('服务器启动成功!');});
-
注意:在使用Ajax实现跨域访问的时候一般是不会携带cookie信息的(处于安全性的考虑),但是我们可以在发送Ajax请求时在客户端设置withCredentials属性设置(false不携带cookie,true携带cookie)。相应的有关跨域访问的服务器的响应头信息设置Access-Control-Allow-Credentials,false不允许携带cookie,true允许携带cookie。