Vuex基础使用和双向绑定

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。

1. Vuex介绍

Vuex是官方的状态管理工具,主要概念有State、Getter、Mutation、Action、Module。
文章的开头说过Vue的组件都是一个个Vue实例,Vuex也可以这样来看,具体对比关系如下:

Vuex 主要作用 Vue组件
State 用来保存状态 相当于data属性
Getter 用来对属性进行组合修改 相当于计算属性
Mutation 常用来直接改变State的值 相当于methods
Action 主要用于提交Mutations,并且可以处理异步 -
Module 用于模块化状态管理 模块化Vue组件

使用Vuex,我们用State保存状态,使用Getter来对状态数据进行处理,使用Mutation来直接改变State的值,用Action来提交Mutation,Action不是另类的Mutation,他操作的是Mutation中的函数,按照规范,Action是不能直接修改State的,虽然它可以修改State,如果需要返回一个状态或者一个异步的回调,比如在Actions里面进行了http请求,可以直接返回一个Promise,使用Module,来对庞大的项目进行分组。

2. 对Vuex中State的获取

一般情况下都会把Vuex全局注入到Vue中,这时每一个Vue实例都能访问到,一般都会使用计算属性来接受Vuex的值,代码如下:

//定义一个简单的Vuex
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);

export default new Vuex.Store({
  state: {
      Title: "123",
  },
  mutations: {
    changeTitle(state, newTitle) {
      state.Title= newTitle;
    },
  },
});

//组件中使用
<template>
<div class="test">{{NewState}}</div>
</template>
<script>
export default {
  computed: {
  NewState(){
    return this.$store.state.Title
  }
  },
//其他必要代码
}
</script>

这样就能够在页面上显示Vuex中的Title值了。

除此之外还有一个辅助函数,在本例子中代码改为:

//组件中使用
<template>
<div class="test">{{Title}}</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
  computed: {
 ...mapState(['Title']),
  },
//其他必要代码
}
</script>

3. 对Vuex中State的改变

在上述Vuex例子中已经声明了Mutation,只需要把代码改为:

//组件中使用
<template>
<input type="text" v-model="newTitle">
<div class="test">{{Title}}</div>
<button @click="changeTitle">改变Vuex中的title</button>
</template>
<script>
import { mapState } from 'vuex'
export default {
  data(){
  return{
  newTitle:"",
  } },
  computed: {
 ...mapState(['Title']),
  },
 methods:{
 chageTitle(){
  this.$store.commit('changeTitle',this.newTitle)
 },
},
//其他必要代码
}
</script>
4.gif

上述代码主要是添加了一个输入框和一个按钮,输入框输入数据后,点击改变按钮,就会触发一个点击事件,这个点击事件中出发函数,函数调用Vuex中Mutation中的方法changeTitle,并传过去了文本框中的值,Mutation中的changeTitle则改变了State中的Title的值,因为组件中获取Vuex的值使用计算属性,因此也会同步到页面。

当然Mutation也有辅助函数,只需在上面修改代码如下:

//组件中使用
<template>
<input type="text" v-model="newTitle">
<div class="test">{{Title}}</div>
<button @click="changeTitles">改变Vuex中的title</button>
</template>
<script>
import { mapState,mapMutations } from 'vuex'
export default {
  data(){
  return{
  newTitle:"",
  } },
  computed: {
 ...mapState(['Title']),
  },
 methods:{
    ...mapMutations(['changeTitle']),
    changeTitles() {
      this.changeTitle(this.newTitle)
    },
    // changeTitle() {
    //   this.$store.commit('changeTitle', this.newTitle)
    // },
},
//其他必要代码
}
</script>

4. Vuex中State的双向绑定

Vuex的State的双向绑定通常用在 form表单,当然也有其他需要双向绑定的情况。
本小节主要应用技术是Vue计算属性的get方法和set方法,对上面例子修改后,代码如下:

export default {
 computed: {
    newTitle: {
      get() {
        return this.$store.state.Title;
      },
      set(value) {
        this.$store.commit('changeTitle', value)
      }
    }
  },
}

把提交修改的事件添加到计算属性的set方法中,就可以在值改变的时候触发修改操作,从而改变State的值,这个操作在form表单中是可以行得通的,因为表单一般用v-model绑定数据,而v-model是一个语法糖,它内部拥有input事件,可以触发计算属性的set方法,而不用v-model的时候则不能触发计算属性的set方法,也就无法实现双向绑定,这时候需要使用监听器监听newTitle值的变化,从而触发改变State的Mutation方法,代码如下:

export default {
 computed: {
    newTitle(){
      return this.$store.state.Title;
    }
  },
 watch:{
   newTitle(newd,oldd){
     this.$store.commit('changeTitle', newd)
   }
 }
}

因为监听器是在数据发生变化时执行的,所以能够解决非v-model指令不能触发set方法的问题。

5. Vuex中对象属性的深层监听

