本文目录
  1. vuex简介
  2. 工作原理图
  3. 环境搭建
  4. vuex入门案例
  5. state
    1. this.$store.state.变量
    2. mapState访问
  6. getters
    1. 通过属性访问
    2. 通过方法访问
    3. mapGetters访问
  7. mutations
    1. 不带参数和带参数
    2. 对象风格提交
    3. mapMutations
  8. actions
    1. 分发action
    2. mapActions
    3. 组合action
  9. modules
  10. vuex 持久化

分类: vue | 标签: vue vuex

vuex笔记

发表于: 2022-07-06 16:47:28 | 字数统计: 4k | 阅读时长预计: 19分钟

vuex官网:https://v3.vuex.vuejs.org/zh/

注意版本对应

vue2要配合vuex3

vue3要配合vuex4

vuex简介

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。(说白了就是用来共享数据,可以跨组件全局共享,类似后端的session

image-20220706094034605

工作原理图

image-20220706094215433

两种执行流程:

第一种(异步):vue组件—>actions—>mutations—>state

第二种:vue组件—>mutations—>state

环境搭建

使用vue cli脚手架创建即可

vuex入门案例

实现点击按钮+1的案例

image-20220706100923900

  • views/Count.vue 组件
<template>
  <div>
    <h1>Count组件</h1>
    <button @click="addToVuex">vuex的count+1</button>
      <!-- 模板中的this可以省略 -->
    <div>vuex中的count:{{$store.state.count}}</div>
  </div>
</template>

<script>
export default {
  name: 'Count',
  methods: {
    addToVuex () {
      this.$store.commit('add')
    }
  }
}
</script>
  • store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  getters: {
  },
  mutations: {
    add (state) {
      state.count++
    }
  },
  actions: {
  },
  modules: {
  }
})

总结:

1>在store.js的state中设置需要共享的数据

2>在组件中通过 this.$store.state.变量名 来获取数据

3>在store.js的mutations中新增改变数据方法add

4>组件中通过 this.$store.commit(‘mutations中的方法’)来改变数据

数据存在state中,通过提交 mutation 的方式,而非直接改变 store.state.count,是因为能够更明确地追踪到状态的变化

state

Vuex 使用单一状态树,用一个对象就包含了全部的应用层级状态。这也意味着,每个应用将仅仅包含一个 store 实例。所以可以通过store来获取state中的数据

案例使用两种方式获取state中的数据

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 2,
    id: 111
  }
  // ...
})

this.$store.state.变量

<template>
  <div>
    <h1>Count组件</h1>
    <!-- 模板中的this可以省略 下面两种其实是一样的-->
    <div>第一种vuex中的count:{{$store.state.count}}</div>
    <div>第二种vuex中的count:{{count}}</div>
  </div>
</template>

<script>
export default {
  name: 'Count',
  computed: {
    // 利用计算属性
    count () {
      return this.$store.state.count
    }
  }
}
</script>

mapState访问

<template>
  <div>
    <h1>Count组件</h1>
    <!-- 模板中的this可以省略 -->
    <div>vuex中的count:{{myCount+',id:'+myId}}</div>
     <div>vuex中的count:{{count+',id:'+id}}</div>
  </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
  name: 'Count',
  computed: {
    // myCount为自定义名称, count为vuex中state中的名称
    ...mapState({ myCount: 'count', myId: 'id' }),
    // 当映射的计算属性的名称与 state 的子节点名称相同时,可以给 mapState 传一个字符串数组。
    ...mapState(['count', 'id'])
  }
}
</script>

getters

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

通过属性访问

  • 定义store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '111', done: true },
      { id: 2, text: '222', done: false },
      { id: 3, text: '333', done: true }
    ]
  },
  getters: {
    // 接收一个参数
    // doneTodos(state){
    //   return state.todos.filter(v => v.done)
    // },
    doneTodos: state => state.todos.filter(v => v.done),
    // 接收两个参数
    doneTodosCount: (state, getters) => getters.doneTodos.length
  }
  // ...
})
  • 组件中访问
