Vuex进阶
1.插件
下面以一个对state进行持久化存储的插件为例进行介绍:
代码结构:

saveInLocal.js
export default function (store) { if (localStorage.state) { store.replaceState(JSON.parse(localStorage.state)); } store.subscribe((mutation, state) => { //在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cookie的存储空间为4k),localStorage中一般浏览器支持的是5M大小,这个在不同的浏览器中localStorage会有所不同。 localStorage.state = JSON.stringify(state); }); }
index.js
import Vue from 'vue' import Vuex from 'vuex' import state from "./state" import getters from './getters' import mutations from "./mutations" import actions from "./actions" import user from './module/user' import saveInLocal from './plugin/saveInLocal'; Vue.use(Vuex) export default new Vuex.Store({ state, getters, mutations, actions, modules: { user }, plugins:[saveInLocal] })
store.vue:
<template>
  <div>
    <a-input :value="inputValue" @input="handlerInput"></a-input>
    <p>{{ inputValue }} -> lastLetter is {{ inputValueLastLetter }}</p>
    <p>appName: {{ appName }}, appNameWithVersion : {{ appNameWithVersion }}</p>
    <p>userName : {{ userName }}, firstLetter is : {{ firstLetter }}</p>
    <button @click="handleChangeAppName">修改appName和user.js中的userName</button>
    <p>动态给state增加appVersion: {{ appVersion }}</p>
    <button @click="handleActionChangeAppName">通过Action修改appName</button>
    <button @click="registerModule">动态注册模块</button>
    <p v-for="(li, index) in todoList" :key="index">{{ li }}</p>
  </div>
</template>
<script>
import AInput from "_c/AInput.vue";
import AShow from "_c/AShow.vue";
//变量的解构赋值
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
import { stat } from "fs";
export default {
  name: "store",
  data() {
    return {
      inputValue: ""
    };
  },
  components: {
    AInput: AInput,
    AShow: AShow
  },
  computed: {
    //ES6展开操作符 mapState展开会形成一个对象 使用对象展开运算符将此对象混入到外部对象中
    ...mapState({
      appName: state => state.appName,
      appVersion: state => state.appVersion,
      userName: state => state.user.userName,
      todoList: state => (state.user.todo ? state.user.todo.todoList : [])
    }),
    // 使用对象展开运算符将 getter 混入 computed 对象中
    // ...mapGetters(["appNameWithVersion"]),
    appNameWithVersion() {
      //通过属性访问getters,Getter 会暴露为 store.getters 对象,可以以属性的形式访问这些值:
      return this.$store.getters.appNameWithVersion;
    },
    ...mapGetters(["firstLetter"]),
    inputValueLastLetter() {
      return this.inputValue.substr(-1, 1);
    }
  },
  methods: {
    handlerInput(val) {
      this.inputValue = val;
    },
    //
    ...mapMutations([
      "SET_USER_NAME", //将 `this.SET_USER_NAME()` 映射为 `this.$store.commit('SET_USER_NAME')`
      "SET_APP_NAME" //将 `this.SET_APP_NAME()` 映射为 `this.$store.commit('SET_APP_NAME')`
    ]),
    ...mapActions([
      "updateAppName" //将 `this.updateAppName()` 映射为 `this.$store.dispatch('updateAppName')`
    ]),
    handleChangeAppName() {
      this.SET_APP_NAME({
        appName: "newAppName"
      });
      this.SET_USER_NAME({
        userName: "shuyujie"
      });
      this.$store.commit("SET_APP_VERSION");
    },
    handleActionChangeAppName() {
      //第一种调用Action的方法
      //this.$store.dispatch('updateAppName')
      //第二种调用Action的方法
      this.updateAppName();
    },
    registerModule() {
      this.$store.registerModule(["user", "todo"], {
        state: {
          todoList: ["学习mutations", "学习actions"]
        }
      });
    }
  }
};
</script>
点击名称为:“修改appName和user.js中的userName”的按钮:
效果图:

2.严格模式
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
index.js代码:
import Vue from 'vue' import Vuex from 'vuex' import state from "./state" import getters from './getters' import mutations from "./mutations" import actions from "./actions" import user from './module/user' import saveInLocal from './plugin/saveInLocal'; Vue.use(Vuex) export default new Vuex.Store({ strict: process.env.NODE_ENV === 'development',//不要在发布环境下启用严格模式! state, getters, mutations, actions, modules: { user }, plugins:[saveInLocal] })
3.Vuex+双向数据绑定
当在严格模式中使用 Vuex 时,在属于 Vuex 的 state 上使用 v-model 会比较棘手,因为state只允许被mutation修改。
解决办法:使用带有 setter 的双向绑定计算属性
state.js:
const state = { appName: 'admin', stateValue: 'I am stateValue' } export default state
mutations.js:
import vue from 'vue' const mutations = { SET_APP_NAME(state, params) { //若params是对象格式 state.appName = params.appName; //若params是字符串格式 //state.appName = params; }, SET_APP_VERSION(state) { vue.set(state, 'appVersion', 'v100.0') //state.appVersion = 'v2.0' }, SET_STATE_VALUE (state, value) { state.stateValue = value } } export default mutations;
store.vue:
<template> <div> <a-input v-model="stateValue"/> <p>{{ stateValue }}</p> <p>appName: {{ appName }}, appNameWithVersion : {{ appNameWithVersion }}</p> <p>userName : {{ userName }}, firstLetter is : {{ firstLetter }}</p> <button @click="handleChangeAppName">修改appName和user.js中的userName</button> <p>动态给state增加appVersion: {{ appVersion }}</p> <button @click="handleActionChangeAppName">通过Action修改appName</button> <button @click="registerModule">动态注册模块</button> <p v-for="(li, index) in todoList" :key="index">{{ li }}</p> </div> </template> <script> import AInput from "_c/AInput.vue"; import AShow from "_c/AShow.vue"; //变量的解构赋值 import { mapState, mapGetters, mapMutations, mapActions } from "vuex"; import { stat } from "fs"; export default { name: "store", data() { return { inputValue: "" }; }, components: { AInput: AInput, AShow: AShow }, computed: { //ES6展开操作符 mapState展开会形成一个对象 使用对象展开运算符将此对象混入到外部对象中 ...mapState({ appName: state => state.appName, appVersion: state => state.appVersion, userName: state => state.user.userName, todoList: state => (state.user.todo ? state.user.todo.todoList : []) }), // 使用对象展开运算符将 getter 混入 computed 对象中 // ...mapGetters(["appNameWithVersion"]), appNameWithVersion() { //通过属性访问getters,Getter 会暴露为 store.getters 对象,可以以属性的形式访问这些值: return this.$store.getters.appNameWithVersion; }, stateValue: { get () { return this.$store.state.stateValue }, set (val) { this.SET_STATE_VALUE(val) } }, ...mapGetters(["firstLetter"]), inputValueLastLetter() { return this.inputValue.substr(-1, 1); } }, methods: { handlerInput(val) { this.inputValue = val; }, // ...mapMutations([ "SET_USER_NAME", //将 `this.SET_USER_NAME()` 映射为 `this.$store.commit('SET_USER_NAME')` "SET_APP_NAME", //将 `this.SET_APP_NAME()` 映射为 `this.$store.commit('SET_APP_NAME')` 'SET_STATE_VALUE' ]), ...mapActions([ "updateAppName" //将 `this.updateAppName()` 映射为 `this.$store.dispatch('updateAppName')` ]), handleChangeAppName() { this.SET_APP_NAME({ appName: "newAppName" }); this.SET_USER_NAME({ userName: "shuyujie" }); this.$store.commit("SET_APP_VERSION"); }, handleActionChangeAppName() { //第一种调用Action的方法 //this.$store.dispatch('updateAppName') //第二种调用Action的方法 this.updateAppName(); }, registerModule() { this.$store.registerModule(["user", "todo"], { state: { todoList: ["学习mutations", "学习actions"] } }); } } }; </script>
AInput.js:
<template>
  <div>
    <input @input="handleInput" :value="value"/>
  </div>
</template>
<script>
export default {
  name:'AInput',
  props:{
    value:{
      type:[String,Number],
      default:''
    }
  },
  methods:{
    handleInput(event){
      const value=event.target.value;
      this.$emit('input',value);
    }
  }
}
</script>
效果图:
 
                    
                
                
            
        
浙公网安备 33010602011771号