本小节是对上个小节的一点拓展。
在对Vuex的日常使用中,一般都不会一个属性设置一个值,一般都会进行分组,而模块过大的时候就会启用Vuex的Module,在一个设置配置的Vuex中,一般有如此配置:

//系统设置
    config: {
      events: true,
      calls: false,
      messages: false,
      notifications: false,
      sounds: false,
      videoSounds: false
    }

我们在组件中使用则会这样用:

<script>
import { mapState } from 'vuex'
export default {
  name: 'Drawer',
  data() {
    return {
      docked: false,
      position: 'left'
    }
  },
  computed: {
      ...mapState(['config'])
   }
}
</script>

在组件中就会使用v-model="config.events"来使用,这明显是使用了v-model的对象赋值,但是计算属性并不会检测内部的变化,从而触发set方法,去提交对State修改的Mutation方法。
使用Vue的监听器设置deep:true,监听器默认也不会监听内部变化,设置deeptrue可以监听内部变化,代码如下:

<script>
import { mapState } from 'vuex'
export default {
 computed: {
     ...mapState(['config']),
   },
  watch: {
    config: {
      handler(newValue, oldValue) {
        this.$store.commit('changeConfig', newValue)
      },
      deep: true
    },
  },
}
</script>

这样就对config的值进行了深层双向绑定,而不是重复书写这几个配置属性的getset方法

6. Getter

前文提到过,Getter就像计算属性。比如一个列表属性,有时我们只需要其中一条数据我们可以这样做:

export default new Vuex.Store({
  state: {
    list: [
      { id: 1, text: '我是1'},
      { id: 2, text: '我是2'},
      { id: 3, text: '我是3'}
    ]
  },
  getters: {
    getone: state => {
      return state.list.filter(id=> id===1)
    }
  }
})

这样调用时,就只会返回第一条数据。
在组件中使用:

computed: {
 getOnes () {
    return this.$store.getters.getone
  }
}

值得注意的是,虽然Vuex中Geeter类似于组件中的计算属性,但是Getter并没有像计算属性那样混入实例(按照计算属性类比的话,Vuex调用Getter需要使用this.$store.state.getone),但是Getter调用还是要this.$store.getters.getone

同样可以使用助手函数:

computed: {
    ...mapGetters([
      'getone'
    ])
  }

7. Action

Action是Vuex中的异步调用解决方法,因为Mutation只能使用同步方法,Action操作的是Mutation中的函数,下面有一个例子:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add(state) {
      state.count+=1
    }
  },
  actions: {
    increment (context) {
      context.commit('add')
    }
  }
})

在使用的时候可以这样操作:

export default {
 methods:{
 addCount(){
  this.$store.dispatch('add')
 },
},
//其他必要代码
}

同样可以使用助手函数:

import { mapActions } from 'vuex'
export default {
 methods:{
 ...mapActions(['add']),
},
//其他必要代码
}

如果需要在Action中传递值需要绕一下,代码如下:

import { mapActions } from 'vuex'
export default {
 data(){
  return{
    value:'',
  };
 },
methods: {
    changeTitles() {
      this.$store.dispatch('change',value)
    },
//其他必要代码
}
//vuex
export default new Vuex.Store({
  state: {
    Title: "test",
  },
  mutations: {
    changevalue(state, newTitle) {
      state.Title = newTitle;
    }
  },
  actions: {
    change(context, value) {
      context.commit("changeTitle", value);
    }
  },
});

Action和Mutation类似,都是第二个值才是传入的值,因为Action是对Mutation的操作,所以传值需要多绕一下,但是还是可以达到预期效果的。

8. Module

当你的系统非常庞大时,把不同模块的State和Mutation写在一起会非常的乱,因此Vuex还提供了模块化。

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

上面代码是引用Vuex官方文档的代码,只需要在引用方法和操作时加上模块名,就可以正确读取到相应模块的值和方法。

操作方法 非模块化 模块化
获取State this.$store.state.value; this.$store.state.[模块名].value
mapState ...mapState(['value]) ...mapState(['[模块名]/value'])
Mutation this.$store.commit('event') this.$store.[模块名].commit('event')
mapMutation ...mapMutation(['event]) ...mapMutation(['[模块名]/event'])

Action和Getter类似。

使用webpack批量导入Modules

require.context是webpack的一个用来管理依赖的一个函数,使用它可以批量导入,详细查看文档
https://webpack.docschina.org/guides/dependency-management/#require-context

具体使用:

import Vue from "vue";
import Vuex from "vuex";
import getters from "./getters";
Vue.use(Vuex);

// 引入modules下的所有文件
const modulesFiles = require.context("./modules", false, /\.js$/);

const modules = modulesFiles.keys().reduce((modules, path) => {
  // ./app.js => app
  const name = path.replace(/^\.\/(.*)\.\w+$/, "$1");
  // 如果文件是空的则,下面这句取不出来结果
  const value = modulesFiles(path);
  modules[name] = value.default;
  return modules;
}, {});
const store = new Vuex.Store({
  modules,
  getters
});
export default store;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容