<template>
  <div>
    <h1>demo组件</h1>
    <div>id: {{$store.getters.doneTodos[0].id}}</div>
    <div>text: {{$store.getters.doneTodos[0].text}}</div>
    <div>done: {{$store.getters.doneTodos[0].done}}</div>
    <div>doneTodosCount: {{$store.getters.doneTodosCount}}</div>
  </div>
</template>

通过方法访问

通过让 getter 返回一个函数,来实现给 getter 传参

定义store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '111', done: true },
      { id: 2, text: '222', done: false },
      { id: 3, text: '333', done: true }
    ]
  },
  getters: {
    // 让 getter 返回一个函数,来实现给 getter 传参
    getTodoById: state => id => state.todos.find(v => v.id === id)
  }
   // ...
})

组件中 可以传参

<div>getTodoById: {{$store.getters.getTodoById(3).text}}</div>

mapGetters访问

mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性

和前面的mapState类似

定义store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '111', done: true },
      { id: 2, text: '222', done: false },
      { id: 3, text: '333', done: true }
    ]
  },
  getters: {
    // 接收一个参数
    // doneTodos(state){
    //   return state.todos.filter(v => v.done)
    // },
    doneTodos: state => state.todos.filter(v => v.done),
    // 接收两个参数
    doneTodosCount: (state, getters) => getters.doneTodos.length,
    // 让 getter 返回一个函数,来实现给 getter 传参
    getTodoById: state => id => state.todos.find(v => v.id === id)
  }
})

组件中

<template>
  <div>
    <h1>demo组件</h1>
    <div>id: {{myDoneTodos[0].id}}</div>
    <div>text: {{myDoneTodos[0].text}}</div>
    <div>done: {{myDoneTodos[0].done}}</div>
    <div>doneTodosCount: {{doneTodosCount}}</div>
    <div>getTodoById: {{getTodoById(3).text}}</div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
export default {
  name: 'Count',
  computed: {
    ...mapGetters(['doneTodosCount', 'getTodoById']),
    ...mapGetters({ myDoneTodos: 'doneTodos' })
  }
}
</script>

mutations

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。mutation 必须是同步函数

不带参数和带参数

参数也叫载荷

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add (state) {
      state.count++
    },
    // 参数也可以是对象、字符串、数组等
    addN (state, p) {
      state.count += p
    }
  }
})

组件中 通过 store.commit 方法来调用mutations中的方法

<template>
  <div>
    <h1>demo组件</h1>
    <div>vuex中的count:{{count}}</div>
    <button @click="$store.commit('add')">点击让 vuex 的count +1</button>
    <button @click="$store.commit('addN',10)">点击让 vuex 的count +10</button>
  </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
  name: 'Count',
  computed: {
    ...mapState(['count'])
  }
}
</script>

对象风格提交

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    addNObj (state, p) {
      //p就是commit提交过来的对象 {type: 'addNObj', n: 10}
      state.count += p.n
    }
  }
    //...
})

组件中

<template>
  <div>
    <h1>demo组件</h1>
    <div>vuex中的count:{{count}}</div>
    <button @click="addNObj">点击让 vuex 的count +10</button>
  </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
  name: 'Count',
  computed: {
    ...mapState(['count'])
  },
  methods: {
    addNObj () {
        //此处提交一个对象参数,而不是像以前那种传两个参数
        //type对应mutation中的函数,n代表传来的参数,参数可以是多个
      this.$store.commit({
        type: 'addNObj',
        n: 10
      })
    }
  }
}
</script>

mapMutations

使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用

组件

<template>
  <div>
    <h1>demo组件</h1>
    <div>vuex中的count:{{count}}</div>
    <button @click="add">点击让 vuex 的count +1</button>
    <button @click="myAdd">点击让 vuex 的count +1</button>
    <button @click="addN(10)">点击让 vuex 的count +10</button>
    <button @click="addNObj({n: 10})">点击让 vuex 的count +10(提交对象)</button>
  </div>
</template>

