客户端:在因特网中,负责获取和消费资源的电脑
服务器:在因特网中,负责存放和对外提供资源的电脑
在个人的电脑中,通过安装浏览器的形式,访问服务器对外提供的各种资源
服务器的本质就是一台电脑,只不过,它的综合性能要比个人电脑强很多!
客户端与服务器之间的通信过程,分为 请求 --> 处理 --> 响应 三个步骤
网页中的每一个资源,都是通过 请求 --> 处理 --> 响应 的方式从服务器获取回来的
URL(全称是Uniform Resource Locator)中文叫统一资源定位符,用于标识互联网上每个资源的唯一存放位置。浏览器只有通过URL地址,才能正确定位资源的存放位置,从而成功访问到对应的资源
常见的URL:
URL地址一般由三部分组成:
服务器对外提供哪些资源?
网页中的数据,也是服务器对外提供的一种资源
骨架、颜值、行为皆为数据服务,数据,在网页中无处不在
如果要在网页中请求服务器上的数据资源,则需要用到 XMLHttpRequest 对象。 XMLHttpRequest(简称 xhr)是浏览器提供的 js 成员,通过它可以请求服务器上的数据资源
数据,也是服务器对外提供的一种资源。只要是资源,必然要通过 请求 --> 处理 --> 响应 的方式进行获取
客户端请求服务器时,请求的方式有很多种,最常见的两种请求方式分别为 get 和 post 请求
get 请求通常用于获取服务端资源(向服务器要资源)
post 请求通常用于向服务器提交数据(往服务器发送资源)
XMLHttpRequest (简称xhr)是浏览器提供的Javascript 对象,通过它可以请求服务器上的数据资源。jQuery中的Ajax函数,就是基于xhr对象封装出来的
基于xhr发送请求四步走:
<script>
// 1.创建 xhr 对象
let xhr = new XMLHttpRequest()
// 2.调用 open 函数,指定 请求方式与请求地址
let id = 1
let uname = "小白"
xhr.open("GET", `http://localhost:8086/api/getComment?id=${id}&name=${uname}`)
// 3.调用 send 函数
xhr.send()
// 4.监听 onreadystatechange 事件
xhr.onreadystatechange = function () {
// 监听 xhr 对象的请求状态 readystate 与服务器响应的状态 status
if (xhr.readyState === 4 && xhr.status === 200) {
// 获取服务器响应的数据
console.log(xhr.responseText)
}
}
script>
XMLHttpRequest对象的readyState属性,用来表示当前Ajax请求所处的状态。每个Ajax请求必然处于以下状态中的一个
值 | 状态 | 描述 |
---|---|---|
0 | UNSET | xhr对象已被创建,但尚未调用open方法(未初始化) |
1 | OPENED | open() 方法已被调用,但尚未发送请求(载入) |
2 | HEADERS_RECEIVED | 请求已经发送完成(载入完成) |
3 | LOADING | 接收到部分响应数据(交互) |
4 | DONE | 已经接收到了全部数据,并且连接已经关闭(完成) |
查询字符串(URL 参数)是指在URL的末尾加上用于向服务器发送信息的字符串(变量)
格式:键=值&中间用&符号分割 第一个使用?
URL地址中,只允许出现英文相关的字母、标点符号、数字,因此,在URL地址中不允许出现中文字符
如果URL中需要包含中文这样的字符,则必须对中文字符进行编码(转义)
URL编码原则:使用安全的字符(没有特殊用途或无特殊意义的可打印字符)去表示那些不安全的字符,也就是使用英文字符去表示非英文字符
浏览器提供了URL编码与解码的API,分别是:
<script>
let name = "小白"
// 编码
let encodeStr = encodeURI(name)
console.log(encodeStr)
// 解码
let decodeStr = decodeURI("%E5%B0%8F%E7%99%BD")
console.log(decodeStr)
script>
由于浏览器会自动对URL地址进行编码操作,因此,大多数情况下,是不需要关心URL地址的编码
<script>
// 1.创建 xhr 对象
let xhr = new XMLHttpRequest()
// 2.调用 open 函数,指定 请求方式与请求地址
xhr.open("POST", "http://localhost:8086/api/addComment")
// 3.设置Content-Type属性
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
// 4.调用 send 函数
xhr.send(`title=今天真是个好天气&author=xxx`)
// 5.监听 onreadystatechange 事件
xhr.onreadystatechange = function () {
// 监听 xhr 对象的请求状态 readystate 与服务器响应的状态 status
if (xhr.readyState === 4 && xhr.status === 200) {
// 获取服务器响应的数据
console.log(xhr.responseText)
}
}
script>
数据交换格式,就是服务器端与客户端之间进行数据传输与交换的格式
常用的两种数据交换格式分别是XML和JSON。其中XML用的较少,最为流行使用的数据交换格式就是JSON
<comment>
<title>必属精品title>
<author>who am iauthor>
comment>
JSON就是Javascript对象和数组的字符串表示法,它使用文本表示一个JS对象或数组的信息,因此,JSON的本质是字符串
作用:JSON 是一种轻量级的文本数据交换格式,在作用上类似于XML,专门用于存储和传输数据,但是JSON比XML更小、更快、更易解析
JSON两种结构
对象结构:对象结构在JSON中表示为{}
括起来的内容。数据结构为{ key: value, key: value, … }的键值对结构。其中,key必须是使用英文的双引号包裹的字符串,value 的数据类型可以是数字、字符串、布尔值、null、数组、对象6种类型
<script>
let user = {
"uname": "小白",
"age": 22,
"email": "[email protected]",
"hobbies": ["code", "swim", "girl"],
}
script>
数组结构:数组结构在JSON中表示为[]
括起来的内容。数据结构为[ “java”, “javascript”, 30, true …数组中数据的类型可以是数字、字符串、布尔值、null、数组、对象6种类型
<script>
let user = {
"uname": "小白",
"age": 22,
"email": "[email protected]",
"hobbies": ["code", "swim", "girl"],
"address": [
{
"province": "湖北省",
"city": "武汉市",
},
{
"province": "河南省",
"city": "洛阳市",
}
]
}
script>
注:
JSON和JS对象互转
JS对象转JSON字符串:JSON.stringify(JS对象)
JSON字符串转JS对象:JSON.parse(JS字符串)
<script>
let userObj = {
uname: '小白',
age: 22,
email: '[email protected]',
hobbies: ['code', 'swim', 'girl'],
address: [
{
province: '湖北省',
city: '武汉市',
},
{
province: '河南省',
city: '洛阳市',
}
]
}
// JS对象转JSON字符串
let userStr = JSON.stringify(userObj)
console.log(userStr)
// JSON字符串转JS对象
let obj = JSON.parse(userStr)
console.log(obj)
script>
旧版XMLHttpRequest的缺点
有时,Ajax操作很耗时,而且无法预知要花多少时间。如果网速很慢,用户可能要等很久。新版本的XMLHttpRequest对象,增加了timeout属性,可以设置HTTP请求的时限
具体语法:xhr.timeout=3000
对应的回调函数:xhr.ontimeout
<script>
let xhr = new XMLHttpRequest()
// 设置超时时间
xhr.timeout = 3000
// 超时以后的处理函数
xhr.ontimeout = function () {
alert("请求超时 请稍后重试")
}
xhr.open("GET", "http://localhost:8086/api/getComment")
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText)
}
}
script>
Ajax操作往往用来提交表单数据。为了方便表单处理,HTML5 新增了一个FormData对象,可以模拟表单操作:
<script>
// 1.创建 FormData 对象
let fd = new FormData()
// 2.调用 append 函数,向 ds 追加数据
fd.append("uname", "小黑")
fd.append("upwd", "8848")
// 3.创建 XHR 对象
let xhr = new XMLHttpRequest()
// 4.指定请求类型与URL地址
xhr.open("POST", "http://localhost:8086/api/formData")
// 5.提交 fd 对象,该方式与提交网页表单的效果完全一致
xhr.send(fd)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
}
}
script>
FormData对象也可以用来获取网页表单的值
<form id="user-form">
<label>
用户名:
<input type="text" name="uname" autocomplete="off">
label>
<label>
密码:
<input type="password" name="upwd">
label>
<input type="submit">
form>
<script>
let user_form = document.querySelector("#user-form")
user_form.addEventListener("submit", (e)=> {
e.preventDefault()
// 创建 FormData 快速获取 form 表单中的数据
let fd = new FormData(user_form)
let xhr = new XMLHttpRequest()
xhr.open("POST", "http://localhost:8086/api/formData")
xhr.send(fd)
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) console.log(JSON.parse(xhr.responseText))
}
})
script>
新版XMLHttpRequest对象,不仅可以发送文本信息,还可以上传文件
<form>
<input type="file" id="file">
<button id="btnUpload">上传文件button>
<br>
<p>
<img src="" alt="" id="img">
p>
form>
<script>
document.querySelector("#btnUpload").addEventListener("click", (e) => {
e.preventDefault()
// 获取到选择的文件列表
let files = document.querySelector("#file").files
if (files.length <= 0) return alert("请选择要上传的文件")
let fd = new FormData()
// 将用户选择的文件 添加到 FormData 中
fd.append("avatar", files[0])
let xhr = new XMLHttpRequest()
xhr.open("POST", "http://localhost:8086/api/upload")
xhr.send(fd)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
let data = JSON.parse(xhr.responseText)
if (data.status === 200) {
document.querySelector("#img").src = data.url
} else {
// 上传失败
console.log('图片上传失败!' + data.message);
}
}
}
})
script>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">上传文件h3>
div>
<div class="panel-body">
<div class="row" style="height: 60px;">
<div class="col-md-3">
<input type="file" id="file" class="form-control">
div>
<div class="col-md-3">
<button id="btnUpload" class="btn btn-primary">上传文件button>
div>
div>
<div class="row">
<div class="progress" style="width: 600px;margin-left: 15px;">
<div class="progress-bar progress-bar-striped active" style="width: 10%" id="percent">
10%
div>
div>
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<img src="" alt="..." id="img">
a>
div>
div>
div>
div>
<script>
document.querySelector("#btnUpload").addEventListener("click", (e) => {
e.preventDefault()
// 获取到选择的文件列表
let files = document.querySelector("#file").files
if (files.length <= 0) return alert("请选择要上传的文件")
let fd = new FormData()
// 将用户选择的文件 添加到 FormData 中
fd.append("avatar", files[0])
let xhr = new XMLHttpRequest()
// 监听文件上传进度
xhr.upload.onprogress = function (e) {
if (e.lengthComputable) {
let progress = Math.ceil((e.loaded / e.total) * 100)
// 动态设置进度条
document.querySelector("#percent").style.width = progress + "%"
document.querySelector("#percent").innerHTML = progress + "%"
// console.log(progress)
}
}
// 监听上传完成的事件
xhr.upload.onload = function () {
document.querySelector("#percent").classList.remove("progress-bar-striped")
document.querySelector("#percent").classList.add("progress-bar-success")
}
xhr.open("POST", "http://localhost:8086/api/upload")
xhr.send(fd)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
let data = JSON.parse(xhr.responseText)
if (data.status === 200) {
document.querySelector("#img").src = data.url
} else {
// 上传失败
console.log('图片上传失败!' + data.message);
}
}
}
})
script>
ajaxStart(callback):Ajax请求开始时,执行ajaxStart函数。可以在ajaxStart的callback中显示loading效果
ajaxStop(callback):Ajax请求结束时,执行ajaxStop函数。可以在ajaxStop的callback中隐藏loading效果
注:jQuery版本1.8之后,上述方法只能被附加到文档上
<body style="padding: 15px;">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">文件上传h3>
div>
<div class="panel-body">
<div class="row" style="height: 60px;">
<div class="col-md-3">
<input type="file" id="file" class="form-control">
div>
<div class="col-md-6">
<button class="btn btn-primary" id="btnUpload">上传button>
div>
div>
<div class="row" style="height: 300px;">
<div class="col-md-3">
<img src="./images/loading.gif" width="500" id="loading">
div>
<div class="col-md-3">
<img src="#" class="img-circle" alt="Image" id="img">
div>
div>
div>
div>
<script>
$("#loading").hide()
document.querySelector("#btnUpload").addEventListener("click", function () {
let files = $("#file")[0].files
if (files.length <= 0) {
return alert("请选择文件后在上传")
}
let fd = new FormData()
fd.append("avatar", files[0])
// 监听到 Ajax 请求发起了
$(document).ajaxStart(function () {
$("#loading").show()
})
// 监听到 Ajax 请求完成之后
$(document).ajaxStop(function () {
$("#loading").hide()
})
$.ajax({
method: "POST",
url: "http://localhost:8086/api/upload",
data: fd,
// 不修改 Content-Type 属性,使用 FormData 默认的 Content-Type 值
contentType: false,
// 不对 FormData 中的数据进行 url 编码,而是将 FormData 数据原样发送到服务器
processData: false,
success: (resp) => {
console.log(resp)
$("#img").attr("src", resp.url)
}
})
})
script>
body>
Ajax 的全称是 Asynchronous Javascript And XML(异步 JavaScript 和 XML)
简单理解:在网页中利用 XMLHttpRequest 对象和服务器进行数据交互的方式,就是Ajax
应用场景:检测用户名是否被占用、百度搜索关键词提示、数据的增删改查
浏览器中提供的 XMLHttpRequest 用法比较复杂,所以 jQuery 对 XMLHttpRequest 进行了封装,提供了一系列 Ajax 相关的函数,极大地降低了 Ajax 的使用难度
jQuery 中发起 Ajax 请求最常用的三个方法如下:
<script>
document.querySelector("div").addEventListener("click", () => {
$.get("http://localhost:8086/api/getGoods", { id: 1010 }, function (data) {
if (data.status !== 200) {
return alert("获取商品信息失败")
}
console.log(data)
})
})
script>
<script>
document.querySelector("div").addEventListener("click", () => {
$.post("http://localhost:8086/api/addGoods", { goodName: "小米手机", price: 1999, desc: "有点狠的真旗舰" }, function (data) {
if (data.status !== 200) {
return alert("添加商品信息失败")
}
console.log(data)
})
})
script>
<script>
document.querySelector("div").addEventListener("click", () => {
$.ajax({
// 请求地址
url: "http://localhost:8086/api/addGoods",
// 请求方式
method: "post",
// 期待服务器返回的数据格式
dataType: "json",
// 请求参数
data: { goodName: "红米K40", price: 1999, desc: "极致性价比" },
// 成功
success: (data) => {
console.log(data)
},
error: () => {
console.log("出错了")
},
// 头信息
headers: {
uname: "Mr zhang"
},
// 超时时间
timeout: 3000
})
})
script>
为了简化表单中数据的获取操作,jQuery 提供了 serialize() 函数,语法格式:$(selector).serialize()
<script>
document.querySelector("#formAddUser").addEventListener("submit", function (e) {
console.log($(this).serialize())
$.ajax({
url: "http://localhost:8086/api/user",
method: "post",
dataType: "json",
data: $(this).serialize(),
success: (data) => {
console.log(data)
},
error: () => {
console.log("出错了")
},
timeout: 3000
})
e.preventDefault()
})
script>
serialize() 函数的好处:可以一次性获取到表单中的所有的数据
使用 Ajax 请求数据时,被请求的 URL 地址,就叫做数据接口(简称接口)。同时,每个接口必须有请求方式
接口文档可以包含很多信息,也可以按需进行精简,不过,一个合格的接口文档,应该包含以下6项内容,从而为接口的调用提供依据:
模板引擎,顾名思义,它可以根据程序员指定的模板结构和数据,自动生成一个完整的HTML页面
模板引擎的优点
art-template 是一个简约、超快的模板引擎
官网地址
使用步骤
<script type="text/html" id="user-info">
<p>姓名:{{uname}}</p>
<p>年龄:{{age}}</p>
<p>婚姻状态:{{if isMarry}}已婚{{else}}未婚{{/if}}</p>
<p>出生日期:{{birthday | dateFormat 'yyyy-MM-dd hh:mm:ss'}}</p>
爱好:
<ul>
{{each hobbies}}
<li>{{$value}}</li>
{{/each}}
</ul>
script>
<script>
let data = {
uname: '小昭',
age: 22,
isMarry: false,
birthday: new Date("2002-1-15"),
hobbies: ['girl', 'code', 'swim']
}
// 过滤器定义
template.defaults.imports.dateFormat = function (date) {
let year = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
mouth = month < 10 ? "0" + month : month
day = day < 10 ? "0" + day : day
return `${year}-${mouth}-${day}`
};
// 调用 template 函数
let html = template('user-info', data)
// console.log(html)
document.querySelector('.box').innerHTML = html
script>
具体语法参考官方文档
如果两个页面的协议,域名和端口都相同,则两个页面具有相同的源
举例:http://www.bd.com/index.html
页面的同源检测
URL | 是否同源 | 原因 |
---|---|---|
http://www.bd.com/register.html | 是 | 同源(协议、域名、端口相同) |
https://www.bd.com/about.html | 否 | 协议不同(http 与 https) |
http://zhang.bd.com/index.html | 否 | 域名不同(www.bd.com 与 zhang.bd.com) |
http://www.bd.com:8006/login.html | 否 | 端口不同(默认的 80 端口与 8006 端口) |
http://www.bd.com:80/main.html | 是 | 同源(协议、域名、端口相同) |
同源策略(英文全称 Same origin policy)是浏览器提供的一个安全功能
浏览器规定,A 网站的 JavaScript,不允许和非同源的网站 B/C/D… 之间,进行资源的交互,例如:
同源指的是两个 URL 的协议、域名、端口一致,反之,则是跨域
出现跨域的根本原因:浏览器的同源策略不允许非同源的 URL 之间进行资源的交互
注:浏览器允许发起跨域请求,但是,跨域请求回来的数据,会被浏览器拦截,无法被页面获取到!
如何实现跨域数据请求?
现如今,实现跨域数据请求,最主要的两种解决方案,分别是 JSONP 和 CORS
jQuery 提供的 $.ajax() 函数,除了可以发起真正的 Ajax 数据请求之外,还可以发起 JSONP 数据请求
默认情况下,使用 jQuery 发起 JSONP 请求,会自动携带一个 callback=jQueryxxx 的参数,jQueryxxx 是随机生成的一个回调函数名称
在使用 jQuery 发起 JSONP 请求时,如果想要自定义 JSONP 的参数以及回调函数名称,可以通过如下两个参数来指定:
<script>
// 出现跨域问题
let keyword = "裤"
// $.get(`https://suggest.taobao.com/sug?q=${keyword}`, function (resp) {
// console.log(resp)
// })
// 发起JSONP请求
$.ajax({
url: `https://suggest.taobao.com/sug?q=${keyword}`,
// 表示要发送jsonp的数据请求
dataType: "jsonp",
// 发送到服务端的参数名称,默认值为 callback
jsonp: 'callback',
// 自定义的回调函数名称,默认值为 jQueryxxx 格式
jsonpCallback: 'search',
success: (resp) => {
console.log(resp)
}
})
script>
jQuery 中的 JSONP,也是通过