1、为什么使用vuex
1.1、官方解释
Vuex是一个专门为Vue.js应用程序开发的状态管理模式,集中式存储管理;
集成devtools,零配置time-travel调试,状态快照导入导出;
1.2、状态管理到底是什么
简单理解:
将其看成把许多个组件共享的变量全部存储在一个对象里面,将这个对象放在顶层的Vue实例中,其他组件可以使用,那么多个组件就可以共享这个对象中的所有变量属性。
1.3、为什么官方专门出一个插件Vuex,难道我们自己封装不行?
当然可以,知识我们需要先想一想vuejs带给我们最大的遍历是什么? 即响应式
如果我们自己封装一个对象 但是不能保证里面所有属性做到响应式,故而 vuex就是为了
提供这样一个在多个组件之间共享状态的插件
1.4、管理什么状态
- 用户的登录状态、用户名称、头像、位置信息
- 商品的收藏、购物车的商品
这些状态信息,我们可以放在统一的位置,对它进行保存和管理,而且还是响应式的
1.5、单界面的状态管理
1.5.1、效果图1
1.5.2、简单说明
state: 状态,暂时理解为data中的属性
view: 视图层,可以针对state的变化,显示不同信息
actions:主要是用户的各种操作;点击,输入等,会导致状态改变
1.6、单界面状态管理实现
1.6.1、效果图2
1.6.2、代码实现
HelloVuex.vue
<template>
<div class="test">
<div>当前计数:{{counter}}</div>
<button @click="counter++">+1</button>
<button @click="counter--">-1</button>
</div>
</template>
<script>
export default {
data(){
return {
counter:0
}
}
};
</script>
1.6.3、案例说明
这个案例中counter就是状态管理体现,counter需要某种方式被记录下来,也就是我们的state,counter目前的值需要被显示在界面上,也就是我们的view部分;界面发生某些操作时候(这里是用户点击,也可以是用户的input),需要去更新状态,也就是我们的actions.
1.7、多界面状态管理
1.7.1、简单说明
- 多个视图都依赖同一个状态(一个状态改了,多个界面需要同事更新)
- 不同界面的actions都想修改同一个状态(Home.vue需要修改,Profile.vue需要修改)
- 也就是说某些状态(状态1,2,3)只属于我们某一个视图,但是也有一些状态(4,5,6)属于多个视图共同想要维护的
1.7.2、全局单例模式(大管家)
- 需要做的是将共享的状态抽取出来,交给我们的大管家Vuex,进行统一管理
- 之后每个视图按照规定好的规定,进行访问修改
1.8、多界面状态管理实现
1.8.1、src目录创建store文件夹
在该目录中创建vuex的信息 index.js
首先需要安装vuex 项目需要使用
import Vuex from 'vuex'
import Vue from 'vue'
// 1、安装插件
Vue.use(Vuex)
// 2、创建对象
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
}
}
})
// 3、导出 供给其他视图获取
export default store
1.8.2、挂载到vue实例中
main.js
说明:其次我们让所有的Vue组件都可以使用这个store对象,在main.js导入store对象,并且放在new Vue中;这样在其他Vue组件中,我们就可以通过this.$store的方式,获取到这个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.8.3 、使用vuex的count
<template>
<div class="test">
<div>当前计数:{{ counter }}</div>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
</div>
</template>
<script>
export default {
computed: {
counter() {
return this.$store.state.count;
},
},
methods: {
increment() {
this.$store.commit("increment");
},
decrement() {
this.$store.commit("decrement");
},
},
};
</script>
2、Vuex基本用法
3、Vuex核心概念
3.1、state
Vuex使用了单一状态树管理应用层级的全部状态,单一状态树能够让我们最直接的方式找到某个状态的片段,而且之后的维护和调试过程中,也可以非常方便
3.2、getters
3.2.1、给出需求说明
现在需要获取persons数组中年龄大于19岁的有哪些?
注意:需要在app.vue,HelloVuex.vue均要显示
persons: [
{ name: "波多...", sex: "女v", age: 18 },
{ name: "三上...", sex: "女", age: 17 },
{ name: "一缕清...", sex: "女", age: 20 },
],
3.2.2、 使用computed属性实现
App.vue
<template>
<div id="app">
{{ messages }}
<HelloVuex />
<h1>这是app的显示内容</h1>
<!-- <h2>{{ $store.getters.overAgeTwenty }}</h2> -->
<h2>{{ this.overAgeTwenty }}</h2>
</div>
</template>
<script>
import HelloVuex from "./components/HelloVuex.vue";
export default {
name: "App",
components: {
HelloVuex,
},
data() {
return {
messages: "消息",
};
},
computed: {
overAgeTwenty() {
return this.$store.state.persons.filter((person) => person.age >= 19);
},
},
};
</script>
<style></style>
HelloVuex.vue
<template>
<div class="test">
<div>当前计数:{{ counter }}</div>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<!-- getters基本使用 -->
<h1>这是HelloVuex显示内容</h1>
<!-- <h2>{{ $store.getters.overAgeTwenty }}</h2> -->
<h2>{{ this.overAgeTwenty }}</h2>
</div>
</template>
<script>
export default {
computed: {
counter() {
return this.$store.state.count;
},
overAgeTwenty() {
return this.$store.state.persons.filter((person) => person.age >= 19);
},
},
methods: {
increment() {
// commit 中 increment 来自于store中方法
this.$store.commit("increment");
},
decrement() {
// commit 中 decrement 来自于store中方法
this.$store.commit("decrement");
},
},
};
</script>
3.2.3 、使用getters实现
router->index.js
import Vuex from "vuex";
import Vue from "vue";
// 1、安装插件
Vue.use(Vuex);
// 2、创建对象
const store = new Vuex.Store({
state: {
count: 0,
persons: [
{ name: "波多...", sex: "女v", age: 18 },
{ name: "三上...", sex: "女", age: 17 },
{ name: "一缕清...", sex: "女", age: 20 },
],
},
mutations: {
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
},
},
getters: {
// 超过20岁筛选出来
overAgeTwenty(state) {
return state.persons.filter((item) => item.age > 18);
},
},
});
// 3、导出 供给其他视图获取
export default store;
HelloVuex.vue
<template>
<div class="test">
<div>当前计数:{{ counter }}</div>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<!-- getters基本使用 -->
<h1>这是HelloVuex显示内容</h1>
<h2>{{ $store.getters.overAgeTwenty }}</h2>
<!-- <h2>{{ this.overAgeTwenty }}</h2> -->
</div>
</template>
<script>
export default {
computed: {
counter() {
return this.$store.state.count;
},
overAgeTwenty() {
return this.$store.state.persons.filter((person) => person.age >= 19);
},
},
methods: {
increment() {
// commit 中 increment 来自于store中方法
this.$store.commit("increment");
},
decrement() {
// commit 中 decrement 来自于store中方法
this.$store.commit("decrement");
},
},
};
</script>
App.vue
<template>
<div id="app">
{{ messages }}
<HelloVuex />
<h1>这是app的显示内容</h1>
<h2>{{ $store.getters.overAgeTwenty }}</h2>
<!-- <h2>{{ this.overAgeTwenty }}</h2> -->
</div>
</template>
<script>
import HelloVuex from "./components/HelloVuex.vue";
export default {
name: "App",
components: {
HelloVuex,
},
data() {
return {
messages: "消息",
};
},
computed: {
overAgeTwenty() {
return this.$store.state.persons.filter((person) => person.age >= 19);
},
},
};
</script>
<style></style>
3.2.4 、分析说明
对于getters方案来说:效率更高;computed属性页面多的话需要操作频繁
3.2.5 、作为参数传递
场景一
1、需求说明:现在需要获取persons年龄大于18多少个
2、代码实现
import Vuex from "vuex";
import Vue from "vue";
// 1、安装插件
Vue.use(Vuex);
// 2、创建对象
const store = new Vuex.Store({
state: {
count: 0,
persons: [
{ name: "波多...", sex: "女v", age: 18 },
{ name: "三上...", sex: "女", age: 17 },
{ name: "一缕清...", sex: "女", age: 20 },
],
},
mutations: {
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
},
},
getters: {
// 超过20岁筛选出来
overAgeTwenty(state) {
return state.persons.filter((item) => item.age > 18);
},
// 超过20岁筛选出来 个数
overAgeTwenty1(state, getters) {
return getters.overAgeTwenty.length;
},
// 根据用户id获取用户信息
userInfoById(state) {
return (id) => {
return state.persons.find((item) => item.age === id);
};
},
},
});
// 3、导出 供给其他视图获取
export default store;
关键代码
场景二 根据用户名称获取用户信息
index.js
getUserInfoByName(state) {
return name => {
return state.persons.filter(item => item.name === name)
}
App.vue
<h1>带有入参getters使用</h1>
<h2>{{this.$store.getters.getUserInfoByName("波多...")}}</h2>
HelloVuex.vue
<h1>带有入参getters使用</h1>
<h2>{{this.$store.getters.getUserInfoByName("波多...")}}</h2>
3.3、mutation
3.3.1、状态更新
- Vuex中store状态的更新唯一方式:提交Mutation
- Mutation主要包括两部分
- 字符串的事件类型(type)
- 一个回调函数,该回调函数的第一个参数就是state
- 定义方式
mutations: {
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
}
},
- 通过mutation更新
methods: {
increment() {
// commit 中 increment 来自于store中方法
this.$store.commit("increment");
},
decrement() {
// commit 中 decrement 来自于store中方法
this.$store.commit("decrement");
},
},
3.3.2 、传递参数
**1、需求说明 counter 一次加5 **
store->index.js
addCount(state, payload) {
state.count += payload
}
HelloVuex.vue
// vue
<button @click="addCount(5)">+5</button>
// js
addCount(count) {
this.$store.commit("addCount", count);
},
出现问题:这时仅仅一个入参 加的次数 5,10,15;若是入参多个参数如何操作?
答:这时候我们通常使用对象形式传递,也就是payload是一个对象,然后从对象获取对应属性信息;具体情况从提交风格表现。
3.3.3、提交风格
1、store-index.js
addCount(state, payload) {
state.count += payload.count;
}
2、HelloVuex.vue
addCount(count) {
this.$store.commit({
type: "addCount",
count: count,
});
3.3.4、响应规则
1、store->index.js
import Vuex from 'vuex'
import Vue from 'vue'
// 1、安装插件
Vue.use(Vuex)
// 2、创建对象
const store = new Vuex.Store({
state: {
info: {
name: 'geekmice', age: 18
}
},
mutations: {
updateInfo(state, payload) {
// state.info['height'] = payload.height;
// Vue.set()操作
// Vue.set(state.info, 'height', payload.height)
// 给info赋值一个新的对象
state.info = { ...state.info, 'height': payload.height }
}
},
})
// 3、导出 供给其他视图获取
export default store
2、MutationsVuex.vue
<template>
<div id="app">
<p>我的个人信息:{{ info }}</p>
<button @click="updateInfo">更新信息</button>
</div>
</template>
<script>
export default {
name: "App",
components: {},
computed: {
info() {
return this.$store.state.info;
},
},
methods: {
updateInfo() {
this.$store.commit("updateInfo", {
height: 1.88,
});
},
},
};
</script>
3、App.vue
<mutations-vuex />
3.3.5、常量类型-概念
1、遇到问题说明
- 在mutation中,我们定义了很多事件类型(也就是其中的方法名称)
- 项目增大时候,Vuex管理的状态越来越多,需要更新状态情况越来越多,Mutation中的方法越来越多
- 方法越多,使用者需要话费大量的精力去记住方法,甚至来回切换,查看方法名称
2、如何避免
使用常量代替Mutation事件的类型,把这些常量放在一个单独的文件,方便管理。
3、如何做
创建文件mutation-type.js,定义我们需要的常量
定义常量时候,采用es2015,使用一个常量代替函数名称
3.3.6、常量类型-代码
1、mutation-type.js
export const INCREMENT = 'increment'
2、index.js
import Vuex from 'vuex'
import Vue from 'vue'
import * as types from './mutation-type'
// 1、安装插件
Vue.use(Vuex)
// 2、创建对象
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
[types.INCREMENT](state) {
state.count++;
}
}
})
// 3、导出 供给其他视图获取
export default store
3、HelloVuex.vue
<template>
<div class="test">
<div>当前计数:{{ counter }}</div>
<button @click="increment">+1</button>
</div>
</template>
<script>
import { INCREMENT } from "../store/mutation-type";
export default {
computed: {
counter() {
return this.$store.state.count;
},
overAgeTwenty() {
return this.$store.state.persons.filter((item) => item.age > 18);
},
},
methods: {
increment() {
// commit 中 increment 来自于store中方法
this.$store.commit(INCREMENT);
},
},
};
</script>
4、App.vue
<HelloVuex />
3.3.7、常量类型-同步函数
1、通常情况,Vuex要求我们Mutation中的方法必须是同步方法
主要原因是我们使用devtools时候,可以使用devtools帮助我们追踪mutation的快照
但是如果是异步操作,那么devtools将不能很好的追踪这个操作何时完成
效果图1
效果图2
3.4、action
3.4.1、基本定义
- 1、使用原因
vue官网强调,不要在mutations中进行异步操作,但是某些情况,确实希望vuex进行异步操作,比如网络请求,必须异步
action类似mutations,用来替换mutations - 2、基本使用代码
const store = new Vuex.Store({
state: {
count: 0
},
mutations:{
increment(state){
state.count++
}
},
actions:{
increment(context){
context.commit('increment')
}
}
app.vue
<button @click='add'>+1</button>
methods:{
add() {
this.$store.dispatch('increment','携带的数据')
}
}
-
3、context是什么
- context和store具有相同方法和属性的对象
- 可以通过context去进行commit操作,也可以获取context.state
- 但是他们并不是同一个对象
-
4、代码是否多余
- 如果Vuex有异步操作,我们就可以在actions完成
3.4.2、分发
methods:{
add(){
this.$store.dispatch('increment');
}
}
同样也支持payload
store->index.js
import Vuex from 'vuex'
import Vue from 'vue'
import * as types from './mutation-type'
// 1、安装插件
Vue.use(Vuex)
// 2、创建对象
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
[types.INCREMENT](state,payload) {
state.count+= payload.count;
},
},
actions: {
[types.INCREMENT](context,payload) {
context.commit(types.INCREMENT,payload)
}
}
})
// 3、导出 供给其他视图获取
export default store
HelloVuex.vue
<template>
<div class="test">
<div>当前计数:{{ counter }}</div>
<button @click="increment">+1</button>
</div>
</template>
<script>
import { INCREMENT } from "../store/mutation-type";
export default {
},
methods: {
increment() {
this.$store.dispatch(INCREMENT,{count:1});
}
}
};
</script>
效果图
3.4.3、返回的promise
在action,我们可以将异步操作放在promise中,并且在成功或者失败之后,调用对应的resolve,reject
关键代码样图
3.5、module
3.5.1、认识vuex的module
1、为什么使用module
- vuex使用单一状态树,那么也意味着很多状态都会交给∨uex来管理
- 当应用变得非常复杂时候,store对象就有可能变得臃肿
- 解决这个问题vuex允许我们将 store分割成模块(module) 每个模块拥有自己的state,mutations,actions,getters等
2、如何使用module
3.5.2、module的局部状态
案例说明
state 获取状态管理 name
index.js 代码如下
const moduleA = {
state:{
name:'jack'
}
}
const store = new Vuex.Store({
modules:{
a:moduleA
}
})
App.vue代码如下
<span>{{ $store.state.a.name }}</span>
mutations 修改用户名称
index.js 代码如下
const moduleA = {
state:{
name:'jack'
},
mutations:{
updateName(state,payload){
state.name=payload
}
}
}
const store = new Vuex.Store({
modules:{
a:moduleA
}
})
App.vue代码如下
<h2>获取moduleA中的 state.name</h2>
<span>{{ $store.state.a.name }}</span>
<button @click="updateName">修改姓名</button>
updateName(){
this.$store.commit('updateName','rose');
}
getters 修改名称
1、单纯getters
index.js
const moduleA = {
state:{
name:'jack'
},
mutations:{
updateName(state,payload){
state.name=payload
}
},
getters:{
fullName(state){
return state.name + '-getters';
}
}
}
const store = new Vuex.Store({
modules:{
a:moduleA
}
})
App.vue
<span>{{$store.getters.fullName}}</span>
2、getters当做入参
index.js
const moduleA = {
state:{
name:'jack'
},
mutations:{
updateName(state,payload){
state.name=payload
}
},
getters:{
fullName(state){
return state.name + '-getters';
},
fullName1(state,getters){
return getters.fullName + '-getters1';
}
}
}
const store = new Vuex.Store({
modules:{
a:moduleA
}
})
App.vue
<span>{{$store.getters.fullName1}}</span>
3、一个模块引用另外一个模块内容
const moduleA = {
state:{
name:'jack'
},
mutations:{
updateName(state,payload){
state.name=payload
}
},
getters:{
fullName(state){
return state.name + '-getters';
},
fullName1(state,getters){
return getters.fullName + '-getters1';
},
fullName2(state,getters,rootState){
return getters.fullName1+rootState.count;
}
}
}
const store = new Vuex.Store({
modules:{
a:moduleA
}
})
App.vue
<span>{{$store.getters.fullName2}}</span>
actions 异步修改名字
index.js
const moduleA = {
state:{
name:'jack'
},
mutations:{
updateName(state,payload){
state.name=payload
}
},
getters:{
fullName(state){
return state.name + '-getters';
},
fullName1(state,getters){
return getters.fullName + '-getters1';
},
fullName2(state,getters,rootState){
return getters.fullName1+rootState.count;
}
},
actions: {
aUpdateName(context) {
setTimeout(() => {
context.commit('updateName', 'jack_rose')
}, 1000);
}
}
}
const store = new Vuex.Store({
modules:{
a:moduleA
}
})
App.vue
<button @click="asyncUpdateName">异步修改姓名</button>
asyncUpdateName() {
this.$store.dispatch("aUpdateName");
},