<script>
import { mapMutations, mapState } from 'vuex'
export default {
  name: 'Count',
  computed: {
    ...mapState(['count'])
  },
  methods: {
      //将 this.add() 映射为 this.$store.commit('add') ...
    ...mapMutations(['add', 'addN', 'addNObj']),
    ...mapMutations({ myAdd: 'add' })
  }
}
</script>

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add (state) {
      state.count++
    },
    addN (state, p) {
      state.count += p
    },
    addNObj (state, p) {
      console.log(p)
      state.count += p.n
    }
  }
    //...
})

actions

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

分发action

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.statecontext.getters 来获取 state 和 getters。

组件中可以 this.$store.dispatch(‘xxx’) 来分发

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add (state) {
      state.count++
    },
    addN (state, p) {
      state.count += p
    },
    addNObj (state, p) {
      console.log(p)
      state.count += p.n
    }
  },
  actions: {
    // addTimeout (context) {
    addTimeout ({ commit }) {
      // 执行异步操作
      setTimeout(() => {
        commit('add')
      }, 1000)
    },     
    addNTimeout ({ commit }, p) { //普通传参
      console.log(p)
      setTimeout(() => {// 执行异步操作
        commit('addN', p)
      }, 1000)
    },
    addNObjTimeout ({ commit }, p) {//对象风格传参
      console.log(p)
      setTimeout(() => {
        commit('addNObj', p)
      }, 1000)
    }
  }
})

组件 中 通过 this.$store.dispatch 方法触发:

<template>
  <div>
    <h1>demo组件</h1>
    <div>vuex中的count:{{count}}</div>
    <button @click="$store.dispatch('addTimeout')">点击等1秒 让 vuex 的count +1</button>
    <button @click="$store.dispatch('addNTimeout',10)">点击等1秒(载荷) 让 vuex 的count +10</button>
     <button @click="$store.dispatch({
      type:'addNObjTimeout',
      n:10
     })">点击等1秒(对象风格) 让 vuex 的count +10</button>
  </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
  name: 'Count',
  computed: {
    ...mapState(['count'])
  }
}
</script>

mapActions

使用mapActions简化上面的例子,将组件的代码改为如下

<template>
  <div>
    <h1>demo组件</h1>
    <div>vuex中的count:{{count}}</div>
    <button @click="addTimeout">点击等1秒 让 vuex 的count +1</button>
    <button @click="addNTimeout(10)">点击等1秒(载荷) 让 vuex 的count +10</button>
    <button @click="myAddNObjTimeout({n:10})">点击等1秒(对象) 让 vuex 的count +10</button>
  </div>
</template>

<script>
import { mapState, mapActions } from 'vuex'
export default {
  name: 'Count',
  computed: {
    ...mapState(['count'])
  },
  methods: {
    //将 this.addTimeout() 映射为 this.$store.dispatch('addTimeout') ...
    ...mapActions(['addTimeout', 'addNTimeout']),
    ...mapActions({ myAddNObjTimeout: 'addNObjTimeout' })
  }
}
</script>

组合action

store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise。(一个action去调用另外一个action)

组件

<template>
  <div>
    <h1>demo组件</h1>
    <div>vuex中的count:{{count}}</div>
    <button @click="addNTimeout">点击1秒后cout+1,再过1秒后count+10</button>
  </div>
</template>

<script>
import { mapState, mapActions } from 'vuex'
export default {
  name: 'Count',
  computed: {
    ...mapState(['count'])
  },
  methods: {
    ...mapActions(['addNTimeout'])
  }
}
</script>

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add (state) {
      state.count++
    },
    addN (state, p) {
      state.count += p
    }
  },
  actions: {
    // addTimeout (context) {
    addTimeout ({ commit }) {
      return new Promise((resolve, reject) => {
        // 执行异步操作
        setTimeout(() => {
          commit('add')
          resolve()
        }, 1000)
      })
    },
   /* addNTimeout ({ commit, dispatch }) {
      // 将addTimeout这个action组合进来
      dispatch('addTimeout').then(() => {
        // 执行异步操作
        setTimeout(() => {
          commit('addN', 10)
        }, 1000)
      })
    }*/
      //用es6的async/await来优化
    async addNTimeout ({ commit, dispatch }) {
      // 将addTimeout这个action组合进来
      await dispatch('addTimeout')
      // 执行异步操作
      console.log('1010101010')
      setTimeout(() => {
        commit('addN', 10)
      }, 1000)
    }
  }
})

