# 二.state 的 使用

# 1.案例

  • 1.新建 store.js 文件
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
  state: {
    count: 100,
  },
});
1
2
3
4
5
6
7
8
  • 2.在 main.js 中添加 store
import Vue from "vue";
import App from "./App.vue";
import store from "./store";

Vue.config.productionTip = false;

new Vue({
  store,
  render: (h) => h(App),
}).$mount("#app");
1
2
3
4
5
6
7
8
9
10
  • 3.在 App.vue 中使用 store 中定义的 count 变量
<template>
  <div id="app">
    {{this.$store.state.count}}
  </div>
</template>

<script>
export default {
  name: "app"
};
</script>

<style>

</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 打开浏览器页面出现定义的变量:100

# 2.state 的实现

  • 1.在 store.js 中新建 vuex.js 文件Vue.use(Vuex);会调用 Vuex 中的install方法,声明一个变量Vue,保留用户的构造函数
let Vue;

class Store {}
let install = (_Vue) => {
  Vue = _Vue;
};
export default {
  Store,
  install,
};
1
2
3
4
5
6
7
8
9
10
  • 2.混合mixin,可以给所有的实列混合些东西,beforeCreate所有的组件创建之前都可以调用
let Vue;

class Store {}
let install = (_Vue) => {
  Vue = _Vue;
  Vue.mixin({
    beforeCreate() {
      console.log("aaa");
    },
  });
};
export default {
  Store,
  install,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

调用两次,第一次是 main.js 调用,第二次是 App.vue 组件调用

  • 3.我们需要把根组件中 store 实列给每个组件都增加一个$store

在 vuex.js 中

beforeCreate() {
  if (this.$options && this.$options.store) {
    this.$store = this.$options.store;
  } else {
    this.$store = this.$parent && this.$parent.$store;
  }
}
1
2
3
4
5
6
7
  • 先判断是否是根组件,然后子组件根据父组件的 store 拿到 store

  • 4.组件中调用自己写的 vuex 方法,看是否有 store

在 App.js 中

<template>
  <div id="app">
    <!-- {{this.$store.state.count}} -->
  </div>
</template>

<script>
export default {
  name: "app",
  mounted() {
    console.log(this.$store);
  }
};
</script>

<style>

</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 打开浏览器控制台显示:Store {}

  • 5.获取 state 中的值

  • 组件中的 Store 是从 vuex 中 new 出来的new Vuex.Store

  • Store 类中有 state getters mutations actions 等属性,可以通过 state 属性拿到 state 中的值
let Vue;
class Store {
  constructor(options) {
    let state = options.state;
    this._vm = new Vue({
      data: {
        state,
      },
    });
  }
  get state() {
    return this._vm.state;
  }
}
let install = (_Vue) => {
  Vue = _Vue;
  Vue.mixin({
    beforeCreate() {
      if (this.$options && this.$options.store) {
        this.$store = this.$options.store;
      } else {
        this.$store = this.$parent && this.$parent.$store;
      }
    },
  });
};
export default {
  Store,
  install,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  • 什么样的属性可以实现双向数据绑定 有getset如,new vue({data:{}})
  • 查看浏览器:100

# 3.getters 的使用

  • 1.getter 的使用

sotre.js 中

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
  state: {
    count: 100,
  },
  getters: {
    newCount(state) {
      return state.count + 100;
    },
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13

App.vue 中

<template>
  <div id="app">
    {{this.$store.state.count}}
    {{this.$store.getters.newCount}}
  </div>
</template>

<script>
export default {
  name: "app",
  mounted() {}
};
</script>

<style>

</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 浏览器页面显示:100 200

# 4. getters 的实现

  • 1.将 options 中的 getters 拿出来遍历这个对象,将值返回
let Vue;

class Store {
  constructor(options) {
    let state = options.state;
    this.getters = {};
    this.mutations = {};
    this.actions = {};
    this._vm = new Vue({
      data: {
        state,
      },
    });
    if (options.getters) {
      let getters = options.getters;
      forEach(getters, (getterName, getterFn) => {
        Object.defineProperty(this.getters, getterName, {
          get: () => {
            return getterFn(state);
          },
        });
      });
    }
  }
  get state() {
    return this._vm.state;
  }
}
function forEach(obj, callback) {
  Object.keys(obj).forEach((item) => callback(item, obj[item]));
}
let install = (_Vue) => {
  Vue = _Vue;
  Vue.mixin({
    beforeCreate() {
      if (this.$options && this.$options.store) {
        this.$store = this.$options.store;
      } else {
        this.$store = this.$parent && this.$parent.$store;
      }
    },
  });
};
export default {
  Store,
  install,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  • 页面出现:100 200

# 5.mutations 的使用

  • 1.mutations 的用法

store.js

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
  state: {
    count: 100,
  },
  getters: {
    newCount(state) {
      return state.count + 100;
    },
  },
  mutations: {
    change(state) {
      state.count += 10;
    },
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

App.js

<template>
  <div id="app">
    {{this.$store.state.count}}
    {{this.$store.getters.newCount}}
    <button @click="change">add</button>
  </div>
</template>

<script>
export default {
  name: "app",
  mounted() {},
  methods: {
    change() {
      this.$store.commit("change");
    }
  }
};
</script>

<style>

</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  • 点击按钮页面数字加 10

# 6. mutations 的实现

Vuex.js

class Store {
  constructor(options) {
    let state = options.state;
    this.getters = {};
    this.mutations = {};
    this.actions = {};
    this._vm = new Vue({
      data: {
        state,
      },
    });
    if (options.getters) {
      let getters = options.getters;
      forEach(getters, (getterName, getterFn) => {
        Object.defineProperty(this.getters, getterName, {
          get: () => {
            return getterFn(state);
          },
        });
      });
    }
    let mutations = options.mutations;
    forEach(mutations, (mutationName, mutationFn) => {
      this.mutations[mutationName] = () => {
        mutationFn.call(this, state);
      };
    });
  }
  get state() {
    return this._vm.state;
  }
  commit(type) {
    this.mutations[type]();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  • 刷新浏览器页面数字加 10

# 7.actions 的使用

  • 1.action 的用法(异步方法)

store.js

actions: {
  change({ commit }) {
    setTimeout(() => {
      commit("change");
    }, 1000);
  }
}
1
2
3
4
5
6
7

App.js

change() {
  this.$store.dispatch("change");
}
1
2
3
  • 点击浏览器按钮 1 秒后加 10

# 8.actions 的实现

store.js

class Store {
  constructor(options) {
    let state = options.state;
    this.getters = {};
    this.mutations = {};
    this.actions = {};
    this._vm = new Vue({
      data: {
        state,
      },
    });
    if (options.getters) {
      let getters = options.getters;
      forEach(getters, (getterName, getterFn) => {
        Object.defineProperty(this.getters, getterName, {
          get: () => {
            return getterFn(state);
          },
        });
      });
    }
    let mutations = options.mutations;
    forEach(mutations, (mutationName, mutationFn) => {
      this.mutations[mutationName] = () => {
        mutationFn.call(this, state);
      };
    });
    let actions = options.actions;
    forEach(actions, (actionName, actionFn) => {
      this.actions[actionName] = () => {
        actionFn.call(this, this);
      };
    });
    let { commit, dispatch } = this;
    this.commit = (type) => {
      commit.call(this, type);
    };
    this.dispatch = (type) => {
      dispatch.call(this, type);
    };
  }
  get state() {
    return this._vm.state;
  }
  commit(type) {
    this.mutations[type]();
  }
  dispatch(type) {
    this.actions[type]();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  • 打开浏览器点击按钮数据加 10

# 9.modules 的使用

store.js

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
  modules: {
    a: {
      state: {
        count: 200,
      },
      modules: {
        b: {
          state: {
            count: 300,
          },
        },
      },
    },
  },
  state: {
    count: 100,
  },
  getters: {
    newCount(state) {
      return state.count + 100;
    },
  },
  mutations: {
    change(state) {
      state.count += 10;
    },
  },
  actions: {
    change({ commit }) {
      setTimeout(() => {
        commit("change");
      }, 1000);
    },
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

App.vue

<template>
  <div id="app">
    {{this.$store.state.count}}
    {{this.$store.getters.newCount}}
    {{this.$store.state.a.b.count}}
    <button @click="change">add</button>
  </div>
</template>

<script>
export default {
  name: "app",
  mounted() {},
  methods: {
    change() {
      this.$store.dispatch("change");
    }
  }
};
</script>

<style>

</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  • 打开浏览器会有 300 显示出来

# 10.modules 的实现

let Vue;
class ModuleCollection {
  // 默认actions mutation 都会定义到store上面
  constructor(options) {
    // vuex [a]
    this.register([], options);
  }
  register(path, rawModule) {
    // path 是个空数组 rawModule就是个对象
    let newModule = {
      _raw: rawModule, // 对象 当前 有state getters 那个对象
      _children: {}, // 表示 他包含的模块
      state: rawModule.state, // 自己模块的状态
    };
    if (path.length == 0) {
      this.root = newModule; // 根
    } else {
      // [a,b];  // reduce方法 {{a.b.c}}
      let parent = path.slice(0, -1).reduce((root, current) => {
        return root._children[current];
      }, this.root);
      parent._children[path[path.length - 1]] = newModule;
    }
    if (rawModule.modules) {
      // 有子模块
      forEach(rawModule.modules, (childName, module) => {
        // [a,b];
        // [a,d]
        this.register(path.concat(childName), module);
      });
    }
  }
}
// vuex 中间件  可以封装自己的逻辑  subscirbe registerModule unregisterModule
function installModule(store, rootState, path, rootModule) {
  // rootState.a = {count:200}
  // rootState.a.b = {count:3000}
  if (path.length > 0) {
    // [a,b]
    // 第二次 获取到的就是a对应的对象
    let parent = path.slice(0, -1).reduce((root, current) => {
      return root[current];
    }, rootState);
    // {count:1000,a:{}}
    Vue.set(parent, path[path.length - 1], rootModule.state);
  }
  if (rootModule._raw.getters) {
    forEach(rootModule._raw.getters, (getterName, getterFn) => {
      Object.defineProperty(store.getters, getterName, {
        get: () => {
          return getterFn(rootModule.state);
        },
      });
    });
  }
  if (rootModule._raw.actions) {
    forEach(rootModule._raw.actions, (actionName, actionFn) => {
      let entry = store.actions[actionName] || (store.actions[actionName] = []);
      entry.push(() => {
        actionFn.call(store, store);
      });
    });
  }
  if (rootModule._raw.mutations) {
    forEach(rootModule._raw.mutations, (mutationName, mutationFn) => {
      let entry =
        store.mutations[mutationName] || (store.mutations[mutationName] = []);
      entry.push(() => {
        mutationFn.call(store, rootModule.state);
      });
    });
  }
  forEach(rootModule._children, (childName, module) => {
    installModule(store, rootState, path.concat(childName), module);
  });
}
class Store {
  // state getters mutations actions
  constructor(options) {
    let state = options.state; // {count:200}
    this.getters = {};
    this.mutations = {};
    this.actions = {};
    // 什么样的属性 可以实现双向 有get 和set new vue({data:{}})
    // vuex核心就是借用了vue的实例 因为vue的实例数据变化 会刷新视图
    this._vm = new Vue({
      data: {
        state,
      },
    });

    // 把模块直接的关系进行整理  自己根据用户传入的参数维护了一个对象
    // root._children=>a._children=>b
    this.modules = new ModuleCollection(options);
    //  无论是子模块 还是孙子 所有的mutation 都是根上的

    // this是store的实例 [] path this.mdoue.root 当前的根模块
    installModule(this, state, [], this.modules.root); // {_raw,_children,state}
    let { commit, dispatch } = this;
    this.commit = (type) => {
      commit.call(this, type);
    };
    this.dispatch = (type) => {
      dispatch.call(this, type);
    };
  }
  get state() {
    // Object.definefineProperty get
    return this._vm.state;
  }
  commit(type) {
    // undefine
    this.mutations[type].forEach((fn) => fn());
  }
  dispatch(type) {
    this.actions[type].forEach((fn) => fn());
  }
}

function forEach(obj, callback) {
  Object.keys(obj).forEach((item) => callback(item, obj[item]));
}
let install = (_Vue) => {
  Vue = _Vue; // 保留vue的构造函数
  Vue.mixin({
    beforeCreate() {
      // 我需要把根组件中 store实例 给每个组件都增加一个$store的属性
      // 是否是根组件
      if (this.$options && this.$options.store) {
        debugger;
        this.$store = this.$options.store;
      } else {
        // 子组件 深度优先 父-> 子 -> 孙子
        this.$store = this.$parent && this.$parent.$store;
      }
    },
  });
};

export default {
  Store,
  install,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143