目录
一、路由监听及其应用
现在登录状态下,如果我们手动删除token,切换路由的时候,购物车依然存在。这是因为登录状态依旧没有改变。
1.1、路由监听的格式
TopBar.vue中书写watch监听属性,监听"$route.path"的变化,可以检测是否有发生路由跳转。
watch: {
// 监听路由的变化
"$route.path": {
handler(newVal, oldVal){
// 什么时候执行这里的代码?? 路由发生变化的时候
console.log(newVal, oldVal);
}
}
},
1.2、路由监听的应用
所以,我们可以把TopBar.vue中的created的代码封装起来,看做是设置用户登录状态的函数,封装在methods中
created(){
this.setUserLoginstatus()
},
watch: {
// 监听路由的变化
"$route.path": {
handler(newVal, oldVal){
// console.log(newVal, oldVal)
if(newVal !== oldVal){
this.setUserLoginstatus()
}
}
}
},
methods:{
setUserLoginstatus(){
// 微信登录第二步:临时票据code换取token
let mycode = this.$route.query.code;
if (mycode) {
...
}else{
...
}
},
}
二、组件重载
之前做的利用路由监听完成切换路由就更新用户登录状态值。
实质:监听路径的变化,重新渲染Topbar上的用户数据,在路径发生变化时执行的操作。
这个功能,也可以使用组件重载的方式来完成。即每次切换路由,我们都让TopBar.vue重新加载一次。
首先,现在TopBar的created函数中补充更新用户状态值的代码(此时注释掉前面的watch):
if(mycode){
...
}else{
// !!!如果不是微信登录进来的,就会执行这里的代码
let mytoken = localStorage.getItem("x-auth-token");
this.chanIsLogined(Boolean(mytoken));
}
如何进行组件重载?利用key属性。
先删除TopBar.vue中的watch。
然后来到App.vue组件,给调用TopBar.vue的地方添加key属性:
<template>
...
<!-- 顶部 -->
<TopBar :key="topBarKeyValue"></TopBar>
...
</template>
<script>
data() {
return {
topBarKeyValue:1
};
},
...
watch: {
// 监听路由的变化
"$route.path": {
handler(newVal, oldVal){
// console.log(newVal, oldVal)
if(newVal !== oldVal){
// key属性的值一变化,就会做组件重载,从而执行created函数
console.log("组件重载!!!!");
this.topBarKeyValue++;
}
}
}
},
</script>
这样将Application下的token删除之后点击其他路由,将会恢复登录前的状态,而不会一直保持登录后的状态。
三、获取登录用户信息,请求头携带token
3.1、请求头携带token
在登录功能中,目前我们已经完成了保存token到本地存储中。
在真正项目中,只要我们本地存储中有token,在请求的时候都会带上这个token值在每一个请求头中,不管这个请求需不需token,都会带上。
接下来,我们就需要在请求拦截器中判断token,携带token:
src/request/request.js中:
instance.interceptors.request.use(config => {
const token = localStorage.getItem("x-auth-token");
if (token) {
// 判断是否存在token,如果存在的话,则每个请求的请求头上都加上token
config.headers["x-auth-token"] = token;
}
return config
}, err => {
return Promise.reject(err)
})
3.2、获取用户登录信息
在api.js中:
// 获取登录用户信息
export const UserProfilesAPI = () => request.get("/shop/userProfiles");
在TopBar组件中:
import {UserProfilesAPI} from "@/request/api"
setUserLoginstatus(){
// 微信登录第二步:临时票据code换取token
let mycode = this.$route.query.code;
if (mycode) {
...
}else{
// 没有code,说明没有扫码, 或者 用户已经登录了,不用扫码了
// 判断有没有token,设置登录状态 (因为TopBar上面的信息,是需要靠 有没有登录来去展示的)
let mytoken = localStorage.getItem("x-auth-token");
this.chanIsLogined(Boolean(mytoken));
if(mytoken){
// 请求并渲染用户信息
UserProfilesAPI().then(res=>{
// 打印用户信息
console.log(res);
});
}else{
// 设置回默认的用户信息
}
}
},
携带请求头token成功的结果展示:
四、用户信息渲染
但是在项目中,任意组件都可以获取这个用户信息,比如:TopBar.vue,Login.vue等,所以用户信息可以放在vuex中:
store中新建userInfo文件夹,新建index.js
import {UserProfilesAPI} from "@/request/api"
export default {
namespaced: true,
state: {
// 购物车数量
cartTotal: 0,
// 用户信息
userInfo:{
headImg:require("../../assets/img/userImg.f8bbec5e.png"),
nickName:"--",
coin:"--"
}
},
mutations: {
updateUserInfo(state,payload){
console.log("payload为:",payload);
state.cartTotal = payload.cartTotal;
state.userInfo = payload.userInfo;
}
},
actions: {
asyncUpdateUserInfo(context){
UserProfilesAPI().then(res=>{
// 打印用户信息
context.commit("updateUserInfo",res.data)
});
}
},
}
在TopBar.vue组件中:
<template>
...
<li>
<img
@click="clickAvatar"
class="avatar"
width="26"
:src="userInfo.headImg"
alt=""
/>用户名:{{userInfo.nickName}}
</li>
<li>我的积分:{{userInfo.coin}}</li>
<li>获取积分</li>
<li>叩丁狼官网</li>
<li class="cart-btn btn" v-show="isLogined">
<img src="../assets/img/cart.png" alt="" />
<span>购物车</span>
<b>{{ cartTotal }}</b>
</li>
...
</template>
<script>
setUserLoginstatus(){
setUserLoginstatus(){
// 微信登录第二步:临时票据code换取token
let mycode = this.$route.query.code;
if (mycode) {
// 有code才去换取token
WeixinLoginApi({
code: mycode,
}).then((res) => {
console.log(res);
if (res.code === 0) {
// 登录成功
// 1、提示用户登录成功
// 2、保存token值
// 3、改变登录状态
// 4、清除浏览器地址栏上的code
// 5、获取登录用户信息
this.asyncUpdateUserInfo();
}else{
this.asyncChanToastState({
msg:res.message,
type:"danger"
});
}
})
}else{
// 如果没有code,需要更新用户登录状态
// 没有code说明,用户没扫码
// 说明用户已经登录的不用扫码,或者用户没有登录没扫码,
// 判断用户能否拿到token来做出登录状态的更新
let mytoken = localStorage.getItem("x-auth-token");
this.chanIsLogined(Boolean(mytoken));
// 获取登录用户信息
if(mytoken){
this.asyncUpdateUserInfo();
}
}
},
}
</script>
4.1、获取vuex中的图片img
vuex中的图片获取格式 :src="xx",不加{{}}
可能出现图片获取不到的情况===》require("@/assets/img/userImg.f8bbec5e.png")即可!
在Login.vue组件中登录成功也是异步更新用户信息:
import { mapMutations,mapActions } from "vuex";
...
...mapActions({
asyncUpdateUserInfo:"userInfo/asyncUpdateUserInfo"
}),
toLogin() {
// 1&&2、前两个验证已经完成
// 3 判断验证码是否为空
// 去做登录
LoginAPI({
...
}).then((res) => {
console.log(res);
if(res.code==0){
// 1、提示登录成功
// 2、保存token值,本地存储
// 3、隐藏登录框
// 【!!】4登录状态的切换
// 5、更新用户信息
this.asyncUpdateUserInfo();
}
});
},
注:如果此时用户头像没有出来,在public文件夹中的index.html文件的head标签里,添加这个标签即可
<!-- 防止服务器检查防盗用链接 -->
<meta name="referrer" content="no-referrer" />
五、删除token后的用户信息初始化
在删除token后,切换路由的时候,用户信息需要还原为默认值
TopBar.vue中以及对应的vuex中:
setUserLoginState() {
if(mycode){
...
}else {
...
if (mytoken) {
...
} else {
// 初始化用户数据
this.initUserInfo();
}
}
mutations: {
......
// 初始化操作
initUserInfo(state) {
// 购物车数量
state.cartTotal = 0,
// 用户信息
state.userInfo = {
nickName: "游客~~",
coin: 0,
headImg: require("@/assets/img/userImg.f8bbec5e.png")
}
}
},
六、首页布局的套用
在实际工作项目中,有可能项目部分代码并不是你完成的,而是你同事完成的。甚至有可能你进入到公司之后,接手你同事的项目,这时候就要求你看得懂别人写的代码。
接下来我们套用首页的结构和样式,将views中的Home.vue和components中的home文件夹替换掉项目原本的文件,然后在其基础上进行数据渲染。
6.1、正向传值+数据最大长度控制的第一种方法
api.js中:
// 首页精品推荐请求
export const JingpinAPI = () => request.get("/products/recommend");
// 请求热门兑换的数据
export const RemenAPI = () => request.get("/products/hot");
src/views/Home.vue中:
备注:jingpinArr和remenArr是data中要去渲染页面的数组,接收请求的数据。
async created() {
// 精品请求
let res1 = await JingpinAPI();
// 过滤出前4项
if (res1.code == 0) {
this.jingpinArr = res1.data.data.records.filter(
(item, index) => index < 4
);
}
// 热门请求
let res2 = await RemenAPI();
if (res2.code == 0) {
this.remenArr = res2.data.data.records.filter((item, index) => index < 6);
};
console.log(this.jingpinArr,this.remenArr);
},
src/components/home/List.vue中:
备注:v-for循环中图片的渲染格式 :src="item.xxx",不加{{}}
<li v-for="item in arr" :key="item.id">
<section>
<img :src="imgBaseUrl + item.coverImg" alt="" />
<div class="bottom-box">
<h3>{{ item.name }}</h3>
<p>{{ item.coin }}积分</p>
<div class="btn">立即兑换</div>
</div>
<img
class="flag"
v-show="item.isLatest == 1"
src="../../assets/img/section_new.png"
alt=""
/>
<img
class="flag"
v-show="item.isHotSale == 1"
src="../../assets/img/section_hot.png"
alt=""
/>
</section>
</li>
main.js中保存这个基本图片的url:
// 在组件内部获取这个变量
// 像一个 “全局变量”, 在任意的组件中都可以通过 this.变量名 或者 {{变量名}} 来获取
// 但是不是像Vuex那样共享数据,而是仅仅只是在组件内部的一个变量
Vue.prototype.imgBaseUrl = "http://sc.wolfcode.cn";
6.2、正向传值+数据最大长度控制的第二种方法
备注:左边为子组件,右边为父组件,通过props:["xxx"]接收
备注: 不想使用过滤的话,可以借助maxLength属性和v-show指令对数组最大展示长度进行控制