modules

当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割

默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。

模块化语法总结:

第一: $store.commit和$store.dispatch 第一个参数为【模块名/方法命名】
例如:
$store.commit('moduleA/addN',10)
$store.dispatch('moduleA/addNTimeout',10)

第二:...mapXXX 第一个参数为模块名字符串,第二个参数和以前的写法一致
例如:
...mapState('moduleA', ['count']),
...mapState('moduleA', { moduleAcount: 'count' }),

第三: 
...mapState和...mapGetters写在计算属性(computed)
...mapMutations和...mapActions写在方法里(methods)

【模块化案例】

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

// 当然也可以把moduleA单独放入到一个js文件中
const moduleA = {
  namespaced: true,//开启命名空间
  state: {
    count: 123
  },
  getters: {
    getX2 (state) {
      return state.count * 2
    }
  },
  mutations: {
    addN (state, p) {
      state.count += p
    }
  },
  actions: {
    addNTimeout ({ commit }, p) {
      setTimeout(() => {
        commit('addN', p)
      }, 1000)
    }
  }
}
//moduleB ...
//modulec ...
export default new Vuex.Store({
  modules: {
    moduleA
      //moduleB ...
    //modulec ...
  }
})

组件中使用

<template>
  <div>
    <h1>模块中state的使用</h1>
    <div>moduleA的count:{{ $store.state.moduleA.count }}</div>
    <div>moduleA的count:{{ count }}</div>
    <div>moduleA的count:{{ moduleAcount }}</div>
    <h1>模块中gettters的使用</h1>
    <div>moduleA的getters:{{ $store.getters['moduleA/getX2'] }}</div>
    <div>moduleA的getters:{{ getX2 }}</div>
    <div>moduleA的getters:{{ moduleAGetX2 }}</div>
     <h1>模块中mutations的使用</h1>
    <div><button @click="$store.commit('moduleA/addN',10)">moduleA的mutations+10</button></div>
    <div><button @click="addN(10)">moduleA的mutations+10</button></div>
    <div><button @click="moduleAAddN(10)">moduleA的mutations+10</button></div>
     <h1>模块中actions的使用</h1>
    <div><button @click="$store.dispatch('moduleA/addNTimeout',10)">moduleA的actions+10</button></div>
     <div><button @click="addNTimeout(10)">moduleA的actions+10</button></div>
     <div><button @click="moduleAAddNTimeout(10)">moduleA的actions+10</button></div>
  </div>
</template>

<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
  name: 'Count',
  computed: {
    ...mapState('moduleA', ['count']),
    ...mapState('moduleA', { moduleAcount: 'count' }),
    ...mapGetters('moduleA', ['getX2']),
    ...mapGetters('moduleA', { moduleAGetX2: 'getX2' })
  },
  methods: {
    ...mapMutations('moduleA', ['addN']),
    ...mapMutations('moduleA', { moduleAAddN: 'addN' }),
    ...mapActions('moduleA', ['addNTimeout']),
    ...mapActions('moduleA', { moduleAAddNTimeout: 'addNTimeout' })
  }
}
</script>

vuex 持久化

vuex中数据页面刷新之后会丢失,所以可以配合cookie或localStorage来实现持久化。

这里可以使用第三方的插件vuex-persistedstate来实现

  • 安装
npm install vuex-persistedstate --save
  • 使用
import createPersistedState from "vuex-persistedstate"
const store =newVuex.Store({
 state: {},
 mutations: {},
 actions: {},
 plugins: [createPersistedState()] //使用vuex-persistedstate将数据存储到localStorage
})

参考:Vuex数据状态持久化-vuex-persistedstate

------ 本文结束,感谢您的阅读 ------
本文作者: 程序员青阳
版权声明: 本文采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。