参考文档:菜鸟教程,本教程后端基于node.js下进行,结合b站多个学习教程以及菜鸟教程进行记录,包括ajax简介以及原生ajax、promise封装版ajax和jQuery版ajax的实现方式以及部分参数的含义,最后会讲到数组reduce函数的相关内容以及对跨域问题的总结,本文章有ES6语法,需要的基础知识:nodejs、express、ES6、promise以及jQuery

概念

  • 异步的JavaScript 和 XML
    1. 异步:客户端无需等待服务器端的响应,在等待服务器处理请求的过程中,客户端可以做其它事情
    2. 同步:客户端必须等待服务器端处理用户请求并完成后才能继续做其它操作
  • AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
  • AJAX 不是新的编程语言,而是一种使用现有标准的新方法
  • AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容
  • AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

简介

  • AJAX = 异步 JavaScript 和 XML。

  • AJAX 是一种用于创建快速动态网页的技术。

  • 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。

    异步更新:可以在不重新加载整个页面的情况下对网页某个部分实现更新

  • 传统的网页(不使用 AJAX)如果需要更新内容,必须重载整个网页。

  • ajax工作图

    ajax
  • AJAX是基于现有的Internet标准

  • 🔺AJAX应用程序与浏览器和平台无关的!

原生AJAX

服务器需先搭建好,方便进行调试

大致格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var express = require('express')

var app = express()

//原生获取post请求体
app.use(express.urlencoded({ extended: true }))
//开饭静态资源
app.use('/views/', express.static('./views/'))

//get请求
app.get('/get/', function(req, res) {
res.send('服务器收到请求了')
console.log(req.query)
})
//post请求
app.post('/post/', function(req, res) {
res.send('服务器收到请求了')
console.log(req.body)
})
//开放端口号
app.listen(3000, function() {
console.log('server is running')
})

版本一

大体ajax结构类似,基本分为四步

  1. 创建ajax对象
  2. 告诉Ajax对象要向哪发送请求,以什么方式发送请求1)请求方式 2)请求地址
  3. 发送请求
  4. 获取服务器端响应到客户端的数据(异步)

若需获得来自服务器的响应,使用 XMLHttpRequest 对象的 responseText 或 responseXML 属性。

  • responseText 获得字符串形式的响应数据。

  • responseXML 获得 XML 形式的响应数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="../public/index.css">
<title>ajax</title>
</head>
<body>
<p class="a">你好啊</p>

<script type="text/javascript">
// 1.创建ajax对象
var xhr = new XMLHttpRequest()
// 2.告诉Ajax对象要向哪发送请求,以什么方式发送请求
// 1)请求方式 2)请求地址:必须以文件形式访问,否则会产生跨域问题
xhr.open('get', 'http://192.168.1.103:3000/index')
// 3.发送请求
xhr.send();
// 4.获取服务器端响应到客户端的数据(异步)
xhr.onload = function() {
console.log(xhr.responseText)
}
</script>
</body>
</html>

版本二

