首先把登录注册的静态页面搞过来,然后实现验证码部分,输入手机号,发送请求获取一个验证码,然后直接展示在文本框里。
首先,写接口:
// 获取验证码
export const reqGetCode = (phone) => {
return requests({
url: `/user/passport/sendCode/${phone}`,
method: "get",
});
};
接着,是我们熟悉的vuex:
import { reqGetCode } from "@/api";
// 登录与注册模块
const state = {
code: "",
};
const mutations = {
GETCODE(state, code) {
state.code = code;
},
};
const actions = {
// 获取验证码
async getCode({ commit }, phone) {
let result = await reqGetCode(phone);
// console.log("获取验证码" + result.data);
if (result.code == 200) {
commit("GETCODE", result.data);
return "ok";
} else {
return Promise.reject(new Error(fail));
}
},
};
const getters = {};
export default {
state,
mutations,
actions,
getters,
};
最后,把请求回来的验证码展示在验证码框中:
这部分也比较简单,主要是拿着手机号、验证码、密码去发送请求,添加到数据库。
熟悉的写接口环节:
// 注册
export const reqUserRegister = (phone, password, code) => {
return requests({
url: "/user/passport/register",
data: {
phone,
password,
code,
},
method: "post",
});
};
再到我们滚瓜烂熟的vuex环节
const actions = {
// 用户注册
async userRegister({ commit }, { phone, password, code }) {
try {
let result = await reqUserRegister(phone, password, code);
// console.log(result);
if (result.code == 200) {
return "注册成功";
}
if (result.code == 223) {
return "该用户已注册!";
}
} catch (err) {
alert("请求失败", err.message);
}
},
};
cons
最后,点击注册按钮派发请求:
export default {
name: "Register",
data() {
return {
// 收集表单数据
// 手机号
phone: "",
// 验证码
code: "",
// 密码
password: "",
// 确认密码
password1: "",
// 是否同意协议
agree: true,
};
},
methods: {
.......
async userRegister() {
try {
//来个逻辑短路
// 若phone、code、password都存在且password=password1,agree=true注册才会成功
const { phone, password, password1, agree, code } = this;
if (
phone &&
code &&
password &&
password1 &&
password == password1 &&
agree == true
) {
let result = await this.$store.dispatch("userRegister", {
phone,
code,
password,
});
alert(result);
if (result == "注册成功") {
this.$router.push("/login");
}
}
} catch (err) {
alert("注册失败!", err);
}
},
},
};
先click.prevent
阻止默认提交表单的操作,避免跳来跳去。
写登录接口:
// 登录
export const reqLogin = (data) => {
return requests({
url: "/user/passport/login",
data,
method: "post",
});
};
写vuex:
async userLogin({ commit }, data) {
let result = await reqLogin(data);
console.log("登录信息" + result);
},
派发请求,路由跳转:
// 用户登录
async userLogin() {
try {
// 登录成功
const { phone, password } = this;
phone &&
password &&
(await this.$store.dispatch("userLogin", { phone, password }));
// 跳转到路由首页
this.$router.push("/home");
} catch (error) {
alert("登录失败" + error.message);
}
},
请求数据后,服务端会返回一个字段,名字叫token
,用于唯一地标识用户。
当我们点击登录,返回的数据是这样的,里面有一个字段叫token
,它就是用户的唯一标识,将来我们请求用户相关的东西就要拿着这个token
。
由于vuex是非持久性存储数据,一刷新就没了,所以我们要把token
放在本地存储中
// 登录与注册模块
const state = {
......
token: localStorage.getItem("TOKEN"),
......
};
const mutations = {
......
USERLOGIN(state, token) {
state.token = token;
},
};
const actions = {
.......
// 登录
async userLogin({ commit }, data) {
let result = await reqLogin(data);
// console.log("登录信息" + result);
// 服务器下发的token是用户的唯一标识
// 经常通过token找服务器要用户信息进行展示
if (result.code == 200) {
commit("USERLOGIN", result.data.token);
// 持久化存储token
localStorage.setItem("TOKEN", result.data.token);
return "ok";
} else {
return Promise.reject(new Error("fail"));
}
},
后面的每个请求都要和token有关,所以我们要把token写到请求头中
未登录时左上角:
登录成功后:
这里有个问题,就是这个请求的派发应该写在哪里.
如果写在home页,在home页派发请求(实际是不对的,去别的页面刷新就没用户名了)
1、如果只在home页写,那么跳转到其他页面点击刷新vuex中的用户数据就没了;但是如果每个用到用户名的组件都要派发,那就太麻烦了。
2、如果在app写,那么派发请求是在登录前,还没有token呢就请求不到数据,只有登录之后刷新页面(已经有token)才能请求到(刷新页面app肯定第一个挂载)
解决方案就是用路由守卫做判断,没有用户数据就派发请求。(在下文会解决这个问题,现在先在home派发)
这里我们先把派发home里。先把请求用户数据的接口写好:
// 获取用户信息(需要带着用户的token向服务器要用户信息,写在请求头里)
export const reqGetUserMsg = () => {
return requests({
url: "/user/passport/auth/getUserInfo",
method: "get",
});
};
vuex保存数据到仓库:
// 获取用户信息
async getUserMsg({ commit }) {
let result = await reqGetUserMsg();
console.log("用户信息" + result.data);
if (result.code == 200) {
// 用户已经登录成功且获取到token
commit("GETUSERMSG", result.data);
}
},
展示
computed: {
userName() {
return this.$store.state.user.userMsg.name;
},
},
Header组件中,读取vuex的用户数据,拿到用户名
退出登录接口:
// 退出登录
export const reqLogout = () => {
return requests({
url: "/user/passport/logout",
method: "get",
});
};
vuex清除相关用户信息:
const mutations = {
CLEAR(state) {
// 把仓库中相关用户信息和本地存储清空
state.token = "";
state.userInfo = {};
localStorage.removeItem("TOKEN");
},
};
const actions = {
// 退出登录
async UserLogout({ commit }) {
let result = await reqLogout();
if (result.code == 200) {
commit("CLEAR");
return "ok";
} else {
return Promise.reject(new Error("fail"));
}
},
};
退出后回到首页:
// 退出登录
async logout() {
try {
// 需要发请求通知服务器退出登录(清除一些数据)
// 清除项目当中的数据(userInfo、token)
// 退出成功
await this.$store.dispatch("UserLogout");
// 回到首页
this.$router.push("/home");
} catch (error) {
alert("退出登录失败" + error.message);
}
},
上文还残留着一些问题没有解决。
上面这两个问题都可以使用路由守卫来解决。点击复习路由守卫
src/router/index.js
// 全局守卫——前置守卫
router.beforeEach(async (to, from, next) => {
// 用户登录了才会有token
let token = store.state.user.token;
// 用户是否登录
if (token) {
// 登录了就不能再回到登录页,停留在首页
if ((to.path = "/login")) {
next("/home");
}
// 登录了去其他的页面
else {
// 如果有用户数据,放行
if (store.state.user.userMsg.name) {
next();
}
// 如果没有用户数据,先请求成功再放行
else {
try {
// 这个请求在挂载之前,所以刷新也会先请求
await store.dispatch("getUserMsg");
next();
} catch (err) {
console.log("请求用户数据失败!", err);
// 这里还有一种情况,token过期请求不到
// 那么就要清空token,重新登录
await store.dispatch("userLogout");
// localStorage.removeItem("TOKEN");
next("/login");
}
}
}
}
// 如果没有登录
else {
//没登录不能去的路由地址
let pages = ["/trade", "/pay", "/paysuccess", "/center/myorder"];
if (pages.includes(to.path)) {
// 如果没登陆往这几个页面跳,就回到登录页,并传想去的页面的地址
// 这样能提升用户体验,登录完成后直接跳到刚才的页面
next(`/login?redirect=${to.path}`);
} else {
next();
}
}
});
至此,登录与注册功能的笔记就整理得差不多啦,重新看之前写的代码发现还有很多改进的地方就浅改了一下,之前写的时候确实有很多细节没考虑到,还是得多思考多回头看看才行。