技术:
- 基于 Bootstrap 搭建网站标签和样式
 - 集成 <font title='red'>wangEditor 插件 </font > 实现富文本编辑器
 - 使用原生 JS 完成 <font title='red'> 增删改查 </font > 等业务
 - 基于 axios 与黑马头条线上接口交互
 - 使用 <font title='red'>axios 拦截器 </font > 进行权限判断
 
项目准备:准备配套的素材代码
包含:html,css,js,静态图片,第三方插件等等

目录管理:建议这样管理,方便查找
- assets:资源文件夹 (图片,字体等)
 - lib:资料文件夹 (第三方插件,例如:form-serialize)
 - <font title='red'>page</font>:页面文件夹
 - <font title='red'>utils</font>:使用程序文件夹 (工具插件)
 
# 验证码登录
目标:完成验证码登录,后端设置验证码默认为 246810
步骤:
在 utils /request.js 配置 axios 请求 <font title='red'> 基地址 </font>.
- 作用:提取公共前缀地址,配置后 axios 请求时都会 baseURL + url
 
axios.defaults.baseURL = 'http://geek.itheima.net'
- 解决的场景:防止后端频繁更换基地址,解决需要重新部署很多个页面的情况!
 
收集手机号和验证码数据
基于 axios 调用验证码登录接口
使用 Boostrap 的 Alert 警告框反馈结果给用户
![image-20230815102437892]()
request.js
//axios 公共配置 | |
// 基地址 | |
axios.defaults.baseURL = 'http://geek.itheima.net'  | 
index.js
/** | |
* 目标 1:验证码登录  | |
* 1.1 在 utils/request.js 配置 axios 请求基地址  | |
* 1.2 收集手机号和验证码数据  | |
* 1.3 基于 axios 调用验证码登录接口  | |
* 1.4 使用 Bootstrap 的 Alert 警告框反馈结果给用户  | |
*/  | |
// 1.2 收集手机号和验证码数据 | |
document.querySelector('.btn').addEventListener('click', function () {  | |
const form = document.querySelector('.login-form')  | |
const data = serialize(form, { hash: true, empty: true })  | |
console.log(data)  | |
    // 1.3 基于 axios 调用验证码登录接口 | |
axios({  | |
url: '/v1_0/authorizations',  | |
method: 'POST',  | |
data  | |
}).then(result => {  | |
console.log(result)  | |
myAlert(true, '登录成功')  | |
}).catch(err => {  | |
myAlert(false, '登录失败')  | |
console.dir(err.response.data.message)// 手机号或验证码格式不正确  | |
})  | |
})  | 
alert.js
// 弹窗插件 | |
// 需要先准备 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)  | |
} | 
效果:

# token 的介绍
概念:访问权限的 <font title='red'> 令牌 </font>,本质上是一串 <font title='red'> 字符串 </font>.
创建:正确登录后,由后端签发并返回

作用:判断是否有 <font title='red'> 登录状态 </font > 等,控制访问权限

注意:<font title='red'> 前端 </font > 只能判断 token<font title='red'> 有无 </font>,而 < font title='red'> 后端 </font > 才能判断 token 的 < font title='red'> 有效性 </font>。
# token 的使用
目标:只有登录状态,才可以访问内容页面
步骤:
在 utils /auth.js 中判断无 token 令牌字符串,则强制跳转到登录页 (手动修改地址栏测试)
- 核心代码
 
const token = locaStorage.getItem('token')
// 没有 token 令牌字符串,则强制跳转登录页if(!token) {
location.href = '../login/index.html'
}在登录成功后,保存 token 令牌字符串到本地,再跳转到首页 (手动修改地址栏测试)
- 核心代码
 
// 登录成功后,保存 token 令牌字符串到本地,并跳转到内容列表页面localStorage.setItem('token', result.data.data.token)
setTimeout(() => {
location.href = '../content/index.html'
}, 2000)
auth.js
// 权限插件(引入到了除登录页面,以外的其他所有页面) | |
/** | |
* 目标 1:访问权限控制  | |
* 1.1 判断无 token 令牌字符串,则强制跳转到登录页  | |
* 1.2 登录成功后,保存 token 令牌字符串到本地,并跳转到内容列表页面  | |
*/  | |
//  1.1 判断无 token 令牌字符串,则强制跳转到登录页 | |
const token = localStorage.getItem('token')  | |
if (!token) {  | |
location.href = '../login/index.html'  | |
} | 
效果:

