先搞定静态组件,拷贝LoginRegister

注册模块

  • 表单的验证最后再做
  • 先完成发送验证码的逻辑
### 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">登&nbsp;&nbsp;录</button>
</form>
  • 正确写法
<form> <!--删除action-->
  ......
  <!--增加prevent-->
  <button class="btn" @click.prevent="userLogin">登&nbsp;&nbsp;录</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">登&nbsp;&nbsp;录</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,用户信息再次丢失...

    如果每个组件都挂载一次,当组件很多的时候,这种解决办法简直就是灾难...

用户退出登录

  • 通知服务端退出登录的动作,把本地存储的tokenuserInfo删除
### 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()
	}
	
})