步骤

  1. 实例化xhq对象(XMLHttpRequest

  2. 绑定事件监听(.onreadystatechange()

  3. 设置发送请求的方式、地址、参数(.open()

  4. 发送请求(.send())

GET 还是POST

与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。

在以下情况中,请使用 POST 请求:

  • 无法使用缓存文件(更新服务器上的文件或数据库)
  • 向服务器发送大量数据(POST 没有数据量限制)
  • 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
  • 发送需保证安全的数据时

get请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>原生AJAX GET请求</title>
</head>
<body>
<button id="get">我是原生get请求,你来点我啊</button>
<ul class="ul"></ul>
<script>
var btn = document.querySelector('#get')

//----------AJAX-----------------
btn.onclick = function() {
//实例化XMLHttpRequest对象
var xhr = new XMLHttpRequest()
//绑定注册事件onreadystatechange
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
// console.log(xhr)
var ul = document.querySelector('ul')
var li = document.createElement('li')
li.innerHTML = xhr.response
ul.appendChild(li)
}
}
//指定发送请求的方式、地址、参数,t = Date.now()解决IE浏览器强缓存不随服务器刷新问题
xhr.open('get', "http://192.168.1.103:3000/get?name=猪头&age=18&t="+ Date.now())
//发送请求
xhr.send()
}
//---------------AJAX结束-----------------

</script>
</body>

</html>
  • open() 方法的 url 参数 - 服务器文件地址

  • open() 方法的 async 参数(也就是最后一个)是说明发送的请求同步还是异步 ,true为异步,false为同步

  • onreadystatechange 事件中包含了三个属性,有必要阅读以及了解下这三个属性的含义以及特点,链接地址: onreadystatechange 事件

post请求

post请求类似get,但需增加一个设置请求体请求头(容易忘记),以及将请求体内容作为参数写在send()里,从而让服务端的中间件解析pot请求体(下面只放js代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
var btn = document.querySelector('#get')
btn.onclick = function() {
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
// console.log(1)
if (xhr.readyState === 4 && xhr.status === 200) {
console.log('xhr.response')
}
}
xhr.open('post', 'http://192.168.1.103:3000/post')
//🔺一定要注意设置请求体请求头!!!!!!!!!!
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
xhr.send('name=猪头&age=18')

}
</script>

封装原生AJAX方法(Promise)

版本一

涉及需掌握的内容

  1. 数组reduce方法
  2. 对象转数组 Object.keys('要转的数组')
  3. 转小写 .toLowerCase()
  4. 错误对象 new Error()
  5. nodejs后台搭建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用Promise封装原生Ajax</title>
</head>
<body>
<h2>该页面使用Promise封装了原生Ajax</h2>
<button id="btn1">点我发送GET请求</button>
<button id="btn2">点我发送POST请求</button>

<script type="text/javascript">

let btn1 = document.getElementById('btn1')
let btn2 = document.getElementById('btn2')

btn1.onclick = function () {
sendAjax('http://localhost:3000/test_get','GET',{m:1,n:2}).then((data)=>{
console.log(data)
}).catch((err)=>{
console.log(err)
})
}

btn2.onclick = function () {
sendAjax('http://localhost:3000/test_post','POST',{m:3,n:4}).then((data)=>{
console.log(data)
}).catch((err)=>{
console.log(err)
})
}

function sendAjax(url,method,data) {
return new Promise((resolve,reject)=>{
//1.创建xhr对象
let xhr = new XMLHttpRequest()
//2.绑定监听
console.log(a)
xhr.onreadystatechange = function () {
if(xhr.readyState !== 4){
return
}
if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status<= 299)){
const responseObj = {
data:xhr.response,
status:xhr.status,
statusText:xhr.statusText
}
resolve(responseObj)
}else{
reject(new Error('请求出错了'))
}
}
//3.设置请求的方式,地址,携带的参数
let dataKeys = Object.keys(data)
//4.将传递过来的数据对象加工成urlencoded形式的编码
let str = dataKeys.reduce(function (pre,now) {
return pre+=`${now}=${data[now]}&`
},'')
//5.发送请求
if(method.toLowerCase() === 'get'){
url += `?${str}`
xhr.open(method,url)
xhr.send()
}else if (method.toLowerCase() === 'post'){
xhr.open(method,url)
//post添加请求头
xhr.setRequestHeader('content-type','application/x-www-form-urlencoded')
xhr.send(str)
}
})
}

</script>
</body>
</html>

版本二

仿照JQuery的$.get(‘url’).then结合Promise进行设计,只能get

下面只放出js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function pGet(url, callback) {
return new Promise(function (resolve, reject) {
var oReq = new XMLHttpRequest()
// 当请求加载成功之后要调用指定的函数,这里使用onload()方法
oReq.onload = function () {
// 我现在需要得到这里的 oReq.responseText,注意&&的使用
callback && callback(JSON.parse(oReq.responseText))
resolve(JSON.parse(oReq.responseText)) //进行格式转换
}
oReq.onerror = function (err) {
reject(err)
}
oReq.open("get", url, true)
oReq.send()
})
}

实现方式

1
2
3
4
pGet('http://127.0.0.1:3000/users/4')
.then(function (data) {
console.log(data)
})

JQuery中的AJAX

标准版(提倡使用)

假设需要获取一个id为register_form的表单请求体数据,使用以下代码,下面有各个参数说明

1
2
3
4
5
6
7
8
9
10
11
12
 $('#register_form').on('submit', function (e) {
e.preventDefault()
var formData = $(this).serialize() //序列化表单值
$.ajax({
url: '/register',
type: 'post',
data: formData, //表单请求数据
dataType: 'json', //返回的数据类型
success: function (data) {
//成功执行函数,data为服务端响应数据
}
})

简化版

1
2
3
4
  $.post('http://localhost:3000/test_post',{name:'kobe',age:18},(data)=>{
console.log(data)
})
})

配合Promise使用

需掌握客户端模板引擎(ZP-P93-Promise使用场景)

1
2
3
4
5
6
7
8
9
10
11
var data = {}
$.get('http://127.0.0.1:3000/users/4')
.then(function (user) {
data.user = user
return $.get('http://127.0.0.1:3000/jobs')
})
.then(function (jobs) {
data.jobs = jobs
var htmlStr = template('tpl', data)
document.querySelector('#user_form').innerHTML = htmlStr
})

各参数含义

