vuex 入门详解

 

在SPA单页面组件的开发中 Vue 的 vuex 和 React 的 Redux 都统称为同一状态管理,个人的理解是全局状态管理更合适;简单的理解就是你在 state 中定义了一个数据之后,你可以在所在项目中的任何一个组件里进行获取、进行修改,并且你的修改可以得到全局的响应变更。下面咱们一步一步地剖析下vuex的使用:

(1)首先要安装、使用 vuex

注意: 在vue 2.0+ 你的vue-cli项目中安装 vuex 

npm install vuex --save

(2)在src文件目录下新建一个名为 store 的文件夹,为方便引入并在 store 文件夹里新建一个index.js,里面的内容如下:

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

Vue.use(Vuex);

// 创建 store 实例
export default new Vuex.Store()

(3)在 main.js里面 引入 store ,然后把 【store 的实例】注入所有的子组件,相当于全局注入,这样一来就可以在任何一个组件里面使用 this.$store 了:

import store from './store'

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

(4)在 store 文件夹下面新建一个 modules 文件夹,然后在modules 文件里面建立需要管理状态的 js 文件,既然要把不同部分的状态分开管理,那就要把它们给分成独立的状态文件了,

import account from './modules/account'

export default new Vuex.Store({
  modules: {
    account,
  }
})

(5)在 store/modules.account.js 中,设置全局访问的 state 对象,要设置初始属性值,通过 db.get 方法获取缓存信息。

state: {
  token: db.get('USER_TOKEN'),
  user: db.get('USER'),
},

(6)通过上面步骤后,你已经可以用 this.$store.state.token 或 this.$store.state.user 在任何一个组件里面获取 token 和 user 定义的值了。

computed: {
  token () {
    return this.$store.state.account.token
  },
  user () {
    return this.$store.state.account.user
  }
},

(7)mutattions也是一个对象,这个对象里面可以放改变 state 的初始值的方法,具体的用法就是给里面的方法传入参数 state 或额外的参数,然后利用 vue 的双向数据驱动进行值的改变,同时 db.save 方法更新缓存信息,如下:

mutations: {
  setToken (state, val) {
    db.save('USER_TOKEN', val)
    state.token = val
  },
  setUser (state, val) {
    db.save('USER', val)
    state.user = val
  },
}

(8)其中的 namespaced:true 表示当你需要在别的文件里面使用( mapGetters、mapActions )时,里面的方法需要注明来自哪一个模块的方法:

import db from 'utils/localstorage'

export default {
  namespaced: true,
  state: {
    ...
  },
  mutations: {
    ...
  }
}

(9)在组件中,如何获取 store 中的多个数据信息呢?当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。mapState 函数返回的是一个对象。通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。

import {mapState} from 'vuex'

computed: {
  // 对象形式
  ...mapState({
    token: state => state.account.token,
    username: state => state.account.user.username,
  }),
  // 数组形式
  ...mapState([
    'token',
    'username'
  ])
},

(10)Getter 接受 state 作为其第一个参数,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:

import { mapGetters } from 'vuex'

export default {
  computed: {
  // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([ 'doneTodosCount', 'anotherGetter' ])
    mapGetters({
      // 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
      doneCount: 'doneTodosCount'
    })
  }
}

(11)在组件里面,如何给 store 赋值呢?你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。这里将把登录接口返回的数据存储到 Vuex 和浏览器的 localstorage 中。

import { mapMutations } from 'vuex';

methods: {
  // store.commit 提交 mutation 
  onColorChange (values, colors) {
    this.$store.commit('setting/setColor', colors)
  },
  // mapMutations 映射为 store.commit 调用
  ...mapMutations({
    setToken: 'account/setToken',
    setUser: 'account/setUser',
  }),
  saveLoginData (data) {
    this.setToken(data.token)
    this.setUser(data.user)
  },
}

(12)在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):

import { mapActions } from 'vuex'

