vuejs之vuex学习

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");
},

请添加图片描述

3.5.3、modules写法

4、项目组织架构

在这里插入图片描述

5、仓库地址

码云仓库地址

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值