Vue2中vuex使用方法

在进行vue2项目开发的时候,我们有时会希望某些数据能够全局共享这时候vuex就能够帮到我们

vuex可以将数据集中起来管理,我把vuex叫做一个仓库。在vue2中通常使用vuex3,在vue3中通常适合用vuex4,所以这里我们使用的是vuex3。

创建store仓库

第一步,在vue2项目中下载vuex3,如果用cli自定义项目,可以直接勾选vuex,他会自动帮你下载导入

npm add vuex@3

 第二步,在src目录下新建一个store目录,并在store目录下新建index.js文件

 

 第三步,在index.js中引入Vue和Vuex,然后Vue.use(Vuex)(这些步骤是必要的),然后再导出一个新建的Vuex.store对象给main.js引入挂载,在Vuex.store对象中有许多方法,这里暂时不着急,先写上,后面再说

 在store下的index.js中:     

//这里面存放的就是vuex的核心代码    
import Vue from 'vue'
import Vuex from 'vuex'

//插件安装
Vue.use(Vuex)

//创建仓库
export default new Vuex.Store({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

在main.js中:

import Vue from 'vue'
import App from './App.vue'
//引入了vuex
import store from './store'

Vue.config.productionTip = false

new Vue({
//将vuex挂载
  store,
  render: h => h(App)
}).$mount('#app')

完成这几步,我们就已经创建好了一个vuex所管理的store全局数据仓库了,这时候我们还只是创建了一个空的仓库,但是还没有数据,在vuex中所有共享的数据都要统一放在仓库的State中存储。

State和data相似,只不过State是所有组件共享的数据,而data是某个组件自己的数据。这里我在仓库中添加了一个count数据,它的值为1。仓库可以提供很多变量,并不只是count

//这里面存放的就是vuex的核心代码    
import Vue from 'vue'
import Vuex from 'vuex'

//插件安装
Vue.use(Vuex)

//创建仓库
export default new Vuex.Store({
  state: {
    count:1,
    title:'Hello World'
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

访问数据(state)

给仓库添加了数据之后就要来使用数据了,使用数据有两种方法:①通过store直接访问②通过辅助函数来访问。这里先来看第一种,通过store直接访问

1、在模板中使用

{{ $store.state.count }}

2、在组件逻辑中(即在.vue文件的script中)

this.$store.state.count

3、在js模块中

store.state.count

我们用辅助函数来访问,这里需要用到vuex中的mapStates辅助函数,能够帮助我们把store中的属性自动映射到组件的计算属性中,这里我们可以不用纠结于映射是什么概念,我们只要知道通过mapStates辅助函数可以让我们在组件模板中使用vuex仓库的数据时,不用再写原生代码(例如$store.state.count)那么长的英文了

在组件中使用辅助函数时,有两个步骤

1、引入mapStates

import {mapStates} from 'vuex'

2、在计算属性中使用...展开运算符映射

  computed:{
    ...mapState(['count','title'])
  }

 因为在开发中的仓库会有很多条数据,所以在扩展mapStates中用数组来包裹你想要在这个组件中使用的数据名称

这样我们可以在组件模板中直接使用数据名称也是没有问题的

{{count}}
//1
{{title}}
//Hello World

修改数据(mumations)

现在我们可以访问到数据了,我们还想要修改store仓库中的数据,这时候又有一个新的概念mutations。

在了解mutations之前,我们要遵循vuex的单项数据流,即组件可以访问store仓库中的数据,不能直接修改store仓库中的数据,但是我们可以借助mutations间接修改store仓库中的数据

在store仓库mutations中添加一个给state中的count变量+1的方法

//这里面存放的就是vuex的核心代码    
import Vue from 'vue'
import Vuex from 'vuex'

//插件安装
Vue.use(Vuex)

//创建仓库
export default new Vuex.Store({
  state: {
    count:1,
    title:'Hello World'
  },
  getters: {
  },
  mutations: {
    //函数的第一个参数是当前store的state属性
    addCount(state){
        state.count++
    }
  },
  actions: {
  },
  modules: {
  }
})

这时我们在组件的方法中提交调用mutations,就会使得store中state属性的count值+1

methods:{
    handleCount(){
        this.$store.commit('addCount')
    }
    //count+1
}

 但此时如果我们又有其他的需求,我想让count+5,或者count+10怎么办?这时候我们就需要传参,在addCount中第一个参数为state,我们可以设置第二个参数来改变count的值

//这里面存放的就是vuex的核心代码    
import Vue from 'vue'
import Vuex from 'vuex'

//插件安装
Vue.use(Vuex)

//创建仓库
export default new Vuex.Store({
  state: {
    count:1,
    title:'Hello World'
  },
  getters: {
  },
  mutations: {
    //函数的第一个参数是当前store的state属性
    addCount(state,n){
        state.count+=n
    }
  },
  actions: {
  },
  modules: {
  }
})

这时候我们在组件中使用commit,也传入值,就能让这个方法多次复用了。如果想传入多个值的话,可以用对象把值给包裹起来再传参

methods:{
    handleCount(){
        this.$store.commit('addCount',5)
    }
    //count+5
}

这个时候又遇到难题了,我想在input框里实现实时输入,实时更新,但是在组件中不能直接修改store仓库中的值,所以不能使用v-mode,我们要遵循vuex的单项数据流。我们反向思考,将v-model拆分为:value和@input的组合,用:value来渲染,用@input来监听输入的内容


<template>
  <div id="app">
      <h1>根组件 - {{ title }} - 哈哈</h1>
      <input :value="count" @input="handleInput" type="text">
  </div>
</template>

<script>
import {mapState} from 'vuex'
export default {
  computed:{
    ...mapState(['count','title'])
  },
  methods: {
    handleInput(e){
      //实时获取输入框的值
      console.log(e.target.value);
      this.$store.commit('addCount',e.target.value)
    }
  },
}
</script>

<style lang="less">
#app{
  width: 600px;
  margin:20px auto;
  border:3px solid #ccc;
  border-radius: 3px;
  padding: 10px;
}
</style>

辅助函数mapMutations

        mapMutations和mapStates很像,它是把位于mutations中的方法提取了出来,映射打到组件methods中


<template>
  <div id="app">
      <h1>根组件 - {{ title }} - 哈哈</h1>
      <input :value="count" @input="handleInput" type="text">
  </div>
</template>

<script>
//引入mapMutations
import {mapState,mapMutations} from 'vuex'
export default {
  computed:{
    ...mapState(['count','title'])
  },
  methods: {
    //扩展映射addCount方法
    ...mapMutations(['addCount'])
    handleInput(e){
      //实时获取输入框的值
      console.log(e.target.value);
      //this.$store.commit('addCount',e.target.value)
      //直接调用
      this.addCount(e.target.value)
    }
  },
}
</script>

<style lang="less">
#app{
  width: 600px;
  margin:20px auto;
  border:3px solid #ccc;
  border-radius: 3px;
  padding: 10px;
}
</style>

异步修改数据或调用数据(actions)

        actions在store中和mutations一样,也是用来修改state中的值的,但是actions不同点在于它使用异步的方式来处理数据的,这时候就有疑惑了,mutations中不能做这些操作吗?没错,mutations必须是同步的(便于监测数据变化,记录调试),如果以后有数据请求来改变state中的数据那么只能使用actions来异步改变数据,虽然是在actions中处理异步,处理完异步之后,还是需要通过mutations来修改数据

        需求:一秒钟后,修改state的count为某个数

//这里面存放的就是vuex的核心代码    
import Vue from 'vue'
import Vuex from 'vuex'

//插件安装
Vue.use(Vuex)

//创建仓库
export default new Vuex.Store({
  state: {
    count:1,
    title:'Hello World'
  },
  getters: {
  },
  mutations: {
    //函数的第一个参数是当前store的state属性
    changeCount(state,n){
        state.count = n
    }
  },
  actions: {
        //这里的context可以把它看作store本身
       setAsyncCount(context,num){
        //一秒钟后,给一个数,去修改count
        setTimeOut(()=>{
            context.commit('changeCount',num)
        },1000)
    }
  },
  modules: {
  }
})

        在组件中用dispatch调用actions方法。本质上是actions先给你处理了异步,再把剩下的丢给mutations处理

this.$store.dispatch('setAsyncCount',200)

辅助函数mapActions

        mapActions是把位于actions中的方法提取了出来,映射到组件methods中


<template>
  <div id="app">
      <h1>根组件 - {{ title }} - 哈哈</h1>
      <input :value="count" @input="handleInput" type="text">
        //直接调用actions方法进行异步修改
      <button @click="setAsyncCount(666)">修改666</button>
  </div>
</template>

<script>
//引入mapActions
import {mapState,mapMutations,mapActions} from 'vuex'
export default {
  computed:{
    ...mapState(['count','title'])
  },
  methods: {
    //扩展映射addCount方法
    ...mapMutations(['addCount']),
    //扩展映射setAsyncCount方法
    ...mapActions(['setAsyncCount']),
    handleInput(e){
      //实时获取输入框的值
      console.log(e.target.value);
      //this.$store.commit('addCount',e.target.value)
      //直接调用
      this.addCount(e.target.value)
    }
  },
}
</script>

<style lang="less">
#app{
  width: 600px;
  margin:20px auto;
  border:3px solid #ccc;
  border-radius: 3px;
  padding: 10px;
}
</style>

辅助函数getters

        除了state之外,有时我们还需要从state中派生出一些状态,这些状态时依赖state的,此时会用到getters。例如:state中定义了list,为1-10的数组,组件中,需要显示所有大于5的数据

state:{
    list:[1,2,3,4,5,6,7,8,9,10]
}

        定义getters

        

getters:{
    //注意:
    //(1)getters函数的第一个参数必须是state
    //(2)getters函数必须有返回值
    filterList(state){
        return state.list.filter(item => item > 5)
    }
    //形如计算属性
}

        定义完成后,有两种访问getters的方式

        ①通过store访问getters

//在模板语法中
{{$store.getters.filterList}}

//在script中
this.$store.getters.filterList

        ②通过辅助函数mapGetters映射

//同样也要引入mapGetters
computed:{
    ...mapGetters(['filterList'])
}
//模板语法和script中都可以直接使用,就相当于组件中的计算属性
{{ filterList }}

模块modules(进阶语法)

        由于vuex使用单一状态树,项目的所有数据会集中到一个js文件。当项目变得非常复杂时,store仓库就会变得相当臃肿,难以维护。这时候我们可以使用modules,创建多个子仓库来进行模模块化管理,第一步在store文件夹下新建一个modules文件夹,再在modules文件夹下再新建子仓库,例如用户(user.js)的数据作为一个子仓库来管理,设置(setting.js)作为一个子仓库来管理。

        将来在实际开发项目中会有非常非常多的模块,而每一个js文件就是一个模块,并且每一个模块都有自己的核心概念(例如state,mutations,actions)

        先来看看怎么设置子模块,这里我设置了一个user.js模块,子模块中有state、mumations、actions和getters,都用对象来表达。最后再做一件事,将所有对象导出,相当于导出了一个子模块

//user模块
const state = {
    userInfo:{
        name:'张三',
        age:18
    },
    score:80
}
const mutations = {
    updataUser(state,newUser){
        state.userInfo = newUser
    }
}
const actions = {}
const getters = {}

export default {
    state,
    mutations,
    actions,
    getters
}

        然后再在store文件夹下的index.js文件下进行导入,在modules中进行应用,这里的user是简写,实际上是user:user

import user from './modules/user'

const store = new Vuex.store({
    modules:{
        user
    }
})

         到这一步,尽管已经分模块了,已经模块化了但是子模块的状态,还是会挂载到根级别(index.js)的state中,属性名就是模块名

         想要拿到子模块的数据(state):

        ①直接通过模块名访问  $store.state.模块名.xxx

{{ $store.state.user.userInfo.name }}
//张三

        ②通过mapState映射

                默认根级别的映射 mapState(['xxx']) - 详情可见上方的访问数据

computed:{
    ..mapState(['user'])
    //相当于把子模块的整个state进行了映射,想要访问子模块user.js的数据直接user.xxx
}

                 另一种:子模块的映射 mapState('模块名',['xxx']) - 需要开启命名空间

                 使用这个方法可以限定mapState只能访问某个模块的数据,如果在开发中有多个模块有相同的数据,那么这个方法可以很好的避免重复,那么这个方法需要开启命名空间,什么是命名空间?其实很简单,在子模块的导出项田间一个namespaced:true就可以了

//user模块
const state = {
    userInfo:{
        name:'张三',
        age:18
    },
    score:80
}
const mutations = {
    updataUser(state,newUser){
        state.userInfo = newUser
    }
}
const actions = {}
const getters = {}

export default {
    //开启命名空间
    namespaced:true
    state,
    mutations,
    actions,
    getters
}

在组件中使用

        

computed:{
    ...mapState('user',['userInfo','score'])
    //多个..mapState可以使用多次,不会报错正常写法
    //'user'为限定的子组件名称,['userInfo']为限定子组件的数据,可以写多个
}

        使用子模块的getters访问语句

        ①直接通过模块名访问$store.getters['模块名/xxx']

        ②通过mapGetters映射

                子模块的映射mapGetters('模块名',['xxx']) - 需要开启命名空间

        我先在user.js子模块中添加一个getters方法

//user模块
const state = {
    userInfo:{
        name:'zs',
        age:18
    },
    score:80
}
const mutations = {
    updataUser(state,newUser){
        state.userInfo = newUser
    }
}
const actions = {}
const getters = {
//分模块后,state指代子模块的state
//这里将userInfo中的姓名转为大写
    UpperCaseName(state){
        return state.userInfo.name.toUpperCase()
    }
}

export default {
    //开启命名空间
    namespaced:true
    state,
    mutations,
    actions,
    getters
}

在组件中通过原生的方法获取,也就是直接通过模块名访问

{{ $store.getters['user'/UpperCaseName] }}
//ZS
//成功将小写的zs转为大写的ZS

在组件中通过映射的方法实现(原理和mapState一样):

computed:{
    //在组件中映射
    ...mapGetters('user',['UpperCaseName'])
}
//在模板中直接使用
{{ UpperCaseName }}
//ZS

调用子模块中的mutations修改子模块的数据

         调用方式:

        ①直接通过store调用 $store.commit('模块名/xxx',额外参数)

        

//组件中
methods:{
    xxx(){
        this.$store.commit('模块名/actions方法名',参数)
    }
}

        ②通过mapMutations映射

                子模块的映射mapMutations('模块名',['xxx']) - 需要开启命名空间

methods:{
    ...mapActions('user',['xxx'])
}
//xxx为子模块中的方法名

<button @click="xxx(参数)">按钮</button>
//可以在组件中直接使用

        子模块的actions和mucations类似,可以直接类比使用!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值