VUEX官方文档,可以学习详细,这篇笔记是写vue2升级vue3后使用vuex,或者忘记如何使用vuex做状态管理的情况
vueX状态管理
Vue 3 与 Vue 2 有很多不同之处,但 Vuex 的核心概念——State、Getters、Mutations、Actions 和 Modules——保持基本一致。Vuex 4 是为 Vue 3 设计的,所以确保你已经安装了合适的版本。
下面我将逐步引导如何在 Vue 3 项目中使用 Vuex:
npm install vuex@next
yarn add vuex@next
import { createStore } from 'vuex';
export const store = createStore({
state: {
count: 0
},
mutations: {
//类似 mvc框架的service层参数是state状态管理
increment(state) {
state.count++;
}
},
//类似controller
actions: {
// 参数是上下文对象
increment(context) {
//类似提交service层
context.commit('increment');
}
},
//类似计算属性
getters: {
doubleCount(state) {
return state.count * 2;
}
}
});
在这个例子中,我们定义了一个简单的计数器应用,它包含一个 count 状态,一个用于增加 count 的 increment mutation,一个调用该 mutation 的 increment action,以及一个返回 count 的两倍的 doubleCount getter。
3. 在 Vue 应用中使用 Vuex Store
在你的 main.js(或 main.ts)文件中,你需要导入刚刚创建的 Store,并将其添加到 Vue 应用实例中。
src/main.js 的内容更新如下:
import { createApp } from 'vue';
import App from './App.vue';
import { store } from './store';
//也可以import store from './store/index.js'; //导入自己定义的vuex模块进行挂载
const app = createApp(App);
app.use(store);
app.mount('#app');
在 Vue 项目中,store 文件夹通常用于存放 Vuex 相关的代码,包括 state、mutations、actions 等。当你创建一个 Vuex store 时,你可以将相关代码放在这个文件夹下的 index.js 文件中。
这样命名的原因是,当你从一个文件夹中导入模块时,如果没有指定具体的文件名,JavaScript 就会尝试导入该文件夹下名为 index.js 的文件。因此,将文件命名为 index.js 是一种常见的做法,让代码更简洁。
但是,在你的项目的主文件(例如 main.js)中,你可以选择任何名称来导入你的 store。这个名称只是一个你选择的变量名,用于在项目中引用你的 store。
例如,你可以这样导入 store:
import { store } from ‘./store’;
这里的 store 变量名是你选择的名称,你可以根据项目的需要选择任何合适的名称。在导入语句的右侧,‘./store’ 是相对路径,指向 store 文件夹。由于我们没有指定具体的文件名,因此 JavaScript 会尝试导入 store 文件夹下的 index.js 文件。
如果你将 index.js 文件重命名为另一个名称(例如 store.js),你就需要在导入语句中指定新的文件名:
import { store } from ‘./store/store.js’;
总的来说,index.js 是文件名,用于组织 Vuex 相关的代码;而 store 是变量名,用于在项目中引用 Vuex store。两者的命名可以是不同的。
<template>
<div>
<p>Count: {{ count }}p>
<p>Double Count: {{ doubleCount }}p>
<button @click="increment">Incrementbutton>
div>
template>
<script>
import { computed } from 'vue';
//导入实列函数
import { useStore } from 'vuex';
export default {
setup() {
const store = useStore();//创建实列对象
//使用计算属性接受是为了防止数据接受响应式,或者还需要ref,reactive包装数据
const count = computed(() => store.state.count);
const doubleCount = computed(() => store.getters.doubleCount);
//VUE2:this.$store.getters.
const increment = () => {
//提交给action
store.dispatch('increment');
};
return {
count,
doubleCount,
increment
};
}
};
script>
在这个例子中,我们使用 useStore 来访问 Vuex Store。我们还使用 computed 来创建响应式地引用 Store 的 state 和 getters。最后,我们在 increment 方法中分发一个 action。
Modules 是 Vuex 中一种重要的组织机制,特别是对于大型应用程序。通过 Modules,你可以将 Store 分解成多个模块,每个模块都拥有自己的 state、mutations、actions、getters,甚至是嵌套模块。这样可以让代码组织得更有序,更易于维护。
下面我将详细说明如何在 Vue 3 项目中使用 Vuex 的 Modules:
export const user = {
namespaced: true, // 开启命名空间
state: {
name: 'John Doe'
},
mutations: {
setName(state, name) {
state.name = name;
}
},
actions: {
//第一个参数是上下文对象context,包含了vuex的几个状态,state,commit,这里解构,只要commit,用于执行个体mutation
updateName({ commit }, name) {
commit('setName', name);
}
},
//类似生命周期
getters: {
fullName(state) {
return `${state.name} Smith`;
}
}
};
在这个模块中,我们定义了一个名为 user 的对象,其中包含了该模块的 state、mutations、actions 和 getters。namespaced: true 表示我们想要该模块具有自己的命名空间,这使得它不会与其他模块冲突。
2. 导入和使用 Vuex Modules
接下来,在你的 store 的 index.js 文件中,你需要导入并使用刚刚创建的模块:
import { createStore } from 'vuex';
import { user } from './user';
export const store = createStore({
modules: {
user
}
// 你还可以定义其他的 state、mutations 等
});
这里,我们在 createStore 函数的参数对象中添加了一个 modules 属性,其中包括了我们想要使用的模块。
3. 在组件中使用 Vuex Modules
在组件中使用命名空间的模块与使用全局的 Vuex Store 略有不同。你可以使用 mapState、mapGetters、mapMutations 和 mapActions 辅助函数,或者直接通过命名空间访问模块:
<template>
<div>
<p>{{ fullName }}p>
<button @click="updateName('Jane Doe')">Change Namebutton>
div>
template>
<script>
import { computed } from 'vue';
import { useStore } from 'vuex';
export default {
setup() {
const store = useStore();
//模块下获取state
const name =store.statte.user.name
//对应路径下 模块名/getter名
const fullName = computed(() => store.getters['user/fullName']);//数组形式
const updateName = (name) => {
//同上 vue2中这个是this.$store.getter.user.name 就很麻烦
store.dispatch('user/updateName', name);
};
return {
fullName,
updateName
};
}
};
script>
注意,在访问模块的 getters 和 actions 时,我们使用了 ‘user/fullName’ 和 ‘user/updateName’ 这样的字符串。这些字符串包括了模块的命名空间和 getter 或 action 的名称。
通过使用 Modules,你可以使 Vuex Store 更加模块化和可维护。你可以创建许多小的、聚焦于特定功能或领域的模块,而不是将所有的逻辑都放在一个大的 Store 中。在大型项目中,这种结构可以非常有用。
当我们将对象或方法挂载到Vue实例的原型上或Vue 3中的全局可用性机制,确实能够减少重复导入并提供方便的全局访问。
通过挂载,只需要在适当的位置进行一次导入或实例化,并将其挂载到Vue实例中,然后你可以在整个应用程序的组件中使用this.$参数名来访问挂载的对象或方法,而无需重复导入或创建实例。
这种做法的好处包括:
代码复用和维护性:通过挂载对象或方法到全局,使其在整个应用程序中可以被重复使用,避免了在每个组件中重复导入和实例化的冗余代码。
可读性和简洁性:使用this.$参数名来访问全局对象或方法,代码更加简洁,易读性更强。
全局共享状态:通过挂载,在不同的组件中可以共享相同的对象或方法,方便进行状态管理或共享数据。
比如 vue2中:
import axios from 'axios';
import store from './store/index.js'; //导入自己定义的vuex模块进行挂载
Vue.prototype.$axios = axios。
vue.protype.$store=store
这样就可以在全局使用this.$store.state.参数进行使用,但是vue3中不再使用这种方式挂载了,而是使用全局配置实现挂载
app.config.globalProperties.$store = store
此时的代码就可以使用全局状态管理了
import App from './App'
import store from './store/index.js'; //导入自己定义的vuex模块进行挂载
// #ifndef VUE3
import Vue from 'vue'
import './uni.promisify.adaptor'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App,
})
app.use(store);
app.$mount()
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
app.config.globalProperties.$store = store //全局挂载 {{ $store.state.count }}都可以使用
return {
app
}
}
axios同理
新建一个http.js
import axios from 'axios';
const http = axios.create({
baseURL: 'http://your-api-base-url.com',
timeout: 10000,
});
// 请求拦截器
http.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
http.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.response && error.response.status === 401) {
console.error('未授权,请登录!');
// 这里可以做一些未授权的处理,例如跳转到登录页面等
}
return Promise.reject(error);
}
);
export default http;
全局挂载
import { createApp } from 'vue';
import App from './App.vue';
import http from './http';
const app = createApp(App);
app.config.globalProperties.$http = http;
app.mount('#app');
然后任何地方都可以使用
this.$http.get('/some-endpoint').then(response => {
console.log(response.data);
});