本文是学习了网上的Ajax的课程总结,并加上了一些自己的总结。
Ajax(Asynchronous Javascript And XML),即是异步的JavaScript和XML,Ajax其实就是浏览器与服务器之间的一种异步通信方式。
XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。
原生 AJAX 语法 - XMLHttpRequest的基础使用:
AJAX 是浏览器与服务器通信的技术,采用 XMLHttpRequest 对象相关代码
axios 是对 XHR 相关代码进行了封装,让我们只关心传递的接口参数
学习 XHR 也是了解 axios 内部与服务器交互过程的真正原理
语法如下:
const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址')
xhr.addEventListener('loadend', () => {
// 响应结果
console.log(xhr.response)
})
xhr.send()
XMLHttpRequest.open()
初始化一个请求。
xhrReq.open(method, url);
xhrReq.open(method, url, async);//async表示是否异步执行操作
xhrReq.open(method, url, async, user);
xhrReq.open(method, url, async, user, password);
XMLHttpRequest.send()
发送请求。如果请求是异步的(默认),那么该方法将在请求发送后立即返回。例:
send()
send(body)//body表示在 XHR 请求中要发送的数据体
XMLHttpRequest.setRequestHeader()
设置 HTTP 请求标头的值。必须在 open() 之后、send() 之前调用 setRequestHeader()
方法。例:
setRequestHeader(header, value)//header表示要设置的标头的名称,value表示要设置的标头正文的值
查询参数原理要携带的位置和语法:XXXX?参数名1=值1&参数名2=值2
在调用 open 方法的时候,在 url? 后面按照指定格式拼接参数名和值
多个查询参数,用 URLSearchParams 把参数对象转成“参数名=值&参数名=值“格式的字符串,语法如下:
// 1. 创建 URLSearchParams 对象
const paramsObj = new URLSearchParams({
参数名1: 值1,
参数名2: 值2
})
// 2. 生成指定格式查询参数字符串
const queryString = paramsObj.toString()
// 结果:参数名1=值1&参数名2=值2
JavaScript 对象或值转换为 JSON 字符串:JSON.stringify()
JSON.stringify(value[, replacer [, space]])
解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象: JSON.parse()
JSON.parse(text)
JSON.parse(text, reviver)//reviver 可选--转换器,如果传入该参数 (函数),可以用来修改解析生成的原始值,调用时机在 parse 函数返回之前
步骤和语法:
需要设置请求头 Content-Type:application/json,来告诉服务器端,我们发过去的内容类型是 JSON 字符串,让他转成对应数据结构取值使用
要传递的请求体数据,把 JS 对象转成 JSON 字符串
原生 XHR 需要在 send 方法调用时,传入请求体携带
const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址')
xhr.addEventListener('loadend', () => {
console.log(xhr.response)
})
// 1. 告诉服务器,我传递的内容类型,是 JSON 字符串
xhr.setRequestHeader('Content-Type', 'application/json')
// 2. 准备数据并转成 JSON 字符串
const user = { username: '123', password: '123' }
const userStr = JSON.stringify(user)
// 3. 发送请求体数据
xhr.send(userStr)
Promise
对象表示异步操作最终的完成(或失败)以及其结果值。
Promise 的好处是什么?
逻辑更清晰(成功或失败会关联后续的处理函数)
Promise 管理异步任务
能解决回调函数地狱问题
一个 Promise
必然处于以下几种状态之一:
待定(pending):初始状态,既没有被兑现,也没有被拒绝。
已兑现(fulfilled):意味着操作成功完成。
已拒绝(rejected):意味着操作失败。
// 1. 创建 Promise 对象
const p = new Promise((resolve, reject) => {
// 2. 执行异步任务-并传递结果
// 成功调用: resolve(值) 触发 then() 执行
// 失败调用: reject(值) 触发 catch() 执行
setTimeout(() => {
// resolve('模拟AJAX请求-成功结果')
reject(new Error('模拟AJAX请求-失败结果'))
}, 2000)
})
// 3. 接收结果
p.then(result => {
// 成功
}).catch(error => {
// 失败
})
// 定义myAxios函数,接收配置对象,返回Promise对象
function myAxios(config) {
return new Promise((resolve, reject) => {
// 发起XHR请求,默认请求方法为GET
const xhr = new XMLHttpRequest()
// 判断有params选项,携带查询参数
if (config.params) {
// 使用URLSearchParams转换查询参数字符串,并携带到url上
const paramsObj = new URLSearchParams(config.params)
const queryString = paramsObj.toString()
// 把查询参数字符串,拼接在url?后面
config.url += `?${queryString}`
}
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
// 调用成功/失败的处理程序
// 2xx开头的都是成功响应状态码
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
// 判断有data选项,携带请求体
if (config.data) {
// 转换数据类型,在send中发送
const jsonStr = JSON.stringify(config.data)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(jsonStr)
} else {
// 如果没有请求体数据,正常的发起请求
xhr.send()
}
})
}
同步代码:逐行执行,需原地等待结果后,才继续向下执行
异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发回调函数传递结果(js中:setTimeout / setInterval,事件,AJAX)
概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
缺点:可读性差,异常无法捕获,耦合性严重,牵一发动全身
概念:依靠 then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束
细节:then() 回调函数中的返回值,会影响新生成的 Promise 对象最终状态和结果
好处:通过链式调用,解决回调函数嵌套问题
/**
* 目标:把回调函数嵌套代码,改成Promise链式调用结构
*/
let pname = ''
// 1. 得到-获取省份Promise对象
axios({url: '...'}).then(result => {
pname = result.data.list[0]
document.querySelector('.province').innerHTML = pname
// 2. 得到-获取城市Promise对象
return axios({url: '...', params: { pname }})
}).then(result => {
const cname = result.data.list[0]
document.querySelector('.city').innerHTML = cname
// 3. 得到-获取地区Promise对象
return axios({url: '...', params: { pname, cname }})
}).then(result => {
console.log(result)
const areaName = result.data.list[0]
document.querySelector('.area').innerHTML = areaName
})
概念:在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
做法:使用 async 和 await 解决回调地狱问题
核心代码:
/**
* 目标:掌握async和await语法,解决回调函数地狱
* 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
* 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
*/
// 1. 定义async修饰函数
async function getData() {
// 2. await等待Promise对象成功的结果
const pObj = await axios({url: '...'})
const pname = pObj.data.list[0]
const cObj = await axios({url: '...', params: { pname }})
const cname = cObj.data.list[0]
const aObj = await axios({url: '...', params: { pname, cname }})
const areaName = aObj.data.list[0]
document.querySelector('.province').innerHTML = pname
document.querySelector('.city').innerHTML = cname
document.querySelector('.area').innerHTML = areaName
}
getData()
用 try catch 捕获同步流程的错误:语句标记要尝试的语句块,并指定一个出现异常时抛出的响应
try {
// 要执行的代码
} catch (error) {
// error 接收的是,错误消息
// try 里代码,如果有错误,直接进入这里执行
}
概念:执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环
作用:事件循环负责执行代码,收集和处理事件以及执行队列中的子任务
原因:JavaScript 单线程(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型
执行同步代码,遇到异步代码交给宿主浏览器环境执行,异步有了结果后,把回调函数放入任务队列排队,当调用栈空闲后,反复调用任务队列里的回调函数
异步任务划分为了
宏任务:由浏览器环境执行的异步代码
微任务:由 JS 引擎环境执行的异步代码
执行第一个 script 脚本事件宏任务,里面同步代码遇到 宏任务/微任务 交给宿主环境,有结果回调函数进入对应队列。当执行栈空闲时,清空微任务队列,再执行下一个宏任务,从1再来
具体划分:
合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑
语法:
const p = Promise.all([Promise对象, Promise对象, ...])
p.then(result => {
// result 结果: [Promise对象成功结果, Promise对象成功结果, ...]
}).catch(error => {
// 第一个失败的 Promise 对象,抛出的异常对象
})
axios语法如下:
首先引入axios库
axios库地址:https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
axios({
method: '请求方法',(GET(默认)、POST、DELETE、PUT等)
url: '目标资源地址',
data: {
// `data` 是作为请求体被发送的数据
// 仅适用 'PUT', 'POST', 'DELETE 和 'PATCH' 请求方法
参数名: 值
...
},
params:{
// `params` 是与请求一起发送的 URL 参数
// 必须是一个简单对象或 URLSearchParams 对象
参数名: 值
...
}
})
.then(response => {
// 处理成功情况
console.log(response);
})
.catch(error => {
// 处理错误情况
console.log(error);
alert(error.response.data.message);
})
.finally(() => {
// 总是会执行
});
innerText会把字符串当做普通文本现在在ul标签之间, innerHTML会试着把字符串解析成标签, 如果是普通文本则显示普通文本字符串
表单标签设置或获取值用value属性, 其他标签用innerHTML/innerText, 进行设置/获取
作用:提取公共前缀地址,配置后 axios 请求时都会 baseURL + url
axios.defaults.baseURL = 'http://geek.itheima.net'
axios({
url: '目标资源地址',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`
}
})
在请求或响应被 then 或 catch 处理前拦截它们
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
//例:请求拦截器统一设置公共 headers 选项
const token = localStorage.getItem('token');
token && (config.headers.Authorization = `Bearer ${token}`);
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
//例如:身份验证失败,统一判断并做处理
if(error?.response?.status == 401){
alert('登录状态过期,请重新登录')
location.href = '../login/index.html'
}
return Promise.reject(error);
});
(由后端提供的描述接口的文章)--包含请求的 URL 网址,请求方法,请求参数和说明
接口文档:描述接口的文章(一般是后端工程师,编写和提供)
接口:指的使用 AJAX 和 服务器通讯时,使用的 URL,请求方法,以及参数。AJAX阶段接口文档
图片上传怎么做呢?
先获取图片文件对象
使用 FormData 表单数据对象装入(因为图片是文件而不是以前的数字和字符串了所以传递文件一般需要放入 FormData 以键值对-文件流的数据传递(可以查看请求体-确认请求体结构)
const fd = new FormData()
fd.append(参数名, 值)
提交表单数据对象,使用服务器返回图片 url 网址
// 2.2 选择文件并保存在 FormData
const file = e.target.files[0];
const fd = new FormData();
fd.append('image', file);
修改用户头像时,除了头像文件外,还要在 FormData 表单数据对象中携带用户名。
只读的localStorage
属性允许你访问一个Document 源(origin)的对象 Storage;存储的数据将保存在浏览器会话中。localStorage
类似 sessionStorage,但其区别在于:存储在 localStorage
的数据可以长期保留;而当页面会话结束——也就是说,当页面被关闭时,存储在 sessionStorage
的数据会被清除。
下面的代码片段访问了当前域名下的本地 Storage 对象,并通过 Storage.setItem() 增加了一个数据项目。
localStorage.setItem("myCat", "Tom");
该语法用于读取 localStorage
项,如下:
let cat = localStorage.getItem("myCat");
该语法用于移除 localStorage
项,如下:
localStorage.removeItem("myCat");
该语法用于移除所有的 localStorage
项,如下:
// 移除所有
localStorage.clear();
form-serialize 插件收集表单语法:
引入 form-serialize 插件到自己网页中
使用 serialize 函数
参数1:要获取的 form 表单标签对象(要求表单元素需要有 name 属性-用来作为收集的数据中属性名)
参数2:配置对象
hash:
true - 收集出来的是一个 JS 对象结构
false - 收集出来的是一个查询字符串格式
empty:
true - 收集空值
false - 不收集空值
首先引入
const form = document.querySelector('.art-form');
const data = serialize(form, {hash: true, empty: true});
判断 DOM 元素只剩一条可用,Element.children
是一个只读属性,返回 一个 Node 的子elements
// 4.5 删除最后一页的最后一条,需要自动向前翻页
const children = document.querySelector('.art-list').children
if (children.length === 1 && queryObj.page !== 1) {
queryObj.page--
document.querySelector('.page-now').innerHTML = `第 ${queryObj.page} 页`
}
Element.classList
是一个只读属性,返回一个元素 class
属性的动态 DOMTokenList 集合。
// 使用 classList API 移除、添加类值
div.classList.remove("foo");
div.classList.add("anotherclass");
self.location.href=”/url” 当前页面打开URL页面 location.href=”/url” 当前页面打开URL页面 windows.location.href=”/url” 当前页面打开URL页面,前面三个用法相同。 this.location.href=”/url” 当前页面打开URL页面 parent.location.href=”/url” 在父页面打开新页面 top.location.href=”/url” 在顶层页面打开新页面
location.search属性会返回URL的查询部分,即从"?"开始到URL末尾的部分,或者到"#"为止的部分。例如,如果当前URL是Example Domain,那么location.search的值将是?name=Tom&age=25。
URL对象可以解析整个URL,并提供了访问其各个部分的属性。URLSearchParams对象提供了一种处理查询字符串的便捷方式。例如,可以使用URLSearchParams对象来获取特定的查询参数值。
const paramsStr = location.search;
const params = new URLSearchParams(paramsStr);
对 DOM 元素绑定事件处理函数。所谓事件处理函数,就是处理用户操作的函数,不同的操作对应不同的名称。
在JavaScript中,有三种常用的绑定事件的方法:
在DOM元素中直接绑定
在DOM元素上绑定onclick、onmouseover、onmouseout等。
在JavaScript代码中绑定
在JavaScript代码中(即 script 标签内)绑定事件可以使JavaScript代码与HTML标签分离,文档结构清晰,便于管理和开发。
绑定事件监听函数
用 addEventListener() 或 attachEvent() 来绑定事件监听函数。
好处:
可以绑定多个事件;
可以解除相应的绑定removeEventListener()。
addEventListener() 语法:
element.addEventListener(event, function, useCapture)
event : (必需)事件名,支持所有 DOM事件 。(监听输入框实时改变使用input事件)
function:(必需)指定要事件触发时执行的函数。
useCapture:(可选)指定事件是否在捕获或冒泡阶段执行。true,捕获。false,冒泡。默认false。
attachEvent() 语法:
element.attachEvent(event, function)
event:(必需)事件类型。需加“on“,例如:onclick。
function:(必需)指定要事件触发时执行的函数。
事件委托就是利用冒泡的原理,把事件加到父元素或祖先元素上,触发执行效果。
好处:
提高JavaScript性能。事件委托可以显著的提高事件的处理速度,减少内存的占用。
动态的添加DOM元素,不需要因为元素的改动而修改事件绑定。
...
// 3.1 删除元素->点击(事件委托)
document.querySelector('.art-list').addEventListener('click', e => {
// 获取触发事件目标元素
// console.log(e.target)
// 判断点击的是删除元素
if (e.target.classList.contains('del')) {
// console.log('点击删除元素')
}
})
Window 接口的 setTimeout()
方法设置一个定时器,一旦定时器到期,就会执行一个函数或指定的代码片段。
setTimeout(() => {
console.log("延迟了 1 秒。");
}, 1000);
了解前后端分离项目中 token 的作用
概念:访问权限的令牌,本质上是一串字符串
创建:正确登录后,由后端签发并返回
作用:判断是否有登录状态等,控制访问权限
注意:前端只能判断 token 有无,而后端才能判断 token 的有效性
//设置token
const token = localStorage.setItem('token',res.data.token)
//获取token
const token = localStorage.getItem('token')
富文本:带样式,多格式的文本,在前端一般使用标签配合内联样式实现
富文本编辑器:用于编写富文本内容的容器
步骤:参考文档
1.引入 CSS 定义样式
2.定义 HTML 结构
3.引入 JS 创建编辑器
4.监听内容改变,保存在隐藏文本域(便于后期收集)
引入
// 弹窗插件
例如:
A simple success alert—check it out!
A simple danger alert—check it out!
// 需要先准备 alert 样式相关的 DOM
操作结果
/**
* BS 的 Alert 警告框函数,2秒后自动消失
* @param {*} isSuccess 成功 true,失败 false
* @param {*} msg 提示消息
*/
function myAlert(isSuccess, msg) {
const myAlert = document.querySelector('.alert')
myAlert.classList.add(isSuccess ? 'alert-success' : 'alert-danger')
myAlert.innerHTML = msg
myAlert.classList.add('show')
setTimeout(() => {
myAlert.classList.remove(isSuccess ? 'alert-success' : 'alert-danger')
myAlert.innerHTML = ''
myAlert.classList.remove('show')
}, 2000)
}
如何使用 Bootstrap 弹框呢?
先引入 bootstrap.css 和 bootstrap.js 到自己网页中
准备弹框标签,确认结构(可以从 Bootstrap 官方文档的 Modal 里复制基础例子)- 运行到网页后,逐一对应标签和弹框每个部分对应关系
例子:
直接出现/隐藏用属性方式控制,如果需要先执行一段 JS 逻辑再显示/隐藏就用 JS 方式控制
使用属性方式控制 Bootstarp 弹框的显示和隐藏
通过自定义属性,通知弹框的显示和隐藏,语法如下:
data-bs-toggle和data-bs-target属性绑定来控制弹框显示
data-bs-dismiss 属性来控制隐藏弹框,关闭标签所在的当前提示框
在显示和隐藏之前,需要执行 JS 代码逻辑,就使用 JS 方式 控制 Bootstrap 弹框显示和隐藏
语法如下:
// 创建弹框对象
const modalDom = document.querySelector('css选择器')
const modal = new bootstrap.Modal(modelDom)
// 显示弹框
modal.show()
// 隐藏弹框
modal.hide()
先准备对应的标签结构
设置延迟自动消失的时间
提示框内容
使用 JS 的方式,在 axios 请求响应成功时,展示结果
// 创建提示框对象
const toastDom = document.querySelector('css选择器')
const toast = new bootstrap.Toast(toastDom)
// 显示提示框
toast.show()
渲染数据(查)
核心思路:获取数据 -> 渲染数据
新增数据(增)
核心思路:准备页面标签 -> 收集数据提交(必须) -> 刷新页面列表(可选)
删除图书(删)
核心思路:绑定点击事件(获取要删除的图书唯一标识) -> 调用删除接口(让服务器删除此数据) -> 成功后重新获取并刷新列表
编辑图书(改)
核心思路:准备编辑图书表单 -> 表单回显正在编辑的数据 -> 点击修改收集数据 -> 提交到服务器保存 -> 重新获取并刷新列表