index.js
/** | |
* 目标 1:验证码登录  | |
* 1.1 在 utils/request.js 配置 axios 请求基地址  | |
* 1.2 收集手机号和验证码数据  | |
* 1.3 基于 axios 调用验证码登录接口  | |
* 1.4 使用 Bootstrap 的 Alert 警告框反馈结果给用户  | |
*/  | |
// 1.2 收集手机号和验证码数据 | |
document.querySelector('.btn').addEventListener('click', function () {  | |
const form = document.querySelector('.login-form')  | |
const data = serialize(form, { hash: true, empty: true })  | |
console.log(data)  | |
    // 1.3 基于 axios 调用验证码登录接口 | |
axios({  | |
url: '/v1_0/authorizations',  | |
method: 'POST',  | |
data  | |
}).then(result => {  | |
        // 1.4 使用 Bootstrap 的 Alert 警告框反馈结果给用户 | |
console.log(result)  | |
myAlert(true, '登录成功')  | |
        // 登录成功后,保存 token 令牌字符串到本地,并跳转到内容列表页面 | |
localStorage.setItem('token', result.data.data.token)  | |
setTimeout(() => {  | |
location.href = '../content/index.html'  | |
}, 2000)  | |
}).catch(err => {  | |
myAlert(false, '登录失败')  | |
console.dir(err.response.data.message)// 手机号或验证码格式不正确  | |
})  | |
})  | 
效果:

总结:
- token 的作用?
 
- <font title='red'> 判断 </font > 用户是否有 < font title='red'> 登录状态 </font > 等
 - token 的注意:
 
- <font title='red'> 前端 </font > 只能判断 token 的 < font title='red'> 有无 </font>.
 - <font title='red'> 后端 </font > 通过解密可以提取 token 字符串的原始信息,判断 < font title='red'> 有效性 </font>.
 
# 个人信息设置和 axios 请求拦截器
需求:设置用户昵称
语法:axioos 可以在 headers 选项传递请求头参数
问题:很多接口,都需要携带 token 令牌字符串
解决:在请求拦截器统一设置公共 headers 选项
axios 请求拦截器:发起请求之前,触发的配置函数,对 <font title='red'> 请求参数 </font > 进行额外配置
- 基本结构
 
// 添加请求拦截器 | |
axios.interceptors.request.use(function(config) {  | |
    // 在发送请求之前做些什么 | |
    return config | |
},function(error) {  | |
    // 对请求错误做些什么 | |
return Promise.reject(error)  | |
})  | 

auth.js
/** | |
* 目标 2:设置个人信息  | |
* 2.1 在 utils/request.js 设置请求拦截器,统一携带 token  | |
* 2.2 请求个人信息并设置到页面  | |
*/  | |
axios({  | |
url: '/v1_0/user/profile'  | |
}).then(result => {  | |
console.log(result)  | |
document.querySelector('.nick-name').innerHTML = result.data.data.name  | |
})  | 
request.js
// 添加请求拦截器 | |
axios.interceptors.request.use(function(config) {  | |
    // 在发送请求之前做些什么 | |
    // 统一携带 token 令牌字符串在请求头上 | |
const token = localStorage.getItem('token')  | |
    // 判断如果 token 不为空的话则将每个 header 请求头都加上 token | |
token && (config.headers.Authorization = `Bearer ${token}`)  | |
    return config | |
},function(error) {  | |
    // 对请求错误做些什么 | |
return Promise.reject(error)  | |
})  | 
结果:

<span alt='solid'> 总结 </span>:
- 什么是 axios 请求拦截器?
 
- 发起请求之前,调用的一个 <font title='red'> 函数 </font>,对 < font title='red'> 请求参数 </font > 进行 < font title='red'> 设置 </font>.
 - axios 请求拦截器,什么时候使用?
 
- 有 <font title='red'> 公共配置 </font > 和设置时,统一设置在请求拦截器中
 
# axios 响应拦截器和身份验证失败
axios 响应拦截器:相应回到 then /catch 之前,触发的 <font title='red'> 拦截函数 </font>,对 < font title='red'> 响应结果统一处理 </font>.
例如:身份验证失败,统一判断并做处理。

- 基本结构
 
// 添加响应拦截器 | |
axios.interceptors.response.use(function(config) {  | |
    // 2xx 范围内的状态码都会触发该函数 | |
    // 对响应数据做点什么 | |
    return config | |
},function(error) {  | |
    // 超出 2xx 范围的状态码都会触发该函数 | |
    // 对响应错误做点什么 例如:统一对 401 身份验证失败情况做出处理 | |
console.dir(error)  | |
return Promise.reject(error)  | |
})  | 
判断内容页面的 token 的有效期或者是否是假的 token
在内容页面中修改 localStorage 里面存储的 token 然后重新刷新页面请求一次后端,后端发现 token 不对就会响应一个错误信息,查看情况。
修改内容页面的 token

刷新页面请求服务器。

request.js
if 判断中使用到的?是判断当前函数是否为 undefined 或者 Null 这种不满足条件的情况,如果不满足则不执行后面的函数,满足则继续链式调用防止异常。
// 添加响应拦截器 | |
axios.interceptors.response.use(function(config) {  | |
    // 2xx 范围内的状态码都会触发该函数 | |
    // 对响应数据做点什么 | |
    return config | |
},function(error) {  | |
    // 超出 2xx 范围的状态码都会触发该函数 | |
    // 对响应错误做点什么 例如:统一对 401 身份验证失败情况做出处理 | |
console.dir(error)  | |
    // 使用?链式调用如果前端有一个不满足条件则不调用后面的函数防止异常 | |
if(error?.response?.status === 401) {  | |
alert('身份验证失败,请重新登录')  | |
        //token 不正确或者过期则删除当前对应的 token | |
localStorage.removeItem('token')  | |
        // 跳转至登录页面重新登录 | |
location.href = '../login/index.html'  | |
    } | |
return Promise.reject(error)  | |
})  | 
效果:

总结:
- 什么是 axios 响应拦截器?
 
- 响应回到 then /catch 之前,触发的 <font title='red'> 拦截函数 </font>,对 < font title='red'> 响应结果统一处理 </font>.
 - axios 响应拦截器,什么时候触发成功 / 失败的回调函数?
 
- 状态为 <font title='red'>2xx</font > 触发 < font title='red'> 成功 </font > 回调,<font title='red'> 其它 </font > 则触发 < font title='red'> 失败 </font > 的回调函数
 
# 优化 axios 响应结果
目标:axios 直接接收服务器返回的响应结果
axios({  | |
   // 个人信息 | |
url: '/v1_0/user/profile'  | |
}).then(result => {  | |
   //result: 服务器响应数据对象 | |
}).catch(err => {  | |
})  | 
axios({  | |
   // 文章列表 | |
url: '/v1_0/mp/articles'  | |
}).then(result => {  | |
   //result: 服务器响应数据对象 | |
}).catch(err => {  | |
})  | 
axios 内部封装的接口对象,它会将服务器返回的数据对象 挂载到 data 的属性下,所以我们想要访问它的昵称,那我们就需要在 result 中 这样调用 result.data.data.name 如果每次都需要这样的话就会显得比较麻烦,那么可以对其进行一下优化。

我们可以基于 axios 的响应拦截器来完成
在 request.js 中对响应拦截器进行编写
// 添加响应拦截器 | |
axios.interceptors.response.use(function(config) {  | |
    // 2xx 范围内的状态码都会触发该函数 | |
    // 对响应数据做点什么 例如:直接返回服务器响应结果对象 | |
const result = config.data  | |
    return result | |
},function(error) {  | |
    // 超出 2xx 范围的状态码都会触发该函数 | |
    // 对响应错误做点什么 例如:统一对 401 身份验证失败情况做出处理 | |
    // 使用?链式调用如果前端有一个不满足条件则不调用后面的函数防止异常 | |
if(error?.response?.status === 401) {  | |
alert('身份验证失败,请重新登录')  | |
        //token 不正确或者过期则删除当前对应的 token | |
localStorage.removeItem('token')  | |
        // 跳转至登录页面重新登录 | |
location.href = '../login/index.html'  | |
    } | |
return Promise.reject(error)  | |
})  | 
config 是响应回来的 axios 数据对象,相当于 result 那么 config.data 就获取到了 服务器的 data 数据对象了。将 config.data 获取的数据对象 统一返回为响应结果。这样其它 axios 请求响应接收数据的时候直接调用 result.data.name 就可以取出服务器响应封装的数据对象 data 里面的用户昵称了。
# 发布文章 — 富文本编辑器
富文本:带样式,多格式的文本,在前端一般使用标签配合内联式实现

目标:发布文章页,富文本编辑器的集成
使用:wangEditor 插件 点击查看文档
步骤:参考文档
引入 CSS 定义样式
<link href="https://unpkg.com/@wangeditor/editor@latest/dist/css/style.css" rel="stylesheet"><style>
#editor—wrapper {border: 1px solid #ccc;
z-index: 100; /* 按需定义 */
}#toolbar-container { border-bottom: 1px solid #ccc; }
#editor-container { height: 500px; }
</style>
定义 HTML 结构
<div id="editor—wrapper"><div id="toolbar-container"><!-- 工具栏 --></div>
<div id="editor-container"><!-- 编辑器 --></div>
</div>引入 JS 创建编辑器
<script src="https://unpkg.com/@wangeditor/editor@latest/dist/index.js"></script>
<script>
const { createEditor, createToolbar } = window.wangEditor
const editorConfig = {
placeholder: 'Type here...',
onChange(editor) {
const html = editor.getHtml()
console.log('editor content', html)
// 也可以同步到 <textarea>}}const editor = createEditor({
selector: '#editor-container',
html: '<p><br></p>',
config: editorConfig,
mode: 'default', // or 'simple'
})
const toolbarConfig = {}
const toolbar = createToolbar({
editor,selector: '#toolbar-container',
config: toolbarConfig,
mode: 'default', // or 'simple'
})
</script>
监听内容改变,保存在隐藏文本域 (便于后期收集)
结果:

使用 npm 安装到本地的使用方式:
