Vuex 是什么?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex原理
resetStoreVM(this, state) 他就是整个vuex的关键
// src/store.js
function resetStoreVM (store, state, hot) {
// 省略无关代码
Vue.config.silent = true
store._vm = new Vue({
data: {
$$state: state
},
computed
})
}
去除了一些无关代码后我们发现,其本质就是将我们传入的state作为一个隐藏的vue组件的data,也就是说,我们的commit操作,本质上其实是修改这个组件的data值,结合computed,修改被defineReactive代理的对象值后,会将其收集到的依赖的watcher中的dirty设置为true,等到下一次访问该watcher中的值后重新获取最新值。
这样就能解释了为什么vuex中的state的对象属性必须提前定义好,如果该state中途增加一个属性,因为该属性没有被defineReactive,所以其依赖系统没有检测到,自然不能更新。
vuex中的store本质就是没有
template的隐藏着的vue组件;
vuex核心流程
- Vue Components:Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
- dispatch:操作行为触发方法,是唯一能执行action的方法。
- actions:操作行为处理模块。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
- commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
- mutations:状态改变操作方法。是Vuex修改state的唯一推荐方法,其他修改方式在严格模式下将会报错。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
- state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
- getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。
Vue组件接收交互行为,调用dispatch方法触发action相关处理,若页面状态需要改变,则调用commit方法提交mutation修改state,通过getters获取到state新值,重新渲染Vue Components,界面随之更新。
vuex目录
这是取自vuex官方文档的目录展示,对于初次接触或者仅限于了解的来说,没有必要照搬文档所给的目录结构,按自己的需求灵活修改,当然文档给的目录结构是合理的,层次感和逻辑感更好。
vuex实现
遇到问题时,可以查看store实例,有可能会给你灵感。
1.装载实例
main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import store from './store/' // 引入store
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
store, // 挂载
components: { App },
template: '<App/>'
})
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
})
1.1 挂载跟级别数据/事件
最简单的实现方式,就是把所需state,getter,mutations,actions等直接挂在根级别上,当然这样会导致根级别特别混乱,这个时候就引出了modules,下面会讲到。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 根state
const state = {
testField: "444"
}
// 根getter
const getters = {
getTestField(state) {
return state.testField;
}
}
export default new Vuex.Store({
state,
getters
})
这里仅仅做一个举例,并未写出所有,actions、mutations等和getter的实现方式是一样的。
1.2 引入modules
模块化的思维在项目中已经被用烂了,或者也已经听烦了,当然vuex里也有模块,也会模块化。
创建module_1.js
vuex中的store分模块管理,需要在store的index.js中引入各个模块,为了解决不同模块命名冲突的问题,将不同模块的namespaced:true,之后在不同页面中引入getter、actions、mutations时,需要加上所属的模块名,实际上如果你能确保不会冲突,namespaced:true的作用可有可无。
export default {
namespaced: true,
state: {
testField_1: "11",
},
getters: {
getTestField(state) {
return state.testField_1;
},
},
mutations: {
setTestFieldMutation(state, testField_1) {
state.testField_1 = testField_1;
},
},
actions: {
setTestFieldAction(context, testField_1) {
context.commit("setTestFieldMutation", testField_1);
},
},
};
挂在store上,挂载方式很简单,store提供了modules方法。
import Vue from 'vue'
import Vuex from 'vuex'
import module_1 from './modules/module_1'
Vue.use(Vuex)
// 根state
const state = {
testField: "444"
}
// 根getter
const getters = {
getTestField(state) {
return state.testField;
}
}
export default new Vuex.Store({
state,
getters,
modules: {
modules_1
}
})
1.3 使用
当然,使用的方式也是多种多样的,看你喜欢哪一种,推荐使用辅助函数mapGetters等。
模块中的使用,和根级别的还是有一点区别,当然如果把模块当作根级别下的一个属性就不难理解了。
<template>
<div class="hello">
<h1>{{ getTestFields }}eeeee</h1>
<h1>{{ getModulesTestField }}eeeee</h1>
<h1>{{ getTestField_2 }}eeeee</h1>
// 简单粗暴型,直接获取
<h1>{{ $store.state.testField }}eeeee</h1>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
// import { createNamespacedHelpers } from 'vuex'
// const { mapGetters } = createNamespacedHelpers('modules_2')
export default {
name: 'HelloWorld',
data () {
return {
}
},
computed: {
// 方式一: 直接引入所需module + module下的方法
...mapGetters({
getTestField_2: 'modules_2/getTestField'
}),
// 方式二:先声明module, 引入该module下的方法
// ...mapGetters('modules_2', ['getTestField']),
/*
* 方式三:和方式二是同样的原理
* import { createNamespacedHelpers } from 'vuex'
* const { mapGetters } = createNamespacedHelpers('modules_2')
*/
// ...mapGetters(['getTestField']),
/*
* 默认直接获取根级别state下的数据
*/
getTestFields() {
console.log(this.$store)
/*
* 如果想调用$store的getter/action,需要是根级别,或者module存在namespaced属性
*/
return this.$store.getters['modules_2/getTestField']
// return this.$store.state.testField
},
// 直接获取模块内的state数据
getModulesTestField() {
return this.$store.state.modules_1.testField_1
}
},
methods: {
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1 {
font-weight: normal;
}
</style>
1.4 dispatch
下面的前提都是在命名不规范,所有action全部一样的情况下(极端情况),如果命名规范,那就正常调用即可。
// 默认查找所有不存在namespaced: true,属性的module和根级别下的action
this.$store.dispatch("setTestFieldAction", "I did");
// 如果想调用module下的action,需要有namespaced: true,属性
this.$store.dispatch("modules_2/setTestFieldAction", "I did 2 ");
// unknown action type: modules_1/setTestFieldAction
this.$store.dispatch("modules_1/setTestFieldAction", "I did 1 ");
当然,actions的使用和getter一样,方法异曲同工,在此列举一种方式,其他方式,可以私下试一下,也是完全可行的。
methods: {
// 使用actions的辅助函数,方式和mapGetters同理
...mapActions({
mdu_2: "modules_2/setTestFieldAction"
})
},
mounted() {
// 使用辅助函数mapActions
this.mdu_2("I do 2");
}
1.5 模块间调用
vuex中各个modules间的交互,当然,是可以直接使用的,毕竟都是挂载在store上的。
但,说的是另一种,rootState, rootGetters,rootMutations,rootActions这四种。
1.5.1 rootGetters和rootState
使用方式很简单,就把它当成对象调用即可。
getTestField(state, getters, rootState, rootGetters) {
console.log(state, "state");
console.log(getters, "getters");
console.log(rootState, "rootState");
console.log(rootGetters, "rootGetters");
return state.testField_1 + rootGetters["modules_2/getTestField"];
},
1.5.2 commit 的 dispatch
// modules_1.js
actions: {
setTestFieldAction({ dispatch, commit, getters, rootGetters }) {
console.log(commit, "commit");
dispatch("modules_2/setTestFieldAction2", "rrrrrr", { root: true })
commit("setTestFieldMutation", "testField_1");
commit("setTestFieldMutation3", "dddddd", { root: true });
},
},
到此就可以了,vuex基本可以在各个模块中交互,引用其他模块的state和action,
对于vuex的工作原理可以参考这篇文章。
把自己所知所结做一个记录,当然,这篇文章,还够不上入门,以后回继续补充,有不同见解,欢迎打扰。