vuex速成学习总结

vuex | 应用场景

  • 用于【共享状态】较多,经常被在各处修改的【大型项目】
  • 用于大中型单页应用,简单的 spa 可以使用【中央数据总线 bus 】来管理数据(bus也是一个vue实例)
  • 如果不是多人协作或非常复杂的项目,一个简单的 store 模式就足够了

store | 是全局共享状态

  • 把组件的共享状态抽取出来,以一个全局单例模式管理
  • 组件树构成了一个巨大的“视图”,任何组件都能获取状态或者触发行为
  • 通过定义和隔离状态管理,代码将会变得更结构化且易维护

store | 是响应式全局变量

  • Vuex 的【状态存储是响应式的】,若 修改store(只能用 commit 或 action 方式),那么相应的组件也会更新数据

commit 和 action 操作

  • 应用层级的状态应该集中到单个 store 对象中
  • 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation
  • 异步逻辑都应该封装到 action 里面

vuex 目录结构

  • 一般,一个简单的 store 文件即可
  • 大型多人开发应用,Vuex 相关代码要分割到模块中,如下
├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块

严格模式(不建议设置)

  • 设置 strict: true。不要在发布环境下启用严格模式!

三步跑起第一个 vuex 程序

  • 1 新建 store.js 文件
Vue.use(Vuex);
export default new Vuex.Store({
    state: {
        count: 0
    },
}
  • 2 在根组件 js 文件中注册 store
import store from "./store";
new Vue({
      el: "#app",
      store,
      router: router,
      render: h => h(App)
});
  • 3 已经可以在所有的组件中使用了。通过【this.$store.state】的方式引用,但注意,如果要在组件中使用 store 的值,就需要将其声明为【计算属性】予以保存
export default {
  data() {
        return {};
  },
  computed: {
        count() {
              return this.$store.state.count;
        }
  }
};

基础概念的完全理解

state | 存储数据

getters | 加工数据

  • 加工并获取派生数据,可以理解为 store 中的计算属性
  • 一个Gette中也可以接受另一个Getter
  • 在组件中使用时,可将其值保存在组件的计算属性中
//声明
getters: {
    countText: state => {
          return "the count is " + state.count;
    }
},
//调用
//在组件中保存 getters 中的数据
computed: {
    countText() {
      return this.$store.getters.countText;
    }
},

mutations | 修改 state 的唯一方式

  • 更改 state 的方法,也是唯一的方法
  • mutations必须是同步执行
  • 在组件中,通过 this.$store.commit 方法调用
  • commit 方法第一个参数为mutations方法名,第二个参数可以是一个对象
//声明
mutations: {
    setToken(state, newToken) {
        state.data.token = newToken;
    },
    //向 store.commit 传入额外的参数,即 mutation 的 载荷(payload)
    //使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数
    incrementByAnything(state, payload) {
        state.count += payload.more;
    }
},
//调用
mounted() {
    this.$store.commit("setToken", "bbbbb");
},

actions | 允许异步操作

  • action 提交的是 mutation,不能直接变更状态
  • action 可以包含任意异步操作
  • action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,它可以避免写 const that = this 这样的代码
  • 在组件中,通过 this.$store.dispatch 方法调用
  • dispatch 方法第一个参数为mutations方法名,第二个参数可以是一个对象
  • 在一个 actions 方法中,可以调用另一个 actions 方法
//先声明mutations
mutations: {
    setToken(state, newToken) {
        state.data.token = newToken;
    },
    nameToUppercase(state, friend) {
        state.data.fullName = (state.data.fullName + friend).toUpperCase();
    },
}
//声明actions,其中只能使用mutations
//actions中可以包括异步操作
//actions方法接受两个参数,第一个可以是对象,第二个是传参(可以是对象)
actions: {
    nameToUppercase({ commit,sate }, friend) {
        setTimeout(() => {
            commit("nameToUppercase", friend);
        }, 1500);
    },
    //也可以这样写,传入vue组件的实例 context
    nameToUppercase(context, friend) {
        setTimeout(() => {
            context.commit("nameToUppercase", friend);
        }, 1500);
    }
}
//在组件中调用
methods: {
    uppercaseName() {
      this.$store.dispatch("nameToUppercase", "gs");
    },
}
//在一个 actions 方法中,可以调用另一个 actions 方法
actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

利用 async / await 实现组合 action

  • 通过使用 async / await 语法和 promise 对象,实现异步操作的同步写法
  • 如下,实现2s后给name添加"789",再过1s后将英文字母大写
//在store中定义mutations方法
mutations: {
    nameToUppercase(state, friend) {
        state.data.fullName = (state.data.fullName + friend).toUpperCase();
    },
    nameAddmore(state, str) {
        state.data.fullName = state.data.fullName + str;
    }
}
//在store中定义actions方法
actions: {
    async nameAddmore({ commit }) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log("add 789");
                commit("nameAddmore", "789");
                resolve()
            }, 2000)
        })
    },
    async nameToUppercase({ commit, dispatch }, friend) {
        await dispatch('nameAddmore');
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log("uppercase name");
                commit("nameToUppercase", friend);
                resolve()
            }, 1000)
        })
    }
}
//在组件中调用,可见已经隐藏了所有内部实现
methods: {
    uppercaseName() {
      this.nameToUppercase("gsaaaa").then(() => {
        console.log("all completed");
      });
    },
}
//执行结果
//2s打印 add 789,显示 zhangkai789
//再1s打印 uppercase name 显示 ZHANGKAI789
//最后打印 all completed 显示 ZHANGKAI789