export default {
  methods: {
    updateToken() {
      this.$store.dispatch('account/setToken', this.token); // account.js里actions里的setToken方法
    },
    ...mapActions([
      'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`

      // `mapActions` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
    ]),
    ...mapActions({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
    })
  }

登录流程小案例:

(1)登录点击事件:

// 登录操作
async handleSubmit (e) {
  e.preventDefault()
  const result = await this.$store.dispatch('Login', this.form)
},

(2)在 src/store/modules/passport.js 文件里,actions 中,添加接口请求,将结果 userInfo 使用 commit 提交 mutation,数据存储到 Vuex 的 state  中。

const passport = {
  state: {
    info: {},
  },

  mutations: {
    SET_INFO: (state, info) => {
      state.info = info
    },
  }

  actions: {
    // 登陆
    async Login ({ commit }, userInfo) {
      const { username, password } = userInfo
      const resp = await api.login(username, password)
      const { tokenInfo, userInfo } = resp.data
      ...
      commit('SET_INFO', userInfo)
    }
  }
}
export default passport

(3)如何全局获取数据呢?

import { mapState } from 'vuex'

computed: {
  ...mapState: ({
    userInfo: state => state.passport.info
  })
}

mounted () {
  console.log('mounted', this.userInfo)  // 获取到存储在 Vuex 全局的数据状态
}

当然如果我们的登录是弹框的样子,有些路由未登录时,我们会检查登录态,详情请参考:使用 vue-router 全局守卫钩子函数,根据登录状态进行路由拦截以及滚动条回到页面顶部。当判断为未登录状态时,我们需要弹出登录框提示用户登录后,便可以操作。这是我们可以使用 vuex 全局状态管理来控制登录弹框的显示或隐藏。

import { mapMutations } from 'vuex'
methods: {
  ...mapMutations([
    'setLoginVisible',
  ]),
  // 登录点击事件
  toLogin () {
    // 或者使用 this.$store.commit('setLoginVisible', true)
    this.setLoginVisible(true)
  },
}

store 中如何配置呢?

// src/store/modules/user.js
const user = {
  state: {
    loginVisible: false, // 登录弹窗显示
  },
  mutations: {
    setLoginVisible: (state, loginVisible) => {
      state.loginVisible = loginVisible
    },
  }
}
export default user

当然登录组件挂载在主体架构下面的:

// 登录


export default {
  name: 'Framework',
  components: {
    Login,
  },
  computed: {
    ...mapState({
      loginVisible: state => state.user.loginVisible,
    })
  },
}

那么我们如何设置 Login 组件呢?

// src/views/User/Login/Login.vue

  ...

登录提交事件:

import { mapActions } from 'vuex'
...mapActions(['Login']),

async handleSubmit (e) {
  e.preventDefault()
  ......
  // 或者使用 await this.$store.dispatch('Login', this.form)
  await this.Login(this.form)
  this.setLoginVisible(false)
}

store 中 action 是异步函数,可以调用接口信息

// src/store/modules/user.js 
import api from '@/http/api/user'
const user = {  
  actions: {
    async Login ({ commit }, userInfo) {
      const { username, password, marketId } = userInfo
      try {
        const resp = await api.login({
          username,
          password,
        })
        const { tokenInfo, userInfo } = resp.data
      } catch (e) {
        throw e
      }
    }
  }
}

当然获取 vuex 中数据状态管理如下:

import { mapState } from 'vuex'
computed: {
  ...mapState({
    userInfo: state => state.user.userInfo || {},
  })
}

// 或者使用 const userInfo = this.$store.state.user.userInfo || {}

为啥我们可以使用 this.$store 来获取存储的全局数据信息呢?

在实例化挂载 Vue 节点时,引入 store 的相关文件信息。

import store from './store'
new Vue({
  router,
  store,
  mounted () {},
  render: h => h(App)
}).$mount('#app')

总结:

vuex分为state,getter,mutation,action四个模块,四个模块的作用如下:

(1)state:定义变量;

(2)getters:获取变量;

(3)mutations:同步执行对变量进行的操作;

(4)actions:异步执行对变量进行的操作;

(5)mapMutations/mapActions 只是把 mutation/action 函数绑定到methods里面,调里面的方法时正常传参数。

注意:映射都是映射到当前对象,使用时需要用 this 来调用。更多详细信息,请查看 Vuex 官网,参考案例:购物车

你可能感兴趣的:(vuex,vuex,state,getter,mutation,action)