先搞定静态组件
,拷贝Login
和Register
注册模块
- 表单的验证最后再做
- 先完成发送
验证码
的逻辑
### Register.index.vue
<div class="content">
<label>手机号:</label>
<input type="text" placeholder="请输入你的手机号" v-model="phone"><!--新增phone-->
<span class="error-msg">错误提示信息</span>
</div>
<div class="content">
<label>验证码:</label>
<input type="text" placeholder="请输入验证码" v-model="code"><!--新增code-->
<!--修改页面结构-->
<!-- <img ref="code" src="http://182.92.128.115/api/user/passport/code" alt="code"> -->
<button style="width: 100px;height: 38px;" @click="getCode">获取验证码</button> <!--新增事件-->
<span class="error-msg">错误提示信息</span>
</div>
......
<script>
export default {
name: 'Register',
data(){
return {
phone:'', // 初始化数据
code:''
}
},
methods:{
async getCode(){
try {
const {phone} = this; // 从vc对象解构获取phone值
phone && (await this.$store.dispatch('getCode',phone)) // 这种写法包含判断么?测试结果是'不包含'
this.code = this.$store.state.users.code // 自动填充验证码
}catch(error){
alert(error.message)
}
}
}
}
</script>
### api.index.js
......
// 新增请求
export const reqGetCode = (phone)=>requests({url:`/user/passport/sendCode/${phone}`,method:'get'})
### store.users.index.js
......
import { reqGetCode } from "@/api"
const state = {
code:'' // 初始化
}
const actions = {
async getCode({commit},phone){
let res = await reqGetCode(phone)
if(res.code ==200 ){
commit('GETCODE',res.data)
return 'ok'
}else{
return Promise.reject(new Error('fail'))
}
}
}
- 用户点击'注册',发送数据到后端,注册成功以后,跳转到
登录页
### Register.index.vue
......
<div class="content">
......
<!--新增password-->
<input type="password" placeholder="请输入你的登录密码" v-model="password">
......
</div>
<div class="content">
......
<!--新增password1-->
<input type="password" placeholder="请输入确认密码" v-model="password1">
......
</div>
......
<script>
export default {
name: 'Register',
data(){
return {
phone:'',
code:'',
password:'', // 初始化值
password1:'',
agree:true
}
},
methods:{
......
async userRegister(){
try{
const {phone,code ,password,password1} = this; // 解构取值
// 和发送验证码一样的套路
phone && code && password && password1 && (await this.$store.dispatch('userRegister',{phone,code,password}))
this.$router.push('/login') // 跳转到登录页
}catch(error){
alert(error.message)
}
}
}
}
</script>
### api.index.js
......
// 配置请求(参数需传入一个对象)
export const reqUserRegister = (data)=>requests({url:'/user/passport/register',data,method:'post'})
### store.users.index.js
......
const actions = {
......
async userRegister({commit},user){
let res = await reqUserRegister(user)
if(res.code ==200 ){
return 'ok' // 不需要返回值
}else{
return Promise.reject(new Error('fail'))
}
}
}
登录模块
坑演示
:当用户点击登录
以后,莫名跳转到了其他页面
<form action="##"> <!--坑1: 必须删除action-->
......
<!--坑2: 必须阻止button在form中的默认行为-->
<button class="btn" @click="userLogin">登 录</button>
</form>
- 正确写法
<form> <!--删除action-->
......
<!--增加prevent-->
<button class="btn" @click.prevent="userLogin">登 录</button>
</form>
- 当用户点击
登录
按钮后,向后端发起登录请求
### login.index.vue
<form>
......
<!--绑定v-model-->
<input type="text" placeholder="邮箱/用户名/手机号" v-model="phone">
......
<!--绑定v-model-->
<input type="password" placeholder="请输入密码" v-model="password">
......
<!--绑定点击事件-->
<button class="btn" @click.prevent="userLogin">登 录</button>
</form>
......
<script>
export default {
name: 'Login',
methods:{
async userLogin(){
try{
const {phone,password} = this;
// 点击按钮就派发请求
phone && password && (await this.$store.dispatch('userLogin',{phone,password}))
// 响应正常即跳转到'home'页
this.$router.push('/home')
}catch(error){
alert(error.message)
}
}
},
data(){
return {
phone:'', // 初始化数据
password:''
}
}
}
</script>
### 响应正常的数据接口
- {code: 200, message: '成功', data: {…}, ok: true}
code: 200
data:
name: "18106951910"
nickName: "18106951910"
token: "2de26b8561c942108f17dfff97003bf3"
userId: 15500
[[Prototype]]: Object
message: "成功"
ok: true
### api.index.js
......
// 配置请求
export const reqUserLogin = (data)=>requests({url:'/user/passport/login',data,method:'post'})
### store.users.index.js
......
import { ......reqUserLogin } from "@/api"
const state = {
code:'',
token:'' // 初始化
}
const actions = {
......
async userLogin({commit},data){
let res = await reqUserLogin(data)
console.log(res)
if(res.code ==200 ){
commit('REQUSERLOGIN',res.data.token)
return 'ok'
}else{
return Promise.reject(new Error('fail'))
}
}
}
const mutations = {
REQUSERLOGIN(state,token){
state.token = token // 存储token
}
}
......
- 请求用户信息(必须带着
token
,否则服务器不知道你是谁,不知道该返回哪个用户的信息给你)
### api.reques.js
......
requests.interceptors.request.use((config)=>{
if(store.state.detail.uuid_token){
......
}
if(store.state.users.token){ // 带着token给服务器
config.headers.token = store.state.users.token
}
nprogress.start();
......
})
- 配置请求,获取
用户信息
,vuex
三连环
### api.index.js
......
export const reqUserInfo = (data)=>requests({url:'/user/passport/auth/getUserInfo',method:'get'})
### store.users.index.js
......
import { ......reqUserInfo } from "@/api"
const state = {
......
token:'',
userInfo:{} // 初始化
}
const actions = {
......
async getUserInfo({commit}){ // 获取用户信息
let res = await reqUserInfo()
if(res.code == 200){
commit('REQUSERINFO',res.data)
return 'ok'
}else{
return Promise.reject(new Error('fail'))
}
}
}
const mutations = {
......
REQUSERINFO(state,userInfo){
state.userInfo = userInfo // 保存
}
}
- 该请求在
home
组件派发,Header
组件需要的用户信息
直接从仓库中取
### Home.index.vue
......
mounted(){
......
this.$store.dispatch('getUserInfo') // 挂载完就派发请求,获取用户信息
}
### Header.index.vue
......
<!--未登录-->
<p v-if="!userName"> <!--根据userName是否有值,判断用户是否登录-->
<span>请</span>
<router-link to="/login">登录</router-link>
<router-link to="/register" class="register">免费注册</router-link>
</p>
<!-- 已登录 -->
<p v-else>
<a>{{ userName }}</a>
<a class="register" @click="logout" href="javascript:void(0)">退出登录</a>
</p>
......
import {mapState} from 'vuex'
......
computed:{
...mapState({
userName:state=>state.users.userInfo.name // 简化代码
})
},
- 现存问题: 当用户从
登录页
登录成功以后,跳转到home页
是没有问题的,页面一刷新,用户数据
就没有了
短暂性
修复刷新以后,用户信息无法保存的问题
- 先部署一个
token.js
脚本(作token
的本地持久化)
### utils.token.js
export const setToken = (token)=>{ // 设置
localStorage.setItem('TOKEN',token)
}
export const getToken = ()=>{ // 获取
return localStorage.getItem('TOKEN')
}
export const removeToken = (token)=>{ // 删除
localStorage.removeItem('TOKEN')
}
### store.users.index.js
import {setToken,getToken} from '@/utils/token'
const state = {
......
token:getToken() // 获取本地token
}
async userLogin({commit},data){
let res = await reqUserLogin(data)
if(res.code ==200 ){
setToken(res.data.token) // 新增,作token的本地持久化
commit('REQUSERLOGIN',res.data.token)
return 'ok'
}else{
console.log(res)
return Promise.reject(new Error('fail'))
}
},
### home.index.vue
......
mounted(){
......
this.$store.dispatch('getUserInfo') // 挂载用户信息
}
-
现在,无论
home页
怎么刷新,用户信息都有了,但还是有bug
,比如页面跳到search
,用户信息再次丢失...如果每个组件都挂载一次,当组件很多的时候,这种解决办法简直就是灾难...
用户退出登录
- 通知服务端
退出登录
的动作,把本地存储的token
和userInfo
删除
### api.index.js 配置请求
......
export const reqLogout = ()=>requests({url:'/user/passport/logout',method:'get'})
### store.users.index.js
......
const actions = {
......
async userLogout({commit}){
let res = await reqLogout() // 发起注销请求
if(res.code == 200){
commit('CLEAR')
return 'ok'
}else{
return Promise.reject(new Error('fail'))
}
}
}
......
const mutations = {
......
CLEAR(state){
state.token = '' // 清空用户信息和本地存储
state.userInfo = {}
removeToken();
}
}
### Header.index.vue
......
<p v-if="!userName">
......
</p>
<!-- 登录了 -->
<p v-else>
<a>{{ userName }}</a>
<!--绑定点击事件-->
<a class="register" @click="logout" href="javascript:void(0)">退出登录</a>
</p>
......
methods:{
......
async logout(){
try{
await this.$store.dispatch('userLogout') // 派发请求,跳转到home页
this.$router.push('/home')
}catch(error){
alert(error.message)
}
}
},
路由钩子-路由前置守卫
- demo测试
### router.index.js
......
let router = new VueRouter({ // 新增router接收
routes,
scrollBehavior(to,from,savedPosition) {
return {y:0};
}
})
router.beforeEach((to,from,next)=>{
// to:去哪里
// from:来自哪里
// next:是否放行
// 由于没有next(),现在所有的path都不通...
})
export default router // 导出
- 小改一下,变成这样子
router.beforeEach((to,from,next)=>{
next() // 允许所有的路由通过,等于没写...
})
- 我们把判断
token
的逻辑丢到路由钩子
处理(先处理业务需求,当用户完成登录以后,不能再跳到登录页)
### router.index.js
......
let router = new VueRouter({
......
})
router.beforeEach(async (to,from,next)=>{ // 注意添加 async
// 获取token和name
var token = store.state.users.token
var name = store.state.users.userInfo.name
if(token){
if(to.path == '/login' || to.path == '/register'){ // 如果跳到登录页/注册页,就重定向到home页
next('/home')
}else{
if(name){ // 如果有用户名就放行
next()
}else{
try {
await store.dispatch('getUserInfo') // 没有用户名就重新发请求,获取
next()
}catch(error){ // token失效就注销,跳到登录页
await store.dispatch('userLogout')
next('/login')
}
}
}
}else{ // 没有token就直接放行
next()
}
})