mapActions 辅助函数的映射

  • 在组件中使用时,需要引入对象 import { mapActions } from “vuex”
  • 如下例,将 this.nameToUppercase() 映射为 this.$store.dispatch(‘nameToUppercase’)
  • 这样,在组件中的其它方法中,就可以方便引用了
//声明
actions: {
    nameToUppercase({ commit,sate }, friend) {
        setTimeout(() => {
            commit("nameToUppercase", friend);
        }, 1500);
    },
}
//在组件中调用
methods: {
    ...mapActions(["nameToUppercase"]),
    //在其它方法中调用映射
    uppercaseName() {
      // do something...
      this.nameToUppercase("gsss");
    },
}

【…mapActions语法报错的问题】

  • 原因是缺乏对 es6 语法 rest-spread 的支持,解决办法如下
#安装依赖
npm install --save-dev babel-plugin-transform-object-rest-spread
#在 .babelrc 配置文件中新增
{ "plugins": ["transform-object-rest-spread"] }

Module | 模块

更多功能参考

  • vuex 允许我们将 store 分割成模块
  • 每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块
  • 对每个模块设置 namespaced 属性,可以防止多个模块的方法重名。因为如果不设置,除了 state ,mutation 等方法会注册在主模块上
  • state 的模块调用方式,例如zk模块:this.$store.state.zk.name
  • mutations 的模块调用方式为【目录式】,例如模块zk:this.$store.commit(“zk/setName”, “lili”)
//moduleA
const moduleA = {
    namespaced: true,
    state: {
        name: "zk",
        age: 22
    },
    mutations: {
        setName(state, newName) {
            state.name = newName;
        },
    },
}
export default moduleA;

//moduleB
const moduleB = {
    namespaced: true,
    state: {
        name: "gs",
        age: 18
    },
    mutations: {
        setName(state, newName) {
            state.name = newName;
        },
    },
}
export default moduleB;

//主模块
import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './moduleA'
import moduleB from './moduleB'

Vue.use(Vuex);

export default new Vuex.Store({
    modules: {
        zk: moduleA,
        gs: moduleB
    }
})

//在Vue根实例上注册 store
import store from "./store";
new Vue({
    el: "#app",
    store,
    router: router,
    render: h => h(App)
});
//在组件中使用
computed: {
    zkName() {
      return this.$store.state.zk.name;//注意 state 的模块调用方式
    },
    gsName() {
      return this.$store.state.gs.name;//注意 state 的模块调用方式
    }
},
methods: {
    setZkName() {
      this.$store.commit("zk/setName", "lili");//注意 mutations 的模块调用方式为【目录式】
    },
    setGsName() {
      this.$store.commit("gs/setName", "bebe");//注意 mutations 的模块调用方式为【目录式】
    },
}

你可能感兴趣的:(vue)