Vuex 是一个为 Vue 应用程序开发的状态管理模式,它用集中式存储来管理应用全部组件的状态html
简单来讲,它的做用就是把全部组件的共享状态抽取出来,以一个全局单例的模式进行管理vue
咱们能够把 Vuex 理解成一个 store,里面存储着全部组件共享的 state(数据)和 mutations(操做)java
这里仍是先附上官方文档的连接:https://vuex.vuejs.org/zh/,有兴趣的朋友能够去看看vuex
(1)经过 CDN 引用shell
<script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script>
(2)经过 NPM 安装与使用npm
> npm install vuex
在项目中须要经过 Vue.use()
明确安装 Vuex缓存
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex)
Vuex 中的 state 用于集中存储数据,当咱们须要访问 state 时,能够先将其映射为计算属性app
因为 state 是响应式的,因此当 state 发生变化时,它会从新求取计算属性,并自动更新相应的 DOM异步
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-counter></my-counter> </div> <script> // 一、定义 store const store = new Vuex.Store({ state: { // 定义 state count: 0 } }) // 二、定义组件 const Counter = { template: ` <div> <p>{{ count }}</p> </div> `, // 若是须要访问 state,能够在计算属性中经过 this.$store.state 返回数据 computed: { count() { return this.$store.state.count } } } // 三、建立并挂载根实例 const app = new Vue({ store, // 注入 store components: { 'my-counter': Counter } }).$mount('#app') </script> </body> </html>
如今假设咱们须要在一个组件中使用多个 state,若是为每一个状态都写一条语句将其映射为计算属性未免太过繁琐
因此 Vuex 提供 mapState()
辅助函数可以帮助咱们完成这些工做
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-counter></my-counter> </div> <script> const store = new Vuex.Store({ state: { counter1: 0, counter2: 10, counter3: 100 } }) const Counter = { template: ` <div> <p>couter1: {{ counter1 }}</p> <p>couter2: {{ counter2 }}</p> <p>couter3: {{ counter3 }}</p> </div> `, data: function () { return { localCounter: 1 } }, computed: { // mapState() 返回一个对象,使用对象展开运算符将其混入 computed 对象 ...Vuex.mapState({ // 使用箭头函数,能够简化代码 counter1: state => state.counter1, // 使用字符串 'counter2',等价于 `state => state.counter2` counter2: 'counter2', // 使用常规函数,可以使用 `this` 以获取局部状态 counter3 (state) { return state.counter3 + this.localCounter } }) } } const app = new Vue({ store, components: { 'my-counter': Counter } }).$mount('#app') </script> </body> </html>
Vuex 中的 getter 用于管理派生出来的状态
它就至关于计算属性同样,会被缓存起来,当依赖发生改变时才会从新计算
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-todo></my-todo> </div> <script> const store = new Vuex.Store({ state: { todos: [ { id: 1, text: 'Say Hello', done: true}, { id: 2, text: 'Say Goodbye', done: false} ] }, getters: { // 定义 getters // 其接受 state 做为第一个参数 doneTodos: state => { return state.todos.filter(todo => todo.done) }, // 其接受 getters 做为第二个参数 doneTodosCount: (state, getters) => { return getters.doneTodos.length } } }) const Todo = { template: ` <div> <p>{{ doneTodosCount }} task(s) done</p> <ul><li v-for="item in doneTodos">{{ item.text }}</li></ul> </div> `, computed: { doneTodos () { return this.$store.getters.doneTodos }, doneTodosCount () { return this.$store.getters.doneTodosCount } } } const app = new Vue({ store, components: { 'my-todo': Todo } }).$mount('#app') </script> </body> </html>
和 state 同样,getters 也有一个名为 mapGetters()
的辅助函数将其映射为计算属性
computed: { ...mapGetters([ 'doneTodos', 'doneTodosCount' ]) }
若是要给 getter 重命名,能够用对象形式
computed: { ...mapGetters({ doneTodosAlias: 'doneTodos', doneTodosCountAlias: 'doneTodosCount' }) }
上面咱们讲了怎么访问 state,下面咱们来看看怎么修改 state,改变状态的惟一方法是提交 mutation
这里,请记住一条重要的规则:mutation 必须是同步函数
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-counter></my-counter> </div> <script> const store = new Vuex.Store({ state: { count: 0 }, mutations: { // 定义 mutations // 传入的第一个参数是 state increment(state) { state.count += 1 }, // 传入的第二个参数是 payload,能够提供额外的信息 incrementN(state, payload) { state.count += payload.amount } } }) const Counter = { template: ` <div> <p>{{ count }}</p> <button @click="increment"> 加 1 </button> <button @click="incrementN"> 加 10 </button> </div> `, // 若是须要访问 state,能够在计算属性中经过 store.state 返回数据 computed: { count() { return store.state.count } }, // 若是须要修改 state,能够经过 store.commit() 提交 mutation methods: { increment() { store.commit('increment') }, incrementN() { // 以对象的形式给 commit() 传入 payload store.commit('incrementN', { amount: 10 }) } } } const app = new Vue({ store, components: { 'my-counter': Counter } }).$mount('#app') </script> </body> </html>
固然,咱们也能够使用 mapMutations()
辅助函数将 mutation 映射为 methods
methods: { ...mapMutations([ 'increment', 'incrementN' ]) }
也一样能够使用对象形式支持重命名
methods: { ...mapMutations({ add: 'increment', addN: 'incrementN' }) }
还记得上面咱们说过 mutation 只能是同步函数,若须要使用异步操做,则能够经过分发 action
action 内部能够包含异步逻辑,它作的工做是提交 mutation,而不是直接改变状态
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-counter></my-counter> </div> <script> const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count += 1 }, incrementN(state, payload) { state.count += payload.amount } }, actions: { //定义 actions // 该方法接受一个与 store 实例具备相同属性和方法的对象做为参数 // 咱们能够调用 context.commit() 提交 mutation // 也能够经过 context.state 和 context.getters 访问 state 和 getters incrementAsync(context) { setTimeout(() => { context.commit('increment') }, 1000) }, // 和 mutations 同样,也能够传入第二个参数 payload incrementNAsync(context, payload) { setTimeout(() => { context.commit('incrementN', payload) }, 1000) } } }) const Counter = { template: ` <div> <p>{{ count }}</p> <button @click="increment"> 同步加 1 </button> <button @click="incrementN"> 同步加 10 </button> <button @click="incrementAsync"> 异步加 1 </button> <button @click="incrementNAsync"> 异步加 10 </button> </div> `, computed: { count() { return store.state.count } }, methods: { // 经过 store.commit() 提交 mutation increment() { store.commit('increment') }, incrementN() { // 以对象的形式给 commit() 传入 payload store.commit('incrementN', { amount: 10 }) }, // 经过 store.dispatch() 分发 action incrementAsync() { store.dispatch('incrementAsync') }, incrementNAsync() { // 以对象的形式给 dispatch() 传入 payload store.dispatch('incrementNAsync', { amount: 10 }) } } } const app = new Vue({ store, components: { 'my-counter': Counter } }).$mount('#app') </script> </body> </html>
若是须要处理更复杂的异步逻辑,咱们也能够使用 Promise 和 async/await
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-counter></my-counter> </div> <script> const store = new Vuex.Store({ state: { count: 0 }, mutations: { add(state) { state.count += 10 }, multiply(state) { state.count *= 10 } }, actions: { addAsync(context) { setTimeout(() => { context.commit('add') }, 1000) }, multiplyAsync(context) { setTimeout(() => { context.commit('multiply') }, 1000) }, // 只容许使用异步函数 addAsync 和 multiplyAsync,实现先乘后加 // 使用 async/await async multiplyBeforeAdd(context) { await context.dispatch('multiplyAsync') context.dispatch('addAsync') }, // 只容许使用异步函数 addAsync 和 multiplyAsync,实现先加后乘 // 使用 async/await async addBeforeMultiply(context) { await context.dispatch('addAsync') context.dispatch('multiplyAsync') } } }) const Counter = { template: ` <div> <p>{{ count }}</p> <button @click="done"> 乘10,加10,加10,乘10 </button> </div> `, computed: { count() { return store.state.count } }, methods: { // 先完成先乘后加,再完成先加后乘 done() { store.dispatch('multiplyBeforeAdd').then(() => { store.dispatch('addBeforeMultiply') }) } } } const app = new Vue({ store, components: { 'my-counter': Counter } }).$mount('#app') </script> </body> </html>
【 阅读更多 Vue 系列文章,请看 Vue学习笔记 】