AJAX 概念和 axios 使用
什么是 AJAX
AJAX是浏览器与服务器进行数据通信的技术
使用浏览器的 XMLHttpRequest 对象 与服务器通信
axios使用
语法:
- 引入axios.js: http://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
- 使用 axios 函数
- 传入配置对象
- 再用.then回调函数接收结果,并做后续处理
js
axios({
url:'目标资源地址'
}).then((result)=>{
// 对服务器返回的数据做后续处理
})
认识 URL
- 定义:统一资源定位符,简称网址,用于定位网络中的资源(资源指的是:网页,图片,数据,视频,音频等等)
- 组成:协议,域名,资源路径(最重要的三个)
- http 协议:叫超文本传输协议,规定了浏览器和服务器传递数据的格式
- 域名:标记服务器在互联网中的方位
- 资源路径:标记资源在服务器中的方位
URL 查询参数
- 定义:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据
- 语法:在 url 网址后面用?拼接格式:http://xxxx.com/xxx/xxx?参数名1=值1\&参数名2=值2,参数名一般是后端规定的,值前端看情况传递即可
- 使用 params 选项,axios在运行时会把参数名和值拼接到url后面
js
axios({
url:'目标资源地址'
params:{
参数名:值
}
}).then(result=>{
// 对服务器返回的数据做后续处理
})
常用请求方法和数据提交
请求方法:对服务器资源,要执行的操作
- GET 获取数据
- POST 提交数据
- PUT 修改数据(全部)
- DELETE 删除数据
- PATCH 修改数据(部分)
js
axios({
url: '目标资源地址',
// 获取数据时GET可省略
method: '请求方法',
data: {
参数名: 值
}
}).then(result => {
// 对服务器返回的数据做后续处理
})
axios 错误处理
语法:在then方法的后面,通过点语法调用catch方法,传入回调参数并定义形参
js
axios({
// 请求选项
}).then(result => {
// 处理成功数据
}).catch(error => {
// 处理失败错误
})
HTTP 协议-请求报文
请求报文:浏览器按照协议规定的格式,发送给服务器的内容
这里的格式包含:
- 请求行:请求方法,URL,协议
- 请求头:以键值对的格式携带的附加信息,比如:Content-Type(指定了本次传递的内容类型)
- 空行:分割请求头,空行之后的是发送给服务器的资源
- 请求体:发送的资源
请求报文-错误排查
通过网络-Fetch/XHR中的请求载荷判断问题位置,再对照源代码找出问题
HTTP 协议-响应报文
- 定义:是服务器按照协议固定的格式,返回给浏览器的内容
- 组成:
- 响应行(状态行):协议,HTTP响应状态码,状态信息
- 响应头:以键值对的格式携带的附加信息,比如:Content-Type(告诉浏览器,本次返回的内容类型)
- 空行:分割响应头,控制之后的是服务器返回的资源
- 响应体:返回的资源
- HTTP响应状态码
- 用来表明请求是否成功完成
- 例如:404(客户端要找的资源,在服务器上不存在)2xx(成功)
接口文档
- 定义:描述接口的文章
- 接口:指的使用 AJAX 和 服务器通讯时,使用的 URL,请求方法,以及参数
- AJAX阶段接口文档
用户登录提示信息
好看的提示框
使用提示框,反馈提示消息,因为有4处地方需要提示框,所以封装成函数
- 获取提示框
- 封装提示框函数,重复调用,满足提示需求
功能: - 显示提示框
- 不同提示文字msg,和成功绿色失败红色isSuccess参数(true成功,false失败)
- 过2秒后,让提示框自动消失
js
function alertFn(msg, isSuccess) {
// 1> 显示提示框
myAlert.classList.add('show')
// 2> 实现细节
myAlert.innerText = msg
const bgStyle = isSuccess ? 'alert-success' : 'alert-danger'
myAlert.classList.add(bgStyle)
// 3> 过2秒隐藏
setTimeout(() => {
myAlert.classList.remove('show')
// 提示:避免类名冲突,重置背景色
myAlert.classList.remove(bgStyle)
}, 2000)
}
form-serialize 插件
使用 form-serialize 插件,快速收集目标表单范围内表单元素的值
使用 form-serialize 插件提供的 serialize 函数就可以办到
js
const data = serialize(form, { hash: true, empty: true })
参数1:要获取哪个表单的数据
- 表单元素设置name属性,值会作为对象的属性名
- 建议name属性的值,最好和接口文档参数名一致
参数2:配置对象
hash 设置获取数据结构 - true:JS对象(推荐)一般请求体里提交给服务器
- false: 查询字符串
empty 设置是否获取空值 - true: 获取空值(推荐)数据结构和标签结构一致
- false:不获取空值
Bootstrap 弹框
功能:不离开当前页面,显示单独内容,供用户操作
属性控制方式
如何使用 Bootstrap 弹框
- 先引入 bootstrap.css 和 bootstrap.js 到自己网页中
- 准备弹框标签,确认结构(可以从 Bootstrap 官方文档的 Modal 里复制基础例子)- 运行到网页后,逐一对应标签和弹框每个部分对应关系
- 通过自定义属性,通知弹框的显示和隐藏,语法如下:
html
<!-- data-bs-toggle 点击出现一个modal类型的弹框 data-bs-target指的是弹框对应哪个css选择器 -->
<button data-bs-toggle="modal" data-bs-target="css选择器">
显示弹框
</button>
<!-- data-bs-dismiss 隐藏弹框 -->
<button data-bs-dismiss="modal">Close</button>
js控制方式
js
// 创建弹框对象
const modalDom = document.querySelector('css选择器')
const modal = new bootstrap.Modal(modelDom)
// 显示弹框
modal.show()
// 隐藏弹框
modal.hide()
什么时候用属性控制,什么时候用 JS 控制 Bootstrap 弹框的显示/隐藏
直接出现/隐藏用属性方式控制,如果需要先执行一段 JS 逻辑再显示/隐藏就用 JS 方式控制
图片上传
- 先获取图片文件对象
- 使用 FormData 表单数据对象装入(因为图片是文件而不是以前的数字和字符串了所以传递文件一般需要放入 FormData 以键值对-文件流的数据传递(可以查看请求体-确认请求体结构)
js
const fd = new FormData()
fd.append(参数名, 值)
- 提交表单数据对象,使用服务器返回图片 url 网址
XMLHttpRequest
- AJAX 是浏览器与服务器通信的技术,采用 XMLHttpRequest 对象相关代码
- axios 是对 XHR 相关代码进行了封装,让我们只关心传递的接口参数
- 学习 XHR 也是了解 axios 内部与服务器交互过程的真正原理
- 语法:
js
const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址')
xhr.addEventListener('loadend', () => {
// 响应结果
console.log(xhr.response)
})
xhr.send()
XMLHttpRequest - 查询参数
在调用 open 方法的时候,在 url? 后面按照指定格式拼接参数名和值
用 URLSearchParams 把参数对象转成"参数名=值&参数名=值"格式的字符串,语法如下:
js
// 1. 创建 URLSearchParams 对象
const paramsObj = new URLSearchParams({
参数名1: 值1,
参数名2: 值2
})
// 2. 生成指定格式查询参数字符串
const queryString = paramsObj.toString()
// 结果:参数名1=值1&参数名2=值2
XMLHttpRequest - 数据提交
- 需要自己设置请求头 Content-Type:application/json,来告诉服务器端,我们发过去的内容类型是 JSON 字符串,让他转成对应数据结构取值使用
- 前端要传递的请求体数据,需要我们自己转换
- 原生 XHR 需要在 send 方法调用时,传入请求体携带
语法:
js
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: 'itheima007', password: '7654321' }
const userStr = JSON.stringify(user)
// 3. 发送请求体数据
xhr.send(userStr)
Promise
Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值
语法:
js
// 1. 创建 Promise 对象
const p = new Promise((resolve, reject) => {
// 2. 执行异步任务-并传递结果
// 成功调用: resolve(值) 触发 then() 执行
// 失败调用: reject(值) 触发 catch() 执行
})
// 3. 接收结果
p.then(result => {
// 成功
}).catch(error => {
// 失败
})
Promise 的状态
三种状态:
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝
- 已兑现(fulfilled):操作成功完成
- 已拒绝(rejected):操作失败
注:每个 Promise 对象必定处于三种状态之一,每个 Promise 对象一旦被兑现/拒绝,那就是已敲定,状态无法再被改变
封装_简易axios-获取省份列表
语法:
js
function myAxios(config) {
return new Promise((resolve, reject) => {
// 2. 发起XHR请求,默认请求方法为GET
const xhr = new XMLHttpRequest()
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
// 3. 调用成功/失败的处理程序
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
xhr.send()
})
}
myAxios({
url: 'http://hmajax.itheima.net/api/province'
}).then(result => {
console.log(result)
document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error => {
console.log(error)
document.querySelector('.my-p').innerHTML = error.message
})
封装_简易axios-获取地区列表
语法:
js
function myAxios(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
// 1. 判断有params选项,携带查询参数
if (config.params) {
// 2. 使用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', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
xhr.send()
})
}
myAxios({
url: 'http://hmajax.itheima.net/api/area',
params: {
pname: '陕西省',
cname: '渭南市'
}
}).then(result => {
console.log(result)
document.querySelector('.my-p').innerHTML = result.list.join('<br>')
})
封装_简易axios-注册用户
语法:
js
function myAxios(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
if (config.params) {
const paramsObj = new URLSearchParams(config.params)
const queryString = paramsObj.toString()
config.url += `?${queryString}`
}
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
// 1. 判断有data选项,携带请求体
if (config.data) {
// 2. 转换数据类型,在send中发送
const jsonStr = JSON.stringify(config.data)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(jsonStr)
} else {
// 如果没有请求体数据,正常的发起请求
xhr.send()
}
})
}
document.querySelector('.reg-btn').addEventListener('click', () => {
// 3. 使用myAxios函数,完成注册用户
myAxios({
url: 'http://hmajax.itheima.net/api/register',
method: 'POST',
data: {
username: 'itheima999',
password: '666666'
}
}).then(result => {
console.log(result)
}).catch(error => {
console.dir(error)
})
})
同步代码和异步代码
同步代码:逐行执行,需原地等待结果后,才继续向下执行
异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发回调函数传递结果
回调函数地狱
概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
缺点:可读性差,异常无法捕获,耦合性严重,牵一发动全身
js
axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => {
const pname = result.data.list[0]
document.querySelector('.province').innerHTML = pname
// 获取第一个省份默认下属的第一个城市名字
axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } }).then(result => {
const cname = result.data.list[0]
document.querySelector('.city').innerHTML = cname
// 获取第一个城市默认下属第一个地区名字
axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } }).then(result => {
document.querySelector('.area').innerHTML = result.data.list[0]
})
})
})
Promise-链式调用
- 概念:依靠 then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束
- 细节:then() 回调函数中的返回值,会影响新生成的 Promise 对象最终状态和结果
- 好处:通过链式调用,解决回调函数嵌套问题
js
// 1. 创建Promise对象-模拟请求省份名字
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('北京市')
}, 2000)
})
// 2. 获取省份名字
const p2 = p.then(result => {
console.log(result)
// 3. 创建Promise对象-模拟请求城市名字
// return Promise对象最终状态和结果,影响到新的Promise对象
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result + '--- 北京')
}, 2000)
})
})
// 4. 获取城市名字
p2.then(result => {
console.log(result)
})
// then()原地的结果是一个新的Promise对象
console.log(p2 === p)
async 函数和 await
概念:在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
js
async function getData() {
const pObj = await axios({url: 'http://hmajax.itheima.net/api/province'})
const pname = pObj.data.list[0]
const cObj = await axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
const cname = cObj.data.list[0]
const aObj = await axios({url: 'http://hmajax.itheima.net/api/area', 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()
async 函数和 await 捕获错误
try 和 catch 的作用:语句标记要尝试的语句块,并指定一个出现异常时抛出的响应
js
try {
// 要执行的代码
} catch (error) {
// error 接收的是,错误消息
// try 里代码,如果有错误,直接进入这里执行
// 如果try里某行代码报错后,try中剩余的代码不会执行了
}
事件循环(EventLoop)
- 作用:事件循环负责执行代码,收集和处理事件以及执行队列中的子任务
- 原因:JavaScript 单线程(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型
- 概念:执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环
宏任务与微任务
- ES6 之后引入了 Promise 对象, 让 JS 引擎也可以发起异步任务
- promise本身是同步的,而then和catch回调函数是异步的
- 异步任务划分为
- 宏任务:由浏览器环境执行的异步代码 如:JS 执行脚本事件,setTimeout/setInterval,AJAX请求完成事件,用户交互事件
- 微任务:由 JS 引擎环境执行的异步代码 如:Promise对象.then()的回调
注:一个宏任务包含微任务队列,他们之间是包含关系,不是并列关系
Promise.all 静态方法
- 概念:合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑
- 语法:
js
const p = Promise.all([Promise对象, Promise对象, ...])
p.then(result => {
// result 结果: [Promise对象成功结果, Promise对象成功结果, ...]
}).catch(error => {
// 第一个失败的 Promise 对象,抛出的异常对象
})
01_Node.js入门
什么是 Node.js
- Node.js 是一个独立的 JavaScript 运行环境,能独立执行 JS 代码,因为这个特点,它可以用来编写服务器后端的应用程序
- Node.js 作用除了编写后端应用程序,也可以对前端代码进行压缩,转译,整合等等,提高前端开发和运行效率
- Node.js 基于Chrome V8 引擎封装,独立执行 JS 代码,但是语法和浏览器环境的 V8 有所不同,没有 document 和 window 但是都支持 ECMAScript 标准的代码语法
- Node.js 执行目标 JS 文件,需要使用 node xxx.js 命令来执行
fs模块-读写文件
- 模块:类似插件,封装了方法和属性供我们使用
- fs 模块:封装了与本机文件系统进行交互的,方法和属性
- 语法:
- 加载 fs 模块,得到 fs 对象
js
const fs = require('fs')
- 写入文件内容语法:
js
fs.writeFile('文件路径', '写入内容', err => {
// 写入后的回调函数
})
- 读取文件内容的语法:
js
fs.readFile('文件路径', (err, data) => {
// 读取后的回调函数
// data 是文件内容的 Buffer 数据流
// data 是 buffer 16 进制数据流对象 .toString() 转换成字符串
})
path模块-路径处理
建议:在Node.js代码中,使用绝对路径
补充:__dirname (动态获取当前文件所在文件夹的绝对路径)
注意:path.join()会使用特定于平台的分隔符,作为定界符,将所有给定的路径片段连接在一起
案例-压缩前端html
- 读取源 html 文件内容
- 正则替换字符串
- 写入到新的 html 文件中,并运行查看是否能正常打开网页
js
const fs = require('fs')
const path = require('path')
// 1.1 读取 public/index.html 内容
fs.readFile(path.join(__dirname, 'public', 'index.html'), (err, data) => {
const htmlStr = data.toString()
// 1.2 使用正则替换内容字符串里的,回车符\r 换行符\n
const resultStr = htmlStr.replace(/[\r\n]/g, '')
// 1.3 确认后,写入到 dist/index.html 内
fs.writeFile(path.join(__dirname, 'dist', 'index.html'), resultStr, err => {
if (err) console.log(err)
else console.log('压缩成功')
})
})
URL中的端口号
- URL 是统一资源定位符,简称网址,用于访问网络上的资源
- 端口号的作用:标记服务器里对应的服务程序,值为(0-65535 之间的任意整数)
- 注意:http 协议,默认访问的是 80 端口
- Web服务:一个程序,用于提供网上信息浏览功能
- 注意:0-1023 和一些特定的端口号被占用,我们自己编写服务程序请避开使用
http模块-创建Web服务
- 引入 http 模块,创建 Web 服务对象
- 监听 request 请求事件,设置响应头和响应体
- 启动 Web 服务监听对应端口号
- 浏览器请求 http://localhost:3000 测试
注意:本机的域名叫做 localhost
Node.js模块化
模块化简介
- 在 Node.js 中每个文件都被当做是一个独立的模块,模块内定义的变量和函数都是独立作用域的
- 项目是由多个模块组成的,每个模块之间都是独立的
- 模块内的属性和函数都是私有的,如果对外使用,需要使用标准语法导出和导入才可以,而这个标准叫 CommonJS 标准
- 导出语法:
js
module.exports = {
对外属性名: 模块内私有变量
}
- 导入语法:
js
const 变量名 = require('模块名或路径')
// Node.js 环境内置模块直接写模块名(例如:fs,path,http)
// 自定义模块:写模块文件路径(例如:./utils.js)
变量名的值接收的就是目标模块导出的对象
ECMAScript标准-默认导出和导入
- 导出语法:
js
export default {
对外属性名: 模块内私有变量
}
- 导入语法:
js
import 变量名 from '模块名或路径'
变量名的值接收的就是目标模块导出的对象
- 注意:Node.js 默认只支持 CommonJS 标准语法,如果想要在当前项目环境下使用 ECMAScript 标准语法,请新建 package.json 文件设置 type: 'module' 来进行设置
json
{ "type": "module" }
ECMAScript标准-命名导出和导入
- 命名导出语法:
js
export 修饰定义语句
- 命名导入语法:
js
import { 同名变量 } from '模块名或路径'
注意:同名变量指的是模块内导出的变量名
- 与默认导出如何选择:
- 按需加载,使用命名导出和导入
- 全部加载,使用默认导出和导入
包的概念
- 包:将模块,代码,其他资料整合成一个文件夹,这个文件夹就叫包
- 包分类:
- 项目包:主要用于编写项目和业务逻辑
- 软件包:封装工具和方法进行使用
- 包要求:根目录中,必须有 package.json 文件(记录包的清单信息)
- 包使用:在引入一个包文件夹到代码中,默认引入的是包文件节下的 index.js 模块文件里导出的对象,如果没有 index.js 文件,则会引入 package.json 里 main 属性指定的文件模块导出的对象
npm软件包管理器
npm 使用步骤:
- 初始化清单文件: npm init -y (得到 package.json 文件,有则跳过此命令) 注意 -y 就是所有选项用默认值,所在文件夹不要有中文/特殊符号,建议英文和数字组成,因为 npm 议用英文和数字或者下划线中划线
- 下载软件包:npm i 软件包名称
- 使用软件包
npm安装所有依赖
拿到了一个别人编写的项目,但是没有 node_modules,如何得到需要的所有依赖软件包?直接在项目目录下,运行终端命令:npm i 即可安装 package.json 里记录的所有包和对应版本到本项目中的 node_modules
npm全局软件包-nodemon
- 软件包区别:
- 本地软件包:当前项目内使用,封装属性和方法,存在于 node_modules
- 全局软件包:本机所有项目使用,封装命令和工具,存在于系统设置的位置
- nodemon 作用:替代 node 命令,检测代码更改,自动重启程序
- 使用:
- 安装:npm i nodemon -g (-g 代表安装到全局环境中)
- 运行:nodemon 待执行的目标 js 文件
Node.js概念和常用命令总结
- 执行js文件 node xxx
- 初始化 package.json npm init -y
- 下载本地软件包 npm i 软件包名
- 下载全局软件包 npm i 软件包名 -g
- 删除软件包 npm uni 软件包名
案例-黑马头条数据管理平台
技术
- 基于 Bootstrap 搭建网站标签和样式
- 集成 wangEditor 插件实现富文本编辑器
- 使用原生 JS 完成增删改查等业务
- 基于 axios 与黑马头条线上接口交互
- 使用 axios 拦截器进行权限判断
目录管理
建议这样管理,方便查找
- assets:资源文件夹(图片,字体等)
- lib:资料文件夹(第三方插件,例如:form-serialize)
- page:页面文件夹
- utils:实用程序文件夹(工具插件)
验证码登录
在request.js中配置 axios 请求基地址
作用:提取公共前缀地址,配置后 axios 请求时都会 baseURL + url
语法:
js
axios.defaults.baseURL = 'https://geek.itheima.net'
token 的介绍
概念:访问权限的令牌,本质上是一串字符串
创建:正确登录后,由后端签发并返回
作用:判断是否有登录状态等,控制访问权限
注意:前端只能判断 token 有无,而后端才能判断 token 的有效性
js
const token = localStorage.getItem('token')
// 没有 token 令牌字符串,则强制跳转登录页
if (!token) {
location.href = '../login/index.html'
}
个人信息设置和 axios 请求拦截器
axios 请求拦截器:发起请求之前,调用的一个函数,对请求参数进行设置
什么时候使用:有公共配置和设置时,统一设置在请求拦截器中
拦截器
js
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})
js
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
// 统一携带 token 令牌字符串在请求头上
const token = localStorage.getItem('token')
token && (config.headers.Authorization = `Bearer ${token}`)
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
axios 响应拦截器和身份验证失败
axios 响应拦截器:响应回到 then/catch 之前,触发的拦截函数,对响应结果统一处理
js
axios.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
return result;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么,例如:判断响应状态为 401 代表身份验证失败
if (error?.response?.status === 401) {
alert('登录状态过期,请重新登录')
window.location.href = '../login/index.html'
}
return Promise.reject(error);
});
优化 axios 响应结果
思路:其实就是在响应拦截器里,response.data 把后台返回的数据直接取出来统一返回给所有使用这个 axios 函数的逻辑页面位置的 then 的形参上
好处:可以让逻辑页面少点一层 data 就能拿到后端返回的真正数据对象
发布文章-富文本编辑器
富文本:带样式,多格式的文本,在前端一般使用标签配合内联样式实现
富文本编辑器:用于编写富文本内容的容器
使用:wangEditor 插件
- 引入 CSS 定义样式
- 定义 HTML 结构
- 引入 JS 创建编辑器
- 监听内容改变,保存在隐藏文本域(便于后期收集)
发布文章-频道列表
- 获取频道列表数据
- 展示到下拉菜单中
发布文章-封面设置
- 准备标签结构和样式
- 选择文件并保存在 FormData
- 单独上传图片并得到图片 URL 地址
- 回显并切换 img 标签展示(隐藏 + 号上传标签)
注:图片地址临时存储在 img 标签上,并未和文章关联保存
发布文章-收集并保存
- 基于 form-serialize 插件收集表单数据对象
- 基于 axios 提交到服务器保存
- 调用 Alert 警告框反馈结果给用户
- 重置表单并跳转到列表页
内容管理-文章列表展示
- 准备查询参数对象
- 获取文章列表数据
- 展示到指定的标签结构中1. 准备查询参数对象
js
const queryObj = {
status: '', // 筛选状态
channel_id: '', // 频道id
page: 1, // 当前页码
per_page: 2 // 每页条数
}
let totalCount = 0 // 总条数
内容管理-筛选功能
- 设置频道列表数据
- 监听筛选条件改变,保存查询信息到查询参数对象
- 点击筛选时,传递查询参数对象到服务器
- 获取匹配数据,覆盖到页面展示
内容管理-分页功能
- 保存并设置文章总条数
- 点击下一页,做临界值判断,并切换页码参数请求最新数据
- 点击上一页,做临界值判断,并切换页码参数请求最新数据
内容管理-删除功能
- 关联文章 id 到删除图标
- 点击删除时,获取文章 id
- 调用删除接口,传递文章 id 到服务器
- 重新获取文章列表,并覆盖展示
内容管理-删除最后一条
- 删除成功时,判断 DOM 元素只剩一条,让当前页码 page--
- 注意,当前页码为 1 时不能继续向前翻页
- 重新设置页码数,获取最新列表展示
内容管理-编辑文章-回显
- 页面跳转传参(URL 查询参数方式)
- 发布文章页面接收参数判断(共用同一套表单)
- 修改标题和按钮文字
- 获取文章详情数据并回显表单
js
const params = `?id=1001&name=xiaoli`
// 查询参数字符串 => 查询参数对象
const result = new URLSearchParams(params)
// 需要遍历使用
result.forEach((value, key) => {
console.log(value, key)
// 1001 id
// xiaoli name
})
内容管理-编辑文章-保存
- 判断按钮文字,区分业务(因为共用一套表单)
- 调用编辑文章接口,保存信息到服务器
- 基于 Alert 反馈结果消息给用户
退出登录
- 绑定点击事件
- 清空本地缓存,跳转到登录页面