Vuex入门教程快速入门简介
文章目录
什么是Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
他相当于Vue的共享数据层 / 共享内存,当你需要开发业务涉及到数据需要跨页面,跨组件时,使用Vuex比使用参数传递,要方便得多。
可以说,当你需要开发一个稳定可靠的vue项目(现在应该很少有单页面的vue项目了),你就必然用到vuex。
Vuex可以干什么
1.高效的实现不同组件间的数据共享和交互。
2.规范数据的操作,使得model层代码基本统一在一个地方,这使得你的项目结构更加清晰,便于维护升级。
3.利用vue Devtools
监控数据变化,方便调试,实现time-travel等高级调试功能。
工作流程
vuex的作用和模块可以从其工作流程图上反映出来。
核心概念
安装
npm install vuex --save
或者
yarn add vuex
模块化打包系统中(例如webpack),还需要调用如下代码,才能真正安装。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
全局初始化多模块Store
一般情况下,推荐在全局给根组件注册vuex,从而给所有组件使用上vuex,后面的介绍中,我们也跳过那些在官网介绍中,分别给单独页面写vuex的情况,因为那种方式并不实用。
一般情况下,项目很容易用到多个模块,所以默认推荐的初始化方法,是多模块初始化。
文件src/store/index.js
如下,其中state
,actions
,mutations
,getters
的具体内容该怎么写,在后文有介绍,此处省略。
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const context = require.context(".", true, /\.js$/);
const moduleStores = {};
context.keys().forEach(key => {
if (key === "./index.js") return;
const funname = key.replace(/(.*?\/)(\w*)(\.js)/, "$2");
const v = context(key).default;
moduleStores[funname] = {
...v,
namespaced: true,
};
});
const rootStore = {
state:{
},
actions: {
},
mutations: {
},
getters: {
}
}
export default new Vuex.Store({
...rootStore,
modules: {
...moduleStores,
},
});
注意要在项目初始化的时候,使用这个文件,因此在main.js
或者app.js
文件中添加如下代码:
import store from "@/store";
new Vue({
el: '#app',
store: store,//确保store功能被添加
})
当项目数据有多个模块时,在src/store/
文件夹下增加其他模块(模块名称将和文件名称一致),文件内容如下:
const state = {
testdata:{},
}
const actions = {
async getTestData({commit, state, dispatch }, config = {}){
这里调用api,然后调用commit("setTestData",result);
}
}
const mutations = {
["setTestData"](state, data){
state.testdata= data;
},
}
const getters = {
["getTestData"](state) {
return state.testdata;
},
}
export default {
namespaced: true,
state,
actions,
mutations,
getters,
};
State单一状态树
所有的应用级状态(所有的共享数据)都存储在这个实例上。他是唯一数据源(SSOT)。
State中的数据和Vue中的data
遵循相同规则。
以下是vue页面/组件中多种使用state中数据的方式:
1.在computed中单个属性指定。
computed: {
count () { return this.$store.state.count }
}
2.在computed中使用mapState批量指定(推荐用法
)
import { mapState } from 'vuex';//需要先引入辅助函数
export default {
//......
computed: {
...mapState({
count:state => state.count,
countAlias: 'count', //和count一样
userData:state => state.user.userData, //绑定user模块中的userData
doneTodosCount(state){ //进行数据计算并返回
return state.todos.filter(todo => todo.done).length
},
}),
localComputed(){},
}
//......
}
Getter计算属性
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
例如前文中进行数据计算的doneTodosCount
,
如果定义在getters里面大概就是这样:
getters:{
["doneTodos"](state) {//求数组
return state.todos.filter(todo => todo.done)
},
doneTodosCount: (state, getters) => {//把别人的getters作为第二个参数
return getters.doneTodos.length
},
getTodoById: (state) => (id) => {//查询数据,返回的实际是一个函数,可以在vuex模块内其他函数使用,也可以外部调用
return state.todos.find(todo => todo.id === id)
},
}
这时,可以在任意组件中直接使用这个值,例如computed中:
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
也可以通过mapGetters批量获取数据(推荐用法
)
import { mapGetters } from 'vuex'
export default {
//...
computed: {
...mapGetters({
doneTodos:"doneTodos",
doneTodosCount:"doneTodosCount",
getTodoById:"getTodoById",
}),
},
}
可以在js函数中查询数据
store.getters.getTodoById(2); //vuex模块内调用 -> { id: 2, text: '...', done: false }
this.$store.getters.getTodoById(2);//外部调用
这个写法相对于直接使用mapstate的最大好处,是不用在view层去计算值(切换页面不会导致重新计算),仅当数据发生变化时,计算数值。
Mutation更改状态
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
在store/index.js
文件或者模块文件
中定义如下:
mutations:{
increment (state) {
state.count++;
},
["setTestData"](state, data){//以对象方式提交荷载 [Payload]
state.testdata= data;
},
}
使用如下,任意地方调用如下语句:
this.$store.commit('increment');
this.$store.commit('setTestData', data);
也可以使用mapMutations批量定义函数,然后直接调用函数:
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
setTest: 'setTestData' // 将 `this.setTest(data)` 映射为 `this.$store.commit('setTestData', data)`
})
}
}
需要注意的是:
1.mutation必须是同步函数,尽量简单,没有复杂计算过程。
2.严格模式下,页面需要通过commit才可以修改数据,但是不推荐这样做,因为要写的代码太多了,一般是通过v-model
的功能将view上的数据直接与state中的数据绑定,直接修改。因此,实际上页面/组件上一般用不到commit
,mapMutations
等相关功能。
3.Mutation 需遵守 Vue 的响应规则。当使用commit('setTestData', data)
的时候,data与原有值不能是同一个对象,否则会导致界面无法自动更新,推荐的写法在以下方法中二选一,
a).函数内,想办法触发更新:
Vue.set(obj, 'newProp', 123);
//或者
state.obj = {...state.obj, newProp: 123}
b).函数外,提交时就是新对象:
commit("setTestData", JSON.parse(JSON.stringify(data)));
//或者
commit("setTestData", {...data});
Actions发起动作
从流程图中,很容易理解到,代码应当使在页面/组件响应时通过dispatch
调用Actions
,然后在Actions
里异步访问API
,回调时通过commit
调用Mutation
修改State
,然后反映到界面变化上来。
当然也可以封装一些同步调用的Actions
,调用时候计算数据,然后commit
。
actions:{
//远程调用
async tryDoAction({commit, state, dispatch }, config = {}){
//发起远程调用,然后回调中修改数据层
this.$api.api_xxx.tryDoAction(config).then(res=>{
commit("setTestData", result.data);
})
},
//本地调用-->直接修改数据
localAction({commit, state, dispatch}, data = {}){
data.p = 3.1415926;//数据调整
commit("setTestData", {...data});//确保界面刷新
},
//连环调用
async actionA ({ commit, state, dispatch }) {
commit('gotData', await getData())
},
async actionB ({ commit, state, dispatch }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
别看Actions这块的介绍短,实际开发的过程中,大部分用户操作,都是在调用actions,然后产生远程调用,所以这块代码会比较多。
从vuex3到vuex4
主要是适应vue3.x版本,vuex需要升级到vuex4.x以上版本,参考官方文档:
https://next.vuex.vuejs.org/guide/migrating-to-4-0-from-3-x.html
其他文档
VUE入门指北
VUE入门指北——(2)MVVM、数据、方法、生命周期
VUE入门指北——(3)单文件组件.vue文件
其他参考资料
知乎 Vuex,从入门到入门
简书 VueX(Vue状态管理模式)
cnblogs 五分钟搞懂Vuex