Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。Vuex背后的基本思想,就是前面所说的单向数据流。下图就是Vuex实现单向数据流的示意图。
1.Vuex的状态存储是响应式的:就是当你的组件使用到了这个Vuex的状态,一旦它改变了,所有关联的组件都会自动更新相对应的数据,这样开发者省事很多。
2.不能直接修改Vuex的状态:如果是个全局对象变量,要修改很容易,但是在Vuex中不能这样做,想修改就得使用Vuex提供的唯一途径:显示地提交(commint)mutations来实现修改。这样做的好处就是方便我们跟踪每一个状态的变化,在开发过程中调试的时候,非常实用。
1 .安装Vuex
npm install vuex --save2 . 引用vuex,创建仓库store。 创建 store.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //数据 const state={ count:10 } //action 执行异步操作,不可以修改state数据 const actions={ getParamSync (context,Object) { //处理异步操作 setTimeout(()=>{ //3.通过commit提交一个名为getParam的mutation //action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation context.commit('increment',Object) },3000) } } //mutation 可直接修改state数据 const mutations={ increment(state,value){ state.count += value; }, decrement(state,value){ state.count -=value; } } //getter const getters = { newCount:state => state.count * 3 } export default new Vuex.Store({ state, mutations, actions, getters })3 . 在 main.js中注册到根组件中
import store from './store/store.js' new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })action: 用来解决异步流程来改变state数据。 而matution是直接进行同步操作的,如果你在mutations里进行异步操作,你会发现没用,并不会起任何效果 只有通过action=>mutations=>states,这个流程进行操作,具体步骤如下:
export default new Vuex.Store({ //存放数据 state: { count: 5, }, //2.接受dispatch传递过来的方法和参数 actions: { getParamSync (context,val) { //处理异步操作 setTimeout(()=>{ //3.通过commit提交一个名为getParam的mutation //action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation context.commit('increment',val) },3000) } } })vue组件中通过事件触发action
methods: { getVal() { //1.通过dispatch将方法getParamSync和多个参数{name,age,sex}传递给actions this.$store.dispatch('getParamSync',1) } }Vuex给我们提供修改仓库 store中的状态的唯一办法就是通过提交mutation
我们在 mutations中定义了一个叫increment的函数,函数体就是我们要进行更改的地方
会接受 state作为第一个参数,第二个是自定义传参
const store = new Vuex.Store({ //state存储应用层的状态 state:{ count:5 //总数:5 }, mutations:{ increment(state,value){ state.count += value; } } });我们在提交commit时候,字符串参数increment,就是对应在 mutations中的increment。 一般通过方法或钩子触发,例如:
methods: { getVal(event) { //获取当前的按键的值 let value = event.target.dataset.value; //通过commit提交一个名为increment的mutation this.$store.commit("increment", value); } }在组件中获取{{count}}方式:
export default { computed: { count(){ return this.$store.state.count; } } };1.increment官方说是type,其实就是注册的事件名 2.可以是单个参数 3.如果是多个参数,我们则用对象放入,否则会报错
可以认为,getters 是store的计算属性,类似于computed,对state里的数据进行一些过滤,改造等等
假设我们要在state.count的基础上派生出一个新的状态newCount出来,就适合使用我们的 getters
getters 接受 state 作为其第一个参数
const store = new Vuex.Store({ //state存储应用层的状态 state:{ count:5 //总数:5 }, getters:{ newCount:state => state.count * 3 } });在组件中获取{{newCount}}方式:
export default { computed: { newCount(){ return this.$store.getters.newCount; } } };当做的项目比较大时,推荐大家用一下方式,将vuex每个部分拆分 运用vuex语法糖mapMutations mutations.js
const mutations = { SET_NEWS(state, val) { state.news = val } } export default mutations存储数据( a.vue文件 )
import { mapMutations } from "vuex"; // 引入mapMutations export default { methods: { ...mapMutations({ // 将changeNews与mutations中的SET_NEWS关联 changeNews: "SET_NEWS" }), submit(){ // 提交一个名为changeNews的mutation,并传入参数val let val = 'test news'; this.changeNews(val);// 相当于this.$store.commit("changeNews", val); } } }2.获取数据( b.vue文件 )
import { mapGetters } from "vuex"; // 引入mapGetters export default { computed: { // 用vuex读取数据(读取的是getters.js中的数据) // 相当于this.$store.getters.news(vuex语法糖) ...mapGetters(["news"]) }, created() { // 获取getters中news数据 console.log(this.news); } }3.store文件目录结构 index.js:
import Vue from 'vue' import Vuex from 'vuex' import * as actions from './actions' import * as getters from './getters' import state from './state' import mutations from './mutations' //每次修改state都会在控制台打印log import createLogger from 'vuex/dist/logger' Vue.use(Vuex) const debug = process.env.NODE_ENV !== 'production' export default new Vuex.Store({ actions, getters, state, mutations, strict: debug, // 当debug=true时开启严格模式(性能有损耗) plugins: debug ? [createLogger()] : [] })state.js:
const state = { news: {} } export default statemutations.js:
const mutations = { SET_NEWS(state, val) { state.news= val } } export default mutationsgetters.js:
// 通常通过getters取数据 (this.$store.getters.news;) export const news = state => state.news // 不做其他处理 直接映射出去actions.js:
//异步处理 ...4.使用store
在main.js中引用
import store from './store' //vuex存储文件 ... ... new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })背景:在Vue中State使用是单一状态树结构,应该的所有的状态都放在state里面,如果项目比较复杂,那state是一个很大的对象,store对象也将对变得非常大,难于管理。 module:可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
一般结构
const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB})使用
// computed属性,从store 中获取状态state,不要忘记login命名空间。 computed: { useName: function() { //return store.state.login.useName return this.$store.state.moduleA.useName } }, methods:{ changeName(){ this.$store.dispatch("changeName",'jason'); } } }store.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const test1 = { namespaced: true, state: { name: 'moduleA', type: 'module A' }, mutations: { updateNameByMutation(state, appendStr){ state.name = state.name + " append Str: " + appendStr } }, actions: { udpateNameByAction({commit}, appendStr) { commit("updateNameByMutation", appendStr) } }, getters: { getNameA(state){ return state.name } } } const test2 = { // 当namespaced=true 时, vuex, 将会自动给各自module 添加访问路径名。 方便区分moduel namespaced: true, state:{ name: 'moduleB', type: 'module B' }, mutations: { updateNameByMutation(state, appendStr){ state.name = state.name + " append Str: " + appendStr } }, actions: { // 如果不使用命名空间, 那么view 指向actions 的该方法时,会执行所有与指定action名相同的函数(即:这里module A,B 中该action都会执行) udpateNameByAction({commit}, appendStr){ commit("updateNameByMutation", appendStr) } }, getters: { getNameB(state){ return state.name } } } const storeInstall = new Vuex.Store({ state: { name: 'i am root state name' }, modules:{ // 这里的路径名: test1, test2, 在view 中 通过 mapActions('test1', [actionName]) 使用并区分需要使用的module test1, test2 } }) export default storeInstallstore.js 几个简单的vuex 使用场景模拟。 我们有多个模块,分别为: test1, test2… 。
我们发现开发中可能会存在相同的stateName/ actionName/ mutaionName /。 (实际开发中,getterName 如果有重名编译会提示 getter 重名....) 我们使用vuex 需要实例化一个Vuex的Store构造函数。 这里storeInstall 中第一个state, 我们可以理解为根 state, 它全局可访问。 modules 中则是我们自定义注册的module. 每个module 中都有自己独立的state, action, mutation, getter... 需要注意的是,这里通过给每个module 对象添加namespaced: true, 来达到命名空间来区分Module的效果。也是通过它来区分更新/调用 对应的vuex 方法来隔离未知数据更新等数据相关问题vue组件
<template> <div> <div> <h2>Page Test1</h2> </div> <div> <a href="javascript:" @click="changeName">udpate: 名称Name</a> <a href="javascript:" @click="showName">显示更新后的Name</a> </div> </div> </template> <script> import { mapState, mapActions } from 'vuex' export default { data(){ return {} }, computed: { ...mapState('test1', { state: state => state }) }, methods: { // test1 模块路径名 ...mapActions('test1', [ 'udpateNameByAction' ]), changeName(){ this["udpateNameByAction"]('ha ha test1 udpate !!') }, showName(){ console.log(this.$store.state) }, }, mounted() { console.log("store name: ", this.$store) console.log("namespace test1 state: ", this.state) } } </script>关于vuex module 这里只是个基本讲解。 总结下来就是module 给了我们一种隔离vuex store 各个 state及相关api 的方法,让数据相关操作在复杂的项目场景可以更清晰,易追踪。