技术

  • 基于 Bootstrap 搭建网站标签和样式
  • 集成 <font title='red'>wangEditor 插件 </font > 实现富文本编辑器
  • 使用原生 JS 完成 <font title='red'> 增删改查 </font > 等业务
  • 基于 axios 与黑马头条线上接口交互
  • 使用 <font title='red'>axios 拦截器 </font > 进行权限判断

项目准备:准备配套的素材代码

包含:html,css,js,静态图片,第三方插件等等

image-20230815101452131

目录管理:建议这样管理,方便查找

  • assets:资源文件夹 (图片,字体等)
  • lib:资料文件夹 (第三方插件,例如:form-serialize)
  • <font title='red'>page</font>:页面文件夹
  • <font title='red'>utils</font>:使用程序文件夹 (工具插件)

# 验证码登录

目标:完成验证码登录,后端设置验证码默认为 246810

步骤

  1. 在 utils /request.js 配置 axios 请求 <font title='red'> 基地址 </font>.

    • 作用:提取公共前缀地址,配置后 axios 请求时都会 baseURL + url
    axios.defaults.baseURL = 'http://geek.itheima.net'
    • 解决的场景:防止后端频繁更换基地址,解决需要重新部署很多个页面的情况!
  2. 收集手机号和验证码数据

  3. 基于 axios 调用验证码登录接口

  4. 使用 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)
}

效果

image-20230815105239053

# token 的介绍

概念:访问权限的 <font title='red'> 令牌 </font>,本质上是一串 <font title='red'> 字符串 </font>.

创建:正确登录后,由后端签发并返回

image-20230815105410152

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

image-20230815105559967

注意:<font title='red'> 前端 </font > 只能判断 token<font title='red'> 有无 </font>,而 < font title='red'> 后端 </font > 才能判断 token 的 < font title='red'> 有效性 </font>。

# token 的使用

目标:只有登录状态,才可以访问内容页面

步骤

  1. 在 utils /auth.js 中判断无 token 令牌字符串,则强制跳转到登录页 (手动修改地址栏测试)

    • 核心代码
    const token = locaStorage.getItem('token')
    // 没有 token 令牌字符串,则强制跳转登录页
    if(!token) {
       location.href = '../login/index.html'
    }
  2. 在登录成功后,保存 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'
}

效果

test

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)// 手机号或验证码格式不正确
    })
})

效果

test

总结:

  1. token 的作用?
    • <font title='red'> 判断 </font > 用户是否有 < font title='red'> 登录状态 </font > 等
  2. 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)
})

token字符串

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)
})

结果

image-20230815132709455

<span alt='solid'> 总结 </span>:

  1. 什么是 axios 请求拦截器?
    • 发起请求之前,调用的一个 <font title='red'> 函数 </font>,对 < font title='red'> 请求参数 </font > 进行 < font title='red'> 设置 </font>.
  2. axios 请求拦截器,什么时候使用?
    • 有 <font title='red'> 公共配置 </font > 和设置时,统一设置在请求拦截器中

# axios 响应拦截器和身份验证失败

axios 响应拦截器:相应回到 then /catch 之前,触发的 <font title='red'> 拦截函数 </font>,对 < font title='red'> 响应结果统一处理 </font>.

例如:身份验证失败,统一判断并做处理。

image-20230815133219452

  • 基本结构
// 添加响应拦截器
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

image-20230815134227005

刷新页面请求服务器。

image-20230815134314948

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)
})

效果

test

总结:

  1. 什么是 axios 响应拦截器?
    • 响应回到 then /catch 之前,触发的 <font title='red'> 拦截函数 </font>,对 < font title='red'> 响应结果统一处理 </font>.
  2. 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 如果每次都需要这样的话就会显得比较麻烦,那么可以对其进行一下优化。

image-20230815140057937

我们可以基于 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 里面的用户昵称了。

# 发布文章 — 富文本编辑器

富文本:带样式,多格式的文本,在前端一般使用标签配合内联式实现

image-20230815144009765

目标:发布文章页,富文本编辑器的集成

使用:wangEditor 插件 点击查看文档

步骤:参考文档

  1. 引入 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>
  2. 定义 HTML 结构

    <div id="editor—wrapper">
        <div id="toolbar-container"><!-- 工具栏 --></div>
        <div id="editor-container"><!-- 编辑器 --></div>
    </div>
  3. 引入 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>
  4. 监听内容改变,保存在隐藏文本域 (便于后期收集)

结果:

image-20230815150023155

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