技术:
- 基于 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 警告框反馈结果给用户
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 安装到本地的使用方式: