Vuex とは何か?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能

核心概念

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state:() => ({
     // 定义属性
  }),
  getters: {
     // store 的计算属性,类似于computed()   
  },
  mutations: {
     // commit
     // 更改 store 中的状态,唯一方法是提交 mutation
  },
  actions: {
     // dispatch
     // action 提交的是 mutation,可以包含任意异步操作    
  },
  modules: {
     // store 分割成模块(module)。
     // 每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
  }
})

import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  store,
  render: function (h) { return h(App) }
}).$mount('#app')

State

// 首先我们在store仓库中定义一个状态
export default new Vuex.Store({
  state:() => ({
      counter: 10
  }),
  // ...
}

两个页面共享store仓库中的状态,所以显示的数据一致

<!-- 组件1 -->
<template>
  <div id="component1">
      <h1>{{$store.state.counter}}</h1>  <!-- 10 -->
  </div>
</template>

<!-- 组件2 -->
<template>
  <div id="component2">
      <h1>{{$store.state.counter}}</h1>  <!-- 10 -->
  </div>
</template>

Getters

有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数:

export default new Vuex.Store({
  state:() => ({
    books: [
     {
      id: 1001,
      title: 'Mysql',
      price: 108,  
      totalNumber: 2000
     },
     {
      id: 1002,
      title: 'C++',
      price: 66,
      totalNumber: 3680  
     },
     {
      id: 1003,
      title: 'JavaScript',
      price: 99,
      totalNumber: 980  
     }
    ]
  }),
  getters: {
    filterPrice(state) {
      return state.books.filter(book => { book.price > 100 }),
    },
    sumPrice(state, getters) {
      // Getter 也可以接受其他 getter 作为第二个参数
      const bookList =getters.filterPrice
      return bookList.reduce((accumulator,currentValue )=> accumulator + currentValue.price, 0);			
    }
  },
  // ...
}

this.$store.getters 直接可以访问store中的Getters属性

<!-- 组件1 -->
<template>
  <div id="component1">
      <h1>{{$store.getters.filterPrice}}</h1>  <!-- '{id: 1001,title: 'Mysql', price: 108, totalNumber: 2000}' -->
      <h1>{{$store.getters.sumPrice}}</h1>  <!-- 108  -->            
  </div>
</template>

<!-- 组件2 -->
<template>
  <div id="component2">
      <h1>{{$store.getters.filterPrice}}</h1>  <!-- '{id: 1001,title: 'Mysql', price: 108, totalNumber: 2000}' -->
      <h1>{{$store.getters.sumPrice}}</h1>  <!-- 108  --> 
  </div>
</template>

Mutations

export default new Vuex.Store({
  state:() => ({
    softwareExperts: [{
      name: 'James Gosling',
      gender: '男',
      countryCit: 'Canada',
      birthday: '1955-05-19'
    }]
  }),
  getters: {    
   getLength(state) {
      return store.softwareExperts.length
   }
  },
  mutations: {
    inviteAnExperts(store, payload) {
      store.softwareExperts.push( payload.info )
    }
  }, 
  // ...
}

点击Button,提交 mutations 更新state.softwareExperts的状态,component1,component2同时渲染,并且是响应式的

<!-- 组件1 -->
<template>
  <div id="component1">
      <h1>{{$store.getters.getLength}}</h1>   <!-- 1 ------> @click ------ 2 -->
      <button @click= "invite"> 邀请一位软件专家 </button>              
  </div>
</template>

<!-- 组件2 -->
<template>
  <div id="component2">
      <h1>{{$store.getters.getLength}}</h1>  <!-- 1 ------> @click ------ 2 -->
  </div> 
</template>
export default {
 // ...
 methods: {
   invite() {
     const info = {
        name: 'Bill Gates',
        gender: '男',
        countryCit: 'United States',
        birthday: '1955-10-28'
     }
     this.$store.commit({
        type: 'inviteAnExperts',
        info
     })
   },
   // ...
 }

Actions

const store = new Vuex.Store({
  state:() => ({
    count: 0
  }),
  mutations: {
    increment(state) {
      state.count++
    }
  },
  actions: {
    //异步
    incrementAsync(context) {
      setTimeout(() => {        
        context.commit('increment')
      }, 1000)
    }
    // context 解构  { ... dispatch, commit, state ... }
  },
})

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

<!-- 组件1 -->
<template>
  <div id="component1">
      <h1>{{$store.state.count}}</h1>   <!-- 0 ------> @click ------ 1 -->
      <button @click= "accumulate"> count++ </button>              
  </div>
</template>

<!-- 组件2 -->
<template>
  <div id="component2">
      <h1>{{$store.state.count}}</h1>  <!-- 0 ------> @click ------ 1 -->
  </div> 
</template>
export default {
 // ...
 methods: {
   accumulate() {    
      this.$store.dispatch('incrementAsync')
   },
   // ...
 }

Modules

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

模块的局部状态

const moduleA = {
  state: () => ({
    count: 0
  }),
  getters: {
    doubleCount (state) {
      return state.count * 2
    },
    sumWithRootCount (state, getters, rootState) {
      // rootState: 根仓库store
      return state.count + rootState.count
    }
  },
  mutations: {
    increment (state) {
      // 这里的 `state` 对象是模块的局部状态 {}
      state.count++
    }
  },
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      // rootState: 根仓库store
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}

const store = new Vuex.Store({
  state: () => ({
      count: 10
  }),
  modules: {
    a: moduleA
  }
})
<template>
  <div id="component1">
      <h1>{{$store.state.a.getters.sumWithRootCount}}</h1>   <!-- 10 -->             
  </div>
</template>
export default {
  methods: {
    // ...
    submitModuleA() {
      // 向moduleA 提交mutations、actions 和 根store提交方式一样 
      this.$store.commit('increment')
      this.$store.dispatch('incrementIfOddOnRootSum ')      
    }    
  }
}

进阶

🚦 Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
🛑 应用层级的状态应该集中到单个 store 对象中。
🛑 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
🛑 异步逻辑都应该封装到 action 里面。
🛑 如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
🛑 对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:

项目结构

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── getters.js        # 根级别的 getters
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块
import Vue from 'vue'
import Vuex from 'vuex'
import {getters} from './store/getters'
import {mutations} from './store/mutations'
import {actions} from './store/actions'
import {actions} from './store/actions'
import {cart} from './store/modules/cart'
import {products} from './store/modules/products'
Vue.use(Vuex)

const state = {
 // ...
}

export default new Vuex.Store({
  state,
  getters,
  mutations,
  actions,
  modules: {
    cart,
    products
  }
})

灵活使用

// 使用常量替代 Mutation 事件类型
export const SOME_MUTATION = 'SOME_MUTATION'
export const SOME_ACTION = 'SOME_ACTIO'
import { SOME_MUTATION } from './mutation-types'
expotr default {
 // ...
 methods: {
   submitMutations() {
      this.$store.commit( SOME_MUTATION )
   },
   submitActions() {
      this.$store.dispatch( SOME_ACTIVE).then(res => {console.log(res)})
   }
 }
}
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
import { SOME_ACTION } from './ACTION-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
    [SOME_MUTATION](state) {
        // mutate state

      })
    }
  },
  actions: {
    [SOME_ACTION]({ commit }) {
       return new Promise((resolve, reject) => {
           commit(SOME_MUTATION)
           reslove(...)
       })
    }
  }
})
posted @ 2020-10-27 09:54  BingNiTer  阅读(184)  评论(1编辑  收藏  举报