vuex 状态管理

314 阅读4分钟

1.定义

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化,并提供devtools extenions 插件工具,支持时间漫游调试,状态快照,导入导出等。

  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入导致的状态变化。 image.png

image.png

2.安装

vue add vuex

3.使用

直接使用matution操作state

<script>
//1.1直接定义
//src/store/index.js
export default new Vuex.Store({
   state: { isLogin: false },
   mutations: {
       login(state) {
          state.isLogin = true;
       },
       logout(state) {
          state.isLogin = false;
       },
  },
 })

//1.2模块化定义
//src/store/user.js
export default {
  state: { isLogin: false },
  mutations: {
   login(state) {
      state.isLogin = true;
   },
   logout(state) {
      state.isLogin = false;
   },
  },
}
//src/store/index.js
import user from './user'
Vue.use(Vuex)
export default new Vuex.Store({
  modules: {
    user
  },
})

//2.使用
//main.js
import store from './store'
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
  
//login.vue调用
<template>
  <button @click="login" v-if="!$store.state.isLogin">登录</button>
  <button @click="logout" v-else>登出</button>
</template>

this.$store.commit('login') 
this.$store.commit('logout')

4.常用对象

4.1 State

const app = new Vue({
  el: '#app',
  // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
  store,
  components: { Counter },
  template: `
    <div class="app">
      <counter></counter>
    </div>
  `
})
//子组件可以直接this访问
this.$store.state.count

4.2 mapState

优点:可以简化定义了命名空间的长访问,如this.$store.user.xxx 定义:默认返回键值堆的方式

data () {
    return {
      username: '张三'
    }
  },
//方法1 使用mapState 
//缺点占用了整个computed标签,不得不修改原来的computed 外层
computed: mapState({
    count: 'count', // 第一种写法
    sex: (state) => state.count, // 第二种写法
    from: function (state) { // 用普通函数this指向vue实例,要注意
      return this.username + ':' + state.count
    },
  }), 
//方法2 使用...mapState //解构使得computed:保留原来外层解构
//使用之前
computed:{
    fn1(){ return ...},
    fn2(){ return ...},
    fn3(){ return ...}
}
//使用之后,外层结构依然不变
computed:{
    //原来的继续保留
    fn1(){ return ...},
    fn2(){ return ...},
    fn3(){ return ...}
    //再维护vuex
    ...mapState({  //通过扩展符 混入到当前computed的对象
        count:'count'
    })
 }

4.3Mutation

修改state只能通过mutation, mutation必须是同步函数

//定义
mutations: {
    login(state) {
      state.isLogin = true;
    },
    logout(state) {
      state.isLogin = false;
    },
  },
//调用
this.$store.commit('login') 
this.$store.commit('logout')

4.4Getter

可以使用getters从store的state中派生出一些状态,如果state发生变化会同步更新派生的状态,效果与computed相同

export default { 
    state: { 
        isLogin: false, 
        username: '' // 用户名 
    },
   getters: { //该属性的获取跟 state获取方式一样
       welcome: state => { return state.username + ',欢迎回来'; } 
   },

4.5Action

Action 类似于 mutation,不同在于:

  1. 提交的是 mutation,而不是直接变更状态。
  2. 可以包含任意异步操作。
export default { 
  state: {
    isLogin: false,
    username: ''
  },
  actions: {
      login({ commit }, username) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            if (username === "admin") {
              commit("login"); //通过comimt提交mutation
              resolve();
            } else {
              reject();
            }
          }, 1000);
        });
      },
  }

//通过dispatch派发action
 this.$store.dispatch("login", "admin")
      .then(() => {
        this.$router.push(this.$route.query.redirect);
      })
      .catch(() => {
        alert("用户名或密码错误");
      });

4.6mapMutation()/mapAction()

自动映射$store方法,避免对$store直接访问

 //mapMutations 使用方法一
...mapMutations([
  'login', // 将 `this.login()` 映射为 `this.$store.commit('login')`
]),
//mapMutations 使用方法二
...mapMutations({
    login: 'login'// 将 `this.login()` 映射为 `this.$store.commit('login')`
})

//action相关修改
import { mapActions } from 'vuex' 
//1.带有命名空间,缺点和computed重名就区分不了
methods: { 
    login() { 
        this['login']('admin').then(...) 
    }, 
    ...mapActions('user',['login']) //解构方式注入当前methods对象 
},

//1.直接命名,使用方法要使用this['xxx/xx']的方式
methods: { 
    login() { 
        this['user/login']('admin').then(...) 
    }, 
  ...mapActions(['user/login'])
},

4.6严格模式

严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有 的状态变更都能被调试工具跟踪到。开启严格模式 strict: true

export default new Vuex.Store({
  modules: {
    user
  },
  strict: true,
})

4.7 定义命名空间

//src/store/user.js
export default {
  namespaced: true, // 设置独立命名空间,避免命名冲突 使用的时候要加上user/xxxx
  ... 
  //定义
  mutations: {
    login(state) {
      state.isLogin = true;
    },
    logout(state) {
      state.isLogin = false;
    },
  },
}
//引入
import user from './user'
Vue.use(Vuex)
export default new Vuex.Store({
  modules: {
    user
  },
})
//调用
this.$store.state.user.isLogin
this.$store.commit('user/login') 
this.$store.commit('user/logout')

4.8插件

Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函 数,它接收 store 作为唯一参数: 需求:每次页面刷新,之前登陆的用户信息都能正常读取并设置到当前的$store信息里

const myPlugin = store => {
// 当 store 初始化后调用
}
注册插件:
const store = new Vuex.Store({
   // ... 
   plugins: [myPlugin],
});

//实现登录状态持久化,store/plugins/persist.js
export default (store) => {
      // 初始化时从localStorage获取数据
      if (localStorage) {
        const user = JSON.parse(localStorage.getItem("user"));
        if (user) {
          store.commit("user/login");
          store.commit("user/setUsername", user.username);
        }
      }

      // 用户状态发生变化时缓存之
      store.subscribe((mutation, state) => {
        if (mutation.type.startsWith("user/")) {
          localStorage.setItem("user", JSON.stringify(state.user));
        } else if (mutation.type === "user/logout") {
          localStorage.removeItem("user");
        }
      });
    };
}

总结

  1. vuex是一个vue专用的状态管理库。以全局的方式集中管理应用状态,并且可以保证数据变更的可预测性。
  2. vuex主要解决多个组件状态共享问题。虽然利用组件通讯方式如 onon emit parentparent chlid 等也能实现状态共享,但是容易出错,也使得程序变得复杂。vuex可以把所有组件的状态都抽离出来,统一在一个全局store里单例管理。在任何的组件都可以用一致的方法获取或修改内容。配合响应式数据,保证了单向数据流的流动。代码也便于维护。
  3. 当我们只是开发小应用可以不使用vuex,(使用根实例定义一个全局的变量管理都可以,或者使用eventbus也是可以),只有当项目大到一定规模,需要大量的全局状态管理才需要引入。
  4. vuex的使用过程:将全局状态放到state对象,使用stote访问他。使用mutation实现同步的修改,配合commit提交使用。使用action实现异步的修改,配合dispatch使用。action里面最终还是执行mutation方法。通过modules选择组织不同的子模块,访问的时候需要加上对应的namespace。
  5. vuex实现单向数据流需要做到数据响应式。在源码中利用的是vue的数据响应式特征实现。利用Vue将state作为data进行响应化处理,从而使得数据变化能够使组件重新渲染。通过约定mutation方法才能修改数据,实现可预测性,实现后期数据的拦截与时间漫游等等扩展功能。