参考文档: w3school

  • url

    类型:String

    默认值: 当前页地址

    发送请求的地址。

  • type

    类型:String

    默认值: “GET”)。

    请求方式 (“POST” 或 “GET”), 默认为 “GET”。其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。

  • success

    类型:Function

    请求成功后的回调函数。

    参数:由服务器返回,并根据 dataType 参数进行处理后的数据;描述状态的字符串。

    这是一个 Ajax 事件。

  • dataType

    类型:String

    预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,比如 XML MIME 类型就被识别为 XML。在 1.4 中,JSON 就会生成一个 JavaScript 对象,而 script 则会执行这个脚本。随后服务器端返回的数据会根据这个值解析后,传递给回调函数。可用值:

    • “xml”: 返回 XML 文档,可用 jQuery 处理。
    • “html”: 返回纯文本 HTML 信息;包含的 script 标签会在插入 dom 时执行。
    • “script”: 返回纯文本 JavaScript 代码。不会自动缓存结果。除非设置了 “cache” 参数。注意:在远程请求时(不在同一个域下),所有 POST 请求都将转为 GET 请求。(因为将使用 DOM 的 script标签来加载)
    • “json”: 返回 JSON 数据 。
    • “jsonp”: JSONP 格式。使用 JSONP 形式调用函数时,如 “myurl?callback=?” jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。
    • “text”: 返回纯文本字符串
  • data

    类型:String

    发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:[“bar1”, “bar2”]} 转换为 ‘&foo=bar1&foo=bar2’。

数组reduce函数

【数组的reduce方法】:

  1. reduce方法接收一个函数作为累加器(“连续操作器”)。

  2. 数组中的每个值(从左到右)开始合并(不一定是相加!),最终为一个值。

  3. reduce为数组中的每一个元素【依次执行】回调函数,但不包括数组中被删除或从未被赋值的元素。

  4. reduce方法最终返回的是最后一次调用累加器的结果。

  5. 累加器函数接受四个参数:preValue, nowValue, nowIndex, arr

参数说明:

array.reduce(function(preValue, nowValue, nowIndex, arr){}, initialValue)

  • preValue:第一次调用时是初始值,如果初始值没有指定,就是数组中第一个元素的值,同时nowValue变为数组中的第二个值。以后调用时是上一次该回调函数的返回值;
  • nowValue:当前元素值;
  • nowIndex:当前索引;
  • arr:调用 reduce 的数组;

注意:

  1. 如果initialValue在调用时被提供,那么第一次的preValue就等于initialValue,nowValue等于数组中的第一个值;
  2. 如果initialValue未被提供,那么preValue等于数组中的第一个值,nowValue自动等于数组中的第二个值。
1
2
3
4
5
6
7
8
9
10
11
var abc = [1, 2, 3, 4, 5, 6]

var result = abc.reduce(function(preValue, nowValue, nowIndex, arr) {
console.log(preValue)
// console.log(nowValue)
// console.log(nowIndex)
// console.log(arr)
return preValue + nowValue
},1000)

console.log(result) //结果为1021

跨域问题总结

跨域产生的原因

浏览器为了安全,而采用的同源策略(Same origin policy)

同源策略含义

  1. 同源策略是由Netscape提出的一个著名的安全策略,现在所有支持JavaScript 的浏览器都会使用这个策略。
  2. Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
  3. 所谓同源是指:协议,域名(IP),端口必须要完全相同
    即:协议、域名(IP)、端口都相同,才能算是在同一个域里。
  4. 同源策略产生的问题
    • Cookie不能读取
    • DOM无法获得
    • Ajax请求不能发送

没有同源策略的危害:

用户信息可能被盗用,导致财产损失以及隐私泄露,不法分子会利用iframe标签获取正规网站的ui界面,然后通过后台服务器去截取用户信息,代码大致如下

1
2
3
4
5
6
7
<iframe id="baidu" src="https://www.baidu.com"></iframe>

<script type="text/javascript">
const iframe = window.frames['baidu']
const hhh = iframe.document.getElementById('输入敏感信息的input的id')
console.log(hhh.value)
</script>

解决跨域问题:

JSONP解决发送请求跨域问题:

JSONP是利用了标签请求资源不受同源策略限制的特点
JSONP需要前后端人员互相配合。

前端页面写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
<button id="btn">按钮</button>
<script type="text/javascript">
var btn = document.getElementById('btn');
btn.onclick = function () {
//1. 创建一个script标签
var script = document.createElement('script');
//2. 设置回调函数
window.getData = function (data) {
console.log(data);//拿到数据
}
//3. 设置script标签src属性,填写跨域请求的地址
script.src = 'http://localhost:3000/jsonp?callback=getData';
//4. 将script标签添加到body中生效
document.body.appendChild(script);
//5.不影响整体DOM结构,删除script标签
document.body.removeChild(script);
}
</script>
</body>

后端写法:

1
2
3
4
5
6
7
app.get('/jsonp', (req, res) => {
//解构赋值获取请求参数
const {callback} = req.query
//去数据库查找对应数据
const data = [{name: 'tom', age: 18}, {name: 'jerry', age: 20}];
res.send(callback + '(' + JSON.stringify(data) + ')'); //数据格式转换问题
})

后台配置cors解决跨域

1
2
//允许哪个url访问自己
res.set('Access-Control-Allow-Origin', 'http://localhost:63342');

使用代理服务器

nginx