下面是一段简单的代码,通过这段代码分析一下如何去实现一个自己的 vuex

  1. import Vue from "vue";
  2. import Vuex from "vuex";
  3. Vue.use(Vuex);
  4. export default new Vuex.Store({
  5. state: { count: 0 },
  6. mutations: {
  7. increment(state, n = 1) {
  8. state.count += n;
  9. }
  10. },
  11. getters: {
  12. score(state) {
  13. return `共扔出:${state.count}`
  14. }
  15. },
  16. actions: {
  17. incrementAsync({ commit }) {
  18. setTimeout(() => {
  19. commit("increment", 2);
  20. }, 1000);
  21. }
  22. }
  23. });

经过分析我们得出 Vuex 需要满足下面四个基本功能

  • 作为 Vue 插件 — 实现 install 方法
  • 实现:state、mutations、actions、getters
  • Store 类
  • 数据响应式

实现插件功能

这里与 Vue Router 中的 install 方法类似,不同的地方在于我们不再全局引入 Vue,之前的代码也可以同样处理

  1. let Vue;
  2. // 实现 install 方法
  3. function install(_Vue) {
  4. // 这里和 Vue Router 代码略有不同,不在全局引入 Vue
  5. Vue = _Vue;
  6. // 全局混入
  7. Vue.mixin({
  8. beforeCreate() {
  9. if (this.$options.store) {
  10. Vue.prototype.$store = this.$options.store;
  11. }
  12. }
  13. });
  14. }

创建 Store 类

  1. class Store {
  2. constructor(options) {
  3. this.state = new Vue({
  4. data: options.state
  5. });
  6. this.mutations = options.mutations;
  7. this.actions = options.actions;
  8. options.getters && this.handleGetters(options.getters);
  9. }
  10. // 声明为箭头函数,保留 commit 中 this 的指向
  11. commit = (type, args) => {
  12. this.mutations[type](this.state, args);
  13. };
  14. dispatch (type, args) {
  15. this.actions[type](
  16. {
  17. commit: this.commit,
  18. state: this.state
  19. },
  20. args
  21. );
  22. }
  23. handleGetters(getters) {
  24. this.getters = {};
  25. // 遍历 getters 所有 key
  26. Object.keys(getters).forEach(key => {
  27. // 为 this.getters 定义若干属性,这些属性是只读的
  28. // $store.getters.score
  29. Object.defineProperty(this.getters, key, {
  30. get: () => {
  31. return getters[key](this.state);
  32. }
  33. });
  34. });
  35. }
  36. }
  37. export default { Store, install };

思考解答

思考:为什么 Store 类中的 commit 方法声明为箭头函数?

  1. // Store 类中的 commit 方法声明为箭头函数是为了保证 this 指向 store,避免下面的情况导致报错
  2. actions: {
  3. incrementAsync({ commit }) {
  4. setTimeout(() => {
  5. commit("increment", 2); // 此处 commit 方法 中 this 指向的是 window
  6. }, 1000);
  7. }
  8. }

完整代码

  1. let Vue;
  2. class Store {
  3. constructor(options) {
  4. this.state = new Vue({
  5. data: options.state
  6. });
  7. this.mutations = options.mutations;
  8. this.actions = options.actions;
  9. options.getters && this.handleGetters(options.getters);
  10. }
  11. // 声明为箭头函数,保留 commit 中 this 的指向
  12. commit = (type, args) => {
  13. this.mutations[type](this.state, args);
  14. };
  15. dispatch (type, args) {
  16. this.actions[type](
  17. {
  18. commit: this.commit,
  19. state: this.state
  20. },
  21. args
  22. );
  23. }
  24. handleGetters(getters) {
  25. this.getters = {};
  26. // 遍历 getters 所有 key
  27. Object.keys(getters).forEach(key => {
  28. // 为 this.getters 定义若干属性,且这些属性是只读的
  29. // $store.getters.score
  30. Object.defineProperty(this.getters, key, {
  31. get: () => {
  32. return getters[key](this.state);
  33. }
  34. });
  35. });
  36. }
  37. }
  38. // 实现 install 方法
  39. function install(_Vue) {
  40. // 这里和 Vue Router 代码略有不同,不在全局引入 Vue
  41. Vue = _Vue;
  42. // 全局混入
  43. Vue.mixin({
  44. beforeCreate() {
  45. if (this.$options.store) {
  46. Vue.prototype.$store = this.$options.store;
  47. }
  48. }
  49. });
  50. }
  51. export default { Store, install };