Vue3 学习——vue中使用vuex(超详细)
回顾: Vue——基础知识学习与项目实操 Vue——配置环境与创建项目
一、vuex 介绍
通过vuex 可方便各个组件间信息传递,vuex 类似react中的redux。
全局维护一个对象,该对象是一个状态store 树,包含 state 与操作 。所有组件之间实现交互时,不需要直接交互,而是分别对维护的全局state进行交互即可。
在store文件夹,默认有 index.js 文件。 index.js 中各部分解释如下:
import { createStore } from 'vuex'
export default createStore({
state: {
},
getters: {
},
mutations: {
updataUser(state,user){
state.user.username = user.username;
}
},
actions: {
},
modules: {
user: ModuleUser,
}
})
state中信息过长时,modules 用来将state 进行分割。 如下,将 state 树分割出一个user state。同样的user下也有这几部分内容:state、getters、mutation、actions、modules 。在新建的user. js 文件中写入如下代码:
const ModuleUser = {
state: {
id: "",
username: "",
photo: "",
},
getters: {
},
mutations: {
updateUser(state,user){
state.id = user.id;
state.username = user.username;
},
},
actions: {
}
modules: {
}
};
export default ModuleUser;
二、vuex 应用
全局存储用户信息,通过vuex维护实现登录功能与用户信息的访问。 登录操作时,如何向服务器验证。
1、补充
(1.1)登录验证方式
传统用户登录验证方式:(用户信息存在数据库里)
登录时客户端向服务器发送用户名和密码进行验证,然后服务器返回给客户端一个session_id,并将session_id存储到数据库里。 当客户端未来再访问服务器的链接时,都会默认将session_id带上,当服务器接收到客服端请求时,会将session_id取出,去数据库中查看是否存在,若存在,将id对应infomation传递给用户。 session_id 存在cookie 里,跨域(本地当前域名访问域名不同的API)很难处理。 jwt 用户登录验证方式:(用户信息存在jwt中) 登录时客户端向服务器发送用户名和密码进行验证,然后服务器返回给客户端一个jwt,jwt不会存储到数据库里。 当客户端未来再访问服务器请求时,如果请求需要验证,则需要附加上jwt。服务器端可以验证jwt是否合法。 如何验证jwt? 将用户信息info 加上私钥加密后生成签名,将info 与 签名 作为公钥传给用户。未来每次验证用户发送info与签名。服务器将info+私所存钥经过加密算法后如果生成签名,则验证通过。
(1.2)用ajax从url中获取数据并显示
格式如下:
$.ajax({
url:" 链接地址 ",
type: " 方法:POST、GET、DELETE等",
data: { 需要输入的参数 },
headers: {
'Authorization':"Bearer " + access, // 是否验证jwt:
},
success(resp){
成功时的回调函数
}
});
示例如下:
$.ajax({
url: 'https://app165.acapp.acwing.com.cn/myspace/userlist/',
type: 'get',
success(resp) {
users.value = resp;
}
})
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/getinfo/",
type: "GET",
data: {
user_id: access_obj.user_id,
},
headers: {
'Authorization':"Bearer " + access,
},
success(resp) {
...
}
})
},
error(){
...
}
});
2、vuex 如何维护 state
实现登录页面,向服务器发送用户名和密码,服务器返回jwt 。验证登录后显示用户列表信息。实现退出页面,退出时将所获取的jwt 删除即可。
(2.1)store 的构建
import $ from "jquery";
import jwt_decode from "jwt-decode";
const ModuleUser = {
state: {
id: "",
username: "",
photo: "",
followerCount: 0,
access: "",
refresh: "",
is_login:false,
},
getters: {
},
mutations: {
updateUser(state,user){
state.id = user.id;
state.username = user.username;
state.photo = user.photo;
state.followerCount = user.followerCount;
state.access = user.access;
state.refresh = user.refresh;
state.is_login = user.is_login;
},
updateAccess(state,user){
state.access = user.access;
},
logout(state){
state.id = "";
state.username = "";
state.photo = "";
state.followerCount = 0;
state.access = "";
state.refresh = "";
state.is_login = false;
}
},
actions: {
login: (context, data) => {
$.ajax({
url: "https://app165.acapp.acwing.com.cn/api/token/",
type: "POST",
data: {
username: data.username,
password: data.password,
},
success(resp) {
const {access,refresh} = resp;
const access_obj = jwt_decode(access);
setInterval(() => {
$.ajax({
url:"https://app165.acapp.acwing.com.cn/api/token/refresh/",
type: "POST",
data: {
refresh,
},
success(resp){
context.commit("updateAccess",resp.access);
}
});
},4.5*60*1000)
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/getinfo/",
type: "GET",
data: {
user_id: access_obj.user_id,
},
headers: {
'Authorization':"Bearer " + access,
},
success(resp) {
context.commit("updateUser",{
...resp,
access:access,
refresh:refresh,
is_login:true,
});
data.success();
}
})
},
error(){
data.error();
}
});
}
},
modules: {
}
};
export default ModuleUser;
(2.2)组件访问store(实现组件间交互)
- 点击Navbar中的登录按钮路由跳转登录组件页面,登录组件访问store中内容。登录组件如下:
<template>
<ContentBase>
<div class="row justify-content-md-center">
<div class="col-3">
<!-- 绑定事件并阻止默认发生的事件 -->
<form @submit.prevent="login">
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<input v-model="username" class="form-control" id="username">
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input v-model="password" type="password" class="form-control" id="password">
</div>
<div class="error-massage">{{error_massage}}</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
</div>
</ContentBase>
</template>
<script>
import ContentBase from '@/components/ContentBase.vue'
import { ref } from 'vue';
import { useStore } from 'vuex';
import router from '@/router/index';
export default {
name: 'LoginView',
components: {
ContentBase
},
setup(){
const store = useStore();
let username = ref('');
let password = ref('');
let error_massage = ref('');
const login = () => {
error_massage.value = "";
store.dispatch("login", {
username: username.value,
password: password.value,
success(){
router.push({name:'userlist'});
},
error(){
error_massage.value = "用户名或密码错误"
}
});
}
return{
username,
password,
error_massage,
login
}
}
}
</script>
<style scoped>
...
</style>
- 点击Navbar中的退出按钮,退出访问store中内容,删除jwt。如下:
<template>
......
<!-- 判断store里的is_login来确认已经登录,是否显示 -->
<ul class="navbar-nav" v-if="!$store.state.user.is_login"><!-- state 下的 user 这个module的state中的is_login属性 -->
<li class="nav-item">
<router-link class="nav-link " :to="{name:'login'}">登录</router-link>
</li>
<li class="nav-item">
<router-link class="nav-link" :to="{name:'register'}">注册</router-link>
</li>
</ul>
<!-- 已登录显示的内容 -->
<ul class="navbar-nav" v-else>
<li class="nav-item">
<router-link class="nav-link " :to="{name:'userprofile', params:{userId: 1}}">
{{$store.state.user.username}}
</router-link>
</li>
<li class="nav-item">
<!-- 退出只需把获取的jwt删除即可 -->
<a class="nav-link" @click="logout" style="cursor:pointer">退出</a>
</li>
</ul>
.......
<template>
<script>
import { useStore } from 'vuex';
export default {
name: "NavBar",
setup: () => {
const store = useStore();
const logout = () => {
store.commit("logout");
};
return {
logout,
}
}
}
</script>
<style scoped>
...
</style>
- 实现登录状态下点击用户列表,打开用户动态页面,且不同的用户列表打开用户动态页面时url中的id不同。
<template>
<ContentBase>
用户列表
<hr>
<div class="card" v-for="user in users" :key="user.id" @click="open_user_profile(user.id)">
<div class="card-body">
<div class="row">
<div class="col-1">
<img class="img-fluid" :src="user.photo" alt="">
</div>
<div class="col-11">
<div class="username">{{user.username}}</div>
<div class="reputation">22 <span>关注</span> {{user.followerCount}} <span>粉丝</span> 2 <span>获赞</span></div>
</div>
</div>
</div>
</div>
</ContentBase>
</template>
<script>
import ContentBase from '../components/ContentBase.vue';
import $ from 'jquery';
import { ref } from 'vue';
import {useStore} from 'vuex';
import router from '@/router/index';
export default {
name: 'UserListView',
components: {
ContentBase,
},
setup(){
let users = ref([]);
const store = useStore();
$.ajax({
url: 'https://app165.acapp.acwing.com.cn/myspace/userlist/',
type: 'get',
success(resp) {
users.value = resp;
}
})
const open_user_profile = (userId) => {
if(store.state.user.is_login){
router.push({
name: "userprofile",
params: {
userId,
}
}
)
}else{
router.push({
name:"login"
});
}
}
return {
users,
open_user_profile,
}
}
}
</script>
<style scoped>
....
</style>
效果如下(此时的用户动态是写死的,只显示写的内容。但url地址是传递的真正的userid。后续会将用户动态页面从云端动态获取):
(2.3) ajax 实现用户动态页面从云端动态获取
用ajax从url中获取数据并显示。
UserProfileView 用户动态组件(包含UserProfileInfo 用户信息组件和UserProfilePost 历史发帖组件)。将写死的信息改为动态ajax获取,代码如下:
<template>
<ContentBase>
<div class="row">
<div class="col-4">
<UserProfileInfo @follow="follow" @unfollow="unfollow" v-bind:user="user" />
<UserProfileWrite @submit="submit" />
</div>
<div class="col-8">
<UserProfilePosts :posts="posts"/>
</div>
</div>
</ContentBase>
</template>
<script>
import ContentBase from '../components/ContentBase.vue';
......
export default {
name: 'UserListView',
components: {
ContentBase,
UserProfileInfo,
UserProfilePosts,
UserProfileWrite,
},
setup: ()=>{
const store = useStore();
const route = useRoute();
const userId = route.params.userId;
const user = reactive({});
const posts = reactive({count: 3,});
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/getinfo/",
type: "GET",
data: {
user_id: userId,
},
headers: {
'Authorization':"Bearer " + store.state.user.access,
},
success(resp) {
user.id = resp.id;
user.username = resp.username;
user.photo = resp.photo;
user.followerCount = resp.followerCount;
user.is_followed = resp.is_followed;
}
});
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/post/",
type: "GET",
data: {
user_id: userId,
},
headers: {
'Authorization':"Bearer " + store.state.user.access,
},
success(resp) {
console.log(resp);
posts.posts = resp;
}
});
const follow = () => {
...
};
const unfollow = () => {
...
};
const submit = (content) => {
posts.count ++,
posts.posts.unshift(
{
id: posts.count,
userId: 1,
content: content,
},
)
}
return {
user: user,
follow,
unfollow,
posts,
submit,
is_me
}
}
}
</script>
<style scoped>
</style>
用户动态中用户信息组件与历史提交组件动态传递: 效果如下: 实现访问其它用户列表时不显示 UserProfileWrile 发布帖子组件。
首先 UserProfileView 中判断当前打开的id是不是登录的id 只有是当前登录的id才会显示 UserProfileWrile 组件
(2.4)ajax 实现当前登录用户动态发帖
删除以前写死发帖的函数,增加ajax访问url动态发帖函数:
(2.5)ajax 实现当前登录用户动态删帖
父组件中写删除函数绑定给子组件: 子组件调用: ajax 调用后端Api 将后端内容真正删除
(2.6)注册功能实现
|