什么是Vuex?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
Vuex解决了什么问题?
- 多个组件依赖于同一状态时,对于多层嵌套的组件的传参将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
- 来自不同组件的行为需要变更同一状态。以往采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
什么时候用Vuex?
- 多个组件依赖于同一状态时。
- 来自不同组件的行为需要变更同一状态。
Vuex的5个核心属性是什么?
分别是 state、getters、mutations、actions、modules 。
state全局存储状态,只可以读,不能进行写操作
- this.$store.state.xxx
- 批量使用Vuex的state状态,mapState辅助函数
使用mapState辅助函数, 利用对象展开运算符将state混入computed对象中
import {mapState} from 'vuex'
export default{
computed:{
...mapState(['price','number'])
}
}
getters对数据进行处理,类似计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
- this.$store.getters.xxx
- mapGetters 辅助函数
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
mutations 是vuex唯一一个可以修改数据的地方,操作为同步函数。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)
- this.$store.commit(‘SET_NUMBER’,10)
mutations: {
SET_NUMBER (state, n) {
state.count += n
}
}
actions 类似mutations,不直接修改数据,通过commit调用mutations修改数据,且可以异步操作
区别:
- action 提交的是 mutation,而不是直接变更状态。mutation可以直接变更状态。
- action 可以包含任意异步操作。mutation只能是同步操作。
- 提交方式不同,action 是用this. s t o r e . d i s p a t c h ( ′ A C T I O N N A M E ′ , d a t a ) 来 提 交 。 m u t a t i o n 是 用 t h i s . store.dispatch('ACTION_NAME',data)来提交。mutation是用this. store.dispatch(′ACTIONNAME′,data)来提交。mutation是用this.store.commit(‘SET_NUMBER’,10)来提交。
- 接收参数不同,mutation第一个参数是state,而action第一个参数是context,其包含了
{
state, // 等同于 `store.state`,若在模块中则为局部状态
rootState, // 等同于 `store.state`,只存在于模块中
commit, // 等同于 `store.commit`
dispatch, // 等同于 `store.dispatch`
getters, // 等同于 `store.getters`
rootGetters // 等同于 `store.getters`,只存在于模块中
}
相同点:
- 都可以接收外部提交时传来的参数。 this. s t o r e . d i s p a t c h ( ′ A C T I O N N A M E ′ , d a t a ) 和 t h i s . store.dispatch('ACTION_NAME',data)和this. store.dispatch(′ACTIONNAME′,data)和this.store.commit(‘SET_NUMBER’,10)
modules
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
使模块具有更高的封装度和复用性,你可以通过添加namespaced: true 的方式使其成为带命名空间的模块。
export default{
namespaced: true,
state,
getters,
mutations,
actions
}
例子说明:
Home组件点击修改数据,About组件如何响应式改变
文件目录
Home组件:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<div @click="change">点击</div>
<div>{{ params.name }}</div>
<div>{{ sidebar.name }}</div>
<HelloWorld msg="Welcome to Your Vue.js App" />
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld.vue'
// import { mapGetters } from "vuex"
export default {
name: 'Home',
components: {
HelloWorld
},
data() {
return {
params: {}
}
},
computed: {
// ...mapGetters(["sidebar"])
sidebar() {
return this.$store.getters.sidebar
}
},
methods: {
change() {
console.log(this.$store)
// this.params = this.$store.getters.sidebar
this.$store.dispatch("app/sideBar", "hello suzilong")
}
}
}
</script>
About组件:
<template>
<div class="about">
<h1>This is an about page</h1>
<div>{{ sidebar.name }}</div>
</div>
</template>
<script>
import { mapGetters } from "vuex"
export default {
name: 'About',
components: {},
data() {
return {
params: {}
}
},
computed: {
...mapGetters(["sidebar"])
// name() {
// return this.$store.getters.sidebar.name
// }
},
methods: {
}
}
</script>
index.js:
import Vue from "vue";
import Vuex from "vuex";
import getters from "./getters";
Vue.use(Vuex);
// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context("./modules", true, /\.js$/);
// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, "$1");
const value = modulesFiles(modulePath);
modules[moduleName] = value.default;
return modules;
}, {});
export default new Vuex.Store({
modules,
getters
// state,
// mutations: {},
// actions: {},
// modules: {}
});
getters.js:
const getters = {
sidebar: state => state.app.sidebar
};
export default getters;
app.js
const state = {
sidebar: {
name: "suzilong",
type: "hello word"
}
};
const mutations = {
SET_SIDEBAR: (state, name) => {
state.sidebar.name = name;
}
};
const actions = {
sideBar({ commit }, name) {
debugger;
setTimeout(() => {
commit("SET_SIDEBAR", name);
}, 1000);
}
};
export default {
namespaced: true, // namespaced: true 的方式使其成为带命名空间的模块
state,
mutations,
actions
};
有一篇比较好的Vuex面试题汇总