新创建的网站,浙江二建建设集团有限公司网站,中海园林建设有限公司网站,比亚迪新能源汽车价格表社交媒体-信息头条项目完整开发笔记完整教程#xff08;附代码资料#xff09;主要内容讲述#xff1a;一、项目初始化使用 Vue CLI 创建项目,加入 Git 版本管理,调整初始目录结构,导入图标素材,引入 Vant 组件库,移动端 REM 适配,关于 , 配置文件,封装请求模块。十、用户关… 
社交媒体-信息头条项目完整开发笔记完整教程附代码资料主要内容讲述一、项目初始化使用 Vue CLI 创建项目,加入 Git 版本管理,调整初始目录结构,导入图标素材,引入 Vant 组件库,移动端 REM 适配,关于 , 配置文件,封装请求模块。十、用户关注  粉丝。十一、我的收藏/历史。十二、编辑用户资料创建组件并配置路由,页面布局,展示用户信息,修改昵称,修改性别,修改生日,修改头像。二、登录注册准备,实现基本登录功能,登录状态提示,表单验证,验证码处理,处理用户 Token,优化封装本地存储操作模块,关于 Token 过期问题。三、个人中心TabBar 处理,页面布局,处理已登录和未登录的页面展示,用户退出,展示登录用户信息,优化设置 Token。四、首页—文章列表页面布局,频道列表,文章列表,文章列表项。五、首页—频道编辑处理页面弹出层,创建频道编辑组件,页面布局,展示我的频道,展示推荐频道列表,添加频道,编辑频道,频道数据持久化,正确的获取首页频道列表数据。六、文章搜索创建组件并配置路由,页面布局,处理页面显示状态,搜索联想建议,搜索结果,搜索历史记录。七、文章详情创建组件并配置路由,页面布局,关于后端返回数据中的大数字问题,展示文章详情,处理内容加载状态,关于文章正文的样式,图片点击预览,关注用户,文章收藏,文章点赞。八、文章评论展示文章评论列表,评论点赞,发布文章评论,评论回复。九、用户页面创建组件并配置路由,页面布局,展示用户信息,用户关注,展示用户文章列表。 
全套笔记资料代码移步 前往gitee仓库查看 
感兴趣的小伙伴可以自取哦欢迎大家点赞转发~ 全套教程部分目录 部分文件图片 二、登录注册 目标 能实现登录页面的布局能实现基本登录功能能掌握 Vant 中 Toast 提示组件的使用能理解 API 请求模块的封装能理解发送验证码的实现思路能理解 Vant Form 实现表单验证的使用 准备 
创建组件并配置路由 
1、创建 src/views/login/index.vue 并写入以下内容 
templatediv classlogin-container登录页面/div
/templatescript
export default {name: LoginPage,components: {},props: {},data () {return {}},computed: {},watch: {},created () {},mounted () {},methods: {}
}
/scriptstyle scoped langless/style 
2、然后在 src/router/index.js 中配置登录页的路由表 
{path: /login,name: login,component: ()  import(/views/login)
}最后访问 /login 查看是否能访问到登录页面。 布局结构 
这里主要使用到三个 Vant 组件 
[NavBar 导航栏]([Form 表单]( [Field 输入框]([Button 按钮](  一个经验使用组件库中的现有组件快速布局再慢慢调整细节效率更高刚开始可能会感觉有点麻烦越用越熟慢慢的就有了自己的思想。 布局样式 写样式的原则将公共样式写到全局src/styles/index.less将局部样式写到组件内部。 1、src/styles/index.less 
body {background-color: #f5f7f9;
}.page-nav-bar {background-color: #3296fa;.van-nav-bar__title {color: #fff;}
}2、src/views/login/index.vue 
templatediv classlogin-container!-- 导航栏 --van-nav-bar classpage-nav-bar title登录 /!-- /导航栏 --!-- 登录表单 --van-form submitonSubmitvan-fieldname用户名placeholder请输入手机号i slotleft-icon classtoutiao toutiao-shouji/i/van-fieldvan-fieldtypepasswordname验证码placeholder请输入验证码i slotleft-icon classtoutiao toutiao-yanzhengma/itemplate #buttonvan-button classsend-sms-btn round sizesmall typedefault发送验证码/van-button/template/van-fielddiv classlogin-btn-wrapvan-button classlogin-btn block typeinfo native-typesubmit登录/van-button/div/van-form!-- /登录表单 --/div
/templatescript
export default {name: LoginIndex,components: {},props: {},data () {return {}},computed: {},watch: {},created () {},mounted () {},methods: {onSubmit (values) {console.log(submit, values)}}
}
/scriptstyle scoped langless
.login-container {.toutiao {font-size: 37px;}.send-sms-btn {width: 152px;height: 46px;line-height: 46px;background-color: #ededed;font-size: 22px;color: #666;}.login-btn-wrap {padding: 53px 33px;.login-btn {background-color: #6db4fb;border: none;}}
}
/style实现基本登录功能 
思路 
注册点击登录的事件获取表单数据根据接口要求使用 v-model 绑定表单验证发请求提交根据请求结果做下一步处理 
一、根据接口要求绑定获取表单数据 
1、在登录页面组件的实例选项 data 中添加 user 数据字段 
...
data () {return {user: {mobile: ,code: }}
}2、在表单中使用 v-model 绑定对应数据 
!-- van-cell-group 仅仅是提供了一个上下外边框能看到包裹的区域 --
van-cell-groupvan-fieldv-modeluser.mobilerequiredclearablelabel手机号placeholder请输入手机号/van-fieldv-modeluser.codetypenumberlabel验证码placeholder请输入验证码required/
/van-cell-group最后测试。 一个小技巧使用 VueDevtools 调试工具查看是否绑定成功。 二、请求登录 
1、创建 src/api/user.js 封装请求方法 
/*** 用户相关的请求模块*/
import request from /utils/request/*** 用户登录*/
export const login  data  {return request({method: POST,url: /app/v1_0/authorizations,data})
}2、给登录按钮注册点击事件 
async onLogin () {try {const res  await login(this.user)console.log(登录成功, res)} catch (err) {if (err.response.status  400) {console.log(登录失败, err)}}
}最后测试。 
登录状态提示 
Vant 中内置了[Toast 轻提示]( 
// 简单文字提示
Toast(提示内容);// loading 转圈圈提示
Toast.loading({duration: 0, // 持续展示 toastmessage: 加载中...,forbidClick: true // 是否禁止背景点击
});// 成功提示
Ttoast.success(成功文案);// 失败提示
Toast.fail(失败文案);提示在组件中可以直接通过 this.$toast 调用。 另外需要注意的是Toast 默认采用单例模式即同一时间只会存在一个 Toast如果需要在同一时间弹出多个 Toast可以参考下面的示例 
Toast.allowMultiple();const toast1  Toast(第一个 Toast);
const toast2  Toast.success(第二个 Toast);toast1.clear();
toast2.clear();下面是为我们的登录功能增加 toast 交互提示。 
async onLogin () {// 开始转圈圈this.$toast.loading({duration: 0, // 持续时间0表示持续展示不停止forbidClick: true, // 是否禁止背景点击message: 登录中... // 提示消息})try {const res  await request({method: POST,url: /app/v1_0/authorizations,data: this.user})console.log(登录成功, res)// 提示 success 或者 fail 的时候会先把其它的 toast 先清除this.$toast.success(登录成功)} catch (err) {console.log(登录失败, err)this.$toast.fail(登录失败手机号或验证码错误)}
}假如请求非常快的话就看不到 loading 效果了这里可以手动将调试工具中的网络设置为慢速网络。 
测试结束再把网络设置恢复为 Online 正常网络。 
表单验证 参考文档[Form 表单验证]( templatediv classlogin-container!-- 导航栏 --van-nav-bar classpage-nav-bar title登录 /!-- /导航栏 --!-- 登录表单 --!--表单验证1、给 van-field 组件配置 rules 验证规则参考文档2、当表单提交的时候会自动触发表单验证如果验证通过会触发 submit 事件如果验证失败不会触发 submit--van-form submitonSubmitvan-fieldv-modeluser.mobilename手机号placeholder请输入手机号:rulesuserFormRules.mobiletypenumbermaxlength11i slotleft-icon classtoutiao toutiao-shouji/i/van-fieldvan-fieldv-modeluser.codename验证码placeholder请输入验证码:rulesuserFormRules.codetypenumbermaxlength6i slotleft-icon classtoutiao toutiao-yanzhengma/itemplate #buttonvan-button classsend-sms-btn round sizesmall typedefault发送验证码/van-button/template/van-fielddiv classlogin-btn-wrapvan-button classlogin-btn block typeinfo native-typesubmit登录/van-button/div/van-form!-- /登录表单 --/div
/templatescript
import { login } from /api/userexport default {name: LoginIndex,components: {},props: {},data () {return {user: {mobile: , // 手机号code:  // 验证码},userFormRules: {mobile: [{required: true,message: 手机号不能为空}, {pattern: /^1[3|5|7|8]\d{9}$/,message: 手机号格式错误}],code: [{required: true,message: 验证码不能为空}, {pattern: /^\d{6}$/,message: 验证码格式错误}]}}},computed: {},watch: {},created () {},mounted () {},methods: {async onSubmit () {// 1. 获取表单数据const user  this.user// TODO: 2. 表单验证// 3. 提交表单请求登录this.$toast.loading({message: 登录中...,forbidClick: true, // 禁用背景点击duration: 0 // 持续时间默认 20000 表示持续展示不关闭})try {const res  await login(user)console.log(登录成功, res)this.$toast.success(登录成功)} catch (err) {if (err.response.status  400) {this.$toast.fail(手机号或验证码错误)} else {this.$toast.fail(登录失败请稍后重试)}}// 4. 根据请求响应结果处理后续操作}}
}
/scriptstyle scoped langless
.login-container {.toutiao {font-size: 37px;}.send-sms-btn {width: 152px;height: 46px;line-height: 46px;background-color: #ededed;font-size: 22px;color: #666;}.login-btn-wrap {padding: 53px 33px;.login-btn {background-color: #6db4fb;border: none;}}
}
/style 
验证码处理 
验证手机号 
async onSendSms () {console.log(onSendSms)// 1. 校验手机号try {await this.$refs.loginForm.validate(mobile)} catch (err) {return console.log(验证失败, err)}// 2. 验证通过显示倒计时// 3. 请求发送验证码
}使用倒计时组件 
1、在 data 中添加数据用来控制倒计时的显示和隐藏 
data () {return {...isCountDownShow: false}
}2、使用倒计时组件 
van-fieldv-modeluser.codeplaceholder请输入验证码
i classicon icon-mima slotleft-icon/ivan-count-downv-ifisCountDownShowslotbutton:time1000 * 5formatss sfinishisCountDownShow  false/van-buttonv-elseslotbuttonsizesmalltypeprimaryroundclickonSendSmsCode发送验证码/van-button
/van-field发送验证码 
1、在 api/user.js 中添加封装数据接口 
export const getSmsCode  mobile  {return request({method: GET,url: /app/v1_0/sms/codes/${mobile}})
}2、给发送验证码按钮注册点击事件 
3、发送处理 
async onSendSms () {// 1. 校验手机号try {await this.$refs.loginForm.validate(mobile)} catch (err) {return console.log(验证失败, err)}// 2. 验证通过显示倒计时this.isCountDownShow  true// 3. 请求发送验证码try {await sendSms(this.user.mobile)this.$toast(发送成功)} catch (err) {// 发送失败关闭倒计时this.isCountDownShow  falseif (err.response.status  429) {this.$toast(发送太频繁了请稍后重试)} else {this.$toast(发送失败请稍后重试)}}
}处理用户 Token Token 是用户登录成功之后服务端返回的一个身份令牌在项目中的多个业务中需要使用到 
访问需要授权的 API 接口校验页面的访问权限… 
但是我们只有在第一次用户登录成功之后才能拿到 Token。 
所以为了能在其它模块中获取到 Token 数据我们需要把它存储到一个公共的位置方便随时取用。 
往哪儿存 
本地存储 获取麻烦数据不是响应式 Vuex 容器推荐 获取方便响应式的  
使用容器存储 Token 的思路 登录成功将 Token 存储到 Vuex 容器中 获取方便响应式 为了持久化还需要把 Token 放到本地存储 持久化  
下面是具体实现。 
1、在 src/store/index.js 中 
import Vue from vue
import Vuex from vuexVue.use(Vuex)export default new Vuex.Store({state: {// 用户的登录状态信息user: JSON.parse(window.localStorage.getItem(TOUTIAO_USER))// user: null},mutations: {setUser (state, user) {state.user  userwindow.localStorage.setItem(TOUTIAO_USER, JSON.stringify(user))}},actions: {},modules: {}
}) 
2、登录成功以后将后端返回的 token 相关数据存储到容器中 
async onLogin () {// const loginToast  this.$toast.loading({this.$toast.loading({duration: 0, // 持续时间0表示持续展示不停止forbidClick: true, // 是否禁止背景点击message: 登录中... // 提示消息})try {const res  await login(this.user)// res.data.data  { token: xxx, refresh_token: xxx }this.$store.commit(setUser, res.data.data)// 提示 success 或者 fail 的时候会先把其它的 toast 先清除this.$toast.success(登录成功)} catch (err) {console.log(登录失败, err)this.$toast.fail(登录失败手机号或验证码错误)}// 停止 loading它会把当前页面中所有的 toast 都给清除// loginToast.clear()
}优化封装本地存储操作模块 
创建 src/utils/storage.js 模块。 
export const getItem  name  {const data  window.localStorage.getItem(name)try {return JSON.parse(data)} catch (err) {return data}
}export const setItem  (name, value)  {if (typeof value  object) {value  JSON.stringify(value)}window.localStorage.setItem(name, value)
}export const removeItem  name  {window.localStorage.removeItem(name)
} 
关于 Token 过期问题 
登录成功之后后端会返回两个 Token 
token访问令牌有效期2小时refresh_token刷新令牌有效期14天用于访问令牌过期之后重新获取新的访问令牌 
我们的项目接口中设定的 Token 有效期是 2 小时超过有效期服务端会返回 401 表示 Token 无效或过期了。 
为什么过期时间这么短 
为了安全例如 Token 被别人盗用 
过期了怎么办 
让用户重新登录用户体验太差了使用 refresh_token 解决 token 过期 
如何使用 refresh_token 解决 token 过期 到课程的后面我们开发的业务功能丰富起来之后再给大家讲解 Token 过期处理。 大家需要注意的是在学习测试的时候如果收到 401 响应码请重新登录再测试。 概述服务器生成token的过程中会有两个时间一个是token失效时间一个是token刷新时间刷新时间肯定比失效时间长当用户的 token 过期时你可以拿着过期的token去换取新的token来保持用户的登陆状态当然你这个过期token的过期时间必须在刷新时间之内如果超出了刷新时间那么返回的依旧是 401。 
处理流程 
在axios的拦截器中加入token刷新逻辑当用户token过期时去向服务器请求新的 token把旧的token替换为新的token然后继续用户当前的请求 
在请求的响应拦截器中统一处理 token 过期 
/*** 封装 axios 请求模块*/
import axios from axios;
import jsonBig from json-bigint;
import store from /store;
import router from /router;// axios.create 方法复制一个 axios
const request  axios.create({baseURL:  // 基础路径
});/*** 配置处理后端返回数据中超出 js 安全整数范围问题*/
request.defaults.transformResponse  [function(data) {try {return jsonBig.parse(data);} catch (err) {return {};}}
];// 请求拦截器
request.interceptors.request.use(function(config) {const user  store.state.user;if (user) {config.headers.Authorization  Bearer ${user.token};}// Do something before request is sentreturn config;},function(error) {// Do something with request errorreturn Promise.reject(error);}
);// 响应拦截器
request.interceptors.response.use(// 响应成功进入第1个函数// 该函数的参数是响应对象function(response) {// Any status code that lie within the range of 2xx cause this function to trigger// Do something with response datareturn response;},// 响应失败进入第2个函数该函数的参数是错误对象async function(error) {// Any status codes that falls outside the range of 2xx cause this function to trigger// Do something with response error// 如果响应码是 401 则请求获取新的 token// 响应拦截器中的 error 就是那个响应的错误对象console.dir(error);if (error.response  error.response.status  401) {// 校验是否有 refresh_tokenconst user  store.state.user;if (!user || !user.refresh_token) {router.push(/login);// 代码不要往后执行了return;}// 如果有refresh_token则请求获取新的 tokentry {const res  await axios({method: PUT,url: headers: {Authorization: Bearer ${user.refresh_token}}});// 如果获取成功则把新的 token 更新到容器中console.log(刷新 token  成功, res);store.commit(setUser, {token: res.data.data.token, // 最新获取的可用 tokenrefresh_token: user.refresh_token // 还是原来的 refresh_token});// 把之前失败的用户请求继续发出去// config 是一个对象其中包含本次失败请求相关的那些配置信息例如 url、method 都有// return 把 request 的请求结果继续返回给发请求的具体位置return request(error.config);} catch (err) {// 如果获取失败直接跳转 登录页console.log(请求刷线 token 失败, err);router.push(/login);}}return Promise.reject(error);}
);export default request;未完待续 同学们请等待下一期 
全套笔记资料代码移步 前往gitee仓库查看 
感兴趣的小伙伴可以自取哦欢迎大家点赞转发~