Vue四、vuex是个啥?怎么用?

vuex官方文档

一、vuex是什么

官方解释是:Vuex是通过全局注入store对象,来实现组件间的状态共享,是一个专为 Vue.js 应用程序开发的状态管理模式

我的理解是,例如你的项目里某一个数据在前端多个组件中都有应用,如果一个改的话,那岂不是每个组件都需要改一次,特别是类似的数据多起来的话,操作起来想想就繁杂,于是,可以通过Vuex来实现组件间的状态共享,改一个,其他组件中的值的状态自动改变。

* 那么可能有人问:直接用全局对象不久可以了?

Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

* 那么可能有人问:Vuex与localStorage不一样么?

vuex 是 vue 的状态管理器,存储的数据是响应式的,但是并不会保存起来,刷新之后就回到了初始状态,但是localStorage是保存在浏览器中的,刷新之后还可以取出来继续使用。而且,vuex里,我们保存的一般都是数组,而localStorage保存到话只支持字符串

* 那么可能有人问:既然是存数据的,那什么时候用vuex,什么时候直接用简单的通信方式哩?

  • 如果项目足够简单(只是多个组件间传递数据),最好不要使用 Vuex。一个简单的 store 模式就足够所需了,只使用组件间常用的通信方法即可,使用 Vuex 可能是繁琐冗余的。

Vue组件简单常用的通信方式有以下几种:
1、父向子传值通过props的方式;
2、子向父传值通过events ($emit),实际上就是子组件把自己的数据发送到父组件;
3、父调用子方法通过ref;provide / inject。
4、兄弟之间通信通过bus
5、跨级嵌套通信可以使用bus;provide / inject等。
6、 vue组件间通信六种方式(完整版)

  • 如果需要构建一个中大型单页应用(多级组件嵌套),一个组件更改某个数据,多个组件自动获取更改后的数据进行业务逻辑处理,Vuex 将会成为自然而然的选择。
vuex的构成
  • 由上面官方给的图可以看出vuex由以下几部分构成
    1. State
    state是存储的单一状态,是存储的基本数据。对象必须是纯粹的对象 (含有零个或多个的 key/value 对)。它作为一个“唯一数据源 (SSOT)”而存在。
    2. Mutation
    更改 Vuex 的 store 中的状态的唯一方法是提交 mutation更改数据【必须是同步函数】。(使用store.commit方法更改state存储的状态)
    3. Action
    Action 类似于 mutation,不同在于:
    Action 提交的是 mutation,而不是直接变更状态。
    Action 可以包含任意【异步操作】。
    Action 通过 store.dispatch 方法触发,Mutation使用store.commit触发。
    4. Getter
    getters是store的计算属性,可以通过store 中的 state 中派生出一些状态(比如说过滤)。就像computed计算属性一样,getter返回的值会根据它的依赖被缓存起来,且只有当它的依赖值发生改变才会被重新计算。
    5. Module
    当应用变得复杂时,store对象可能变得相当臃肿复杂。为了解决这个问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
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的是怎么运行的?

  • vuex的运行流程
  1. 在组件内部,通过dispatch来分发action。
  2. 再通过action来第调用mutation
  3. 进而触发mutation内部的commit来修改state
  4. 最后state改变,导致页面重新render。

二、vuex怎么用

1.安装

NPM
npm install vuex --save
Yarn
yarn add vuex

2.中型数据不太复杂的项目中(直接使用)

其实使用cli工具初始化的项目中如果选择安装vuex的话就已经有了。如下:
src文件夹中新建一个store文件夹,下面新建一个名为index.js的文件。如下

src文件夹下

index.js中注册vuex,并且在state中初始化
一个count变量介绍mutations
一个tasks数组 + 一个taskFinish方法介绍getters
一个increment方法介绍actions

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: { 
      count:0,
      tasks: [
         { id: 1, finish: true },
         { id: 2, finish: false }
      ]
 },
  mutations: {
      // 更改 Vuex 的 store 中的状态的唯一方法
     countAdd (state) {
            // 自定义将传过来的参数操作操作
            state.count++
           }
  },
  getters: {
        // getters是store的计算属性, 有时候需要从  store 中的 state 中派生出一些状态(比如说过滤)
       taskFinish: state => {
              return state.tasks.filter(a=> a.finish)
    }
   },
  actions: { 
         // actions 提交的是 mutation,而不是直接变更状态.
        increment(context) {
            context.commit('countAdd')
          }
  },
  modules: {}
});

main.js中导入store实例,我们就可以通过this.$store.state访问这些状态,一般把它的值注入到computed

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";

// 阻止启动生产消息,常用作指令 ⬇
Vue.config.productionTip = false;

new Vue({
  router,
  store,   // 注册,全局使用vuex,(this.$store)
  render: h => h(App)
}).$mount("#app");

在后缀名为.vue的组件中使用

  • this.$store.state.count实现数据调用
  • this.$store.commit('countAdd')实现通过mutations实现数据修改(同步方式)
  • this.$store.getters.taskFinish实现通过getters实现 state 中的数据派生出一些状态(过滤数据)
<!-- 测试项目 -->
<template>
  <div>      
        <h3>{{this.$store.state.count}}</h3>
        <h4>{{this.$store.getters.taskFinish}}</h4>
        <input type="button" value="count自增"  @click="countAdd"></div>
        <button @click="increment">按钮</button>
</template>

<script>
export default {
  data() {
    return {};
  },

  components: {},

  computed: {},

  mounted() {},

  methods: {
       countAdd() {
            // commit不仅可以传state的参数,而且可以传额外的参数,只需在mutations里定义的函数后面的参数里与这里的一致就可以
            this.$store.commit('countAdd')
        },
       increment(){
            // 效果跟countAdd()是一样的
            this.$store.dispatch('increment')
        }
  }
};
</script>
<style>
</style>

* 看完例子之后可能有人说了,mutations和action效果不是一样的么,直接使用mutations不就行了,何必再用action做一次类似于请求转发的操作呢?

:实际上并非如此。还记得 mutation 必须同步执行这个限制么?Action 就不受约束!我们可以在 action 内部执行异步操作

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

Actions 支持同样的载荷方式对象方式进行分发:

// 以载荷形式分发
store.dispatch('incrementAsync', {
  amount: 10
})

// 以对象形式分发
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

当然这些方法只是简单的介绍理解一下vuex里面的东西,深入理解还需要自己实际写一下去探索。上面输出的格式可以简化,例如
this.$store.state.count放入computed计算属性当中去

export default {
 name: 'App',
  computed:{
       count(){
           return this.$store.state.count;
       }
   }
}

然后直接通过<h3>{{count}}</h3>调用即可

3.大型数据密集型项目中使用(需要将其划分为模块使用)

方法一:src文件夹中新建一个store文件夹,下面分别新建名为index.js / actions.js / getter.js / mutations.js的文件。

store文件夹下

方法一例子来自于vuex最详细完整的使用用法,让大家知道每个文件里的内容的格式

index.js


import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters' // 导入相应的模块,*相当于引入了这个组件下所有导出的事例,使用*和from关键字来实现的模块的继承
import * as actions from './actions'
import * as mutations from './mutations'
 
Vue.use(Vuex)
// 首先声明一个需要全局维护的状态 state,比如 我这里举例的resturantName
const state = {
    resturantName: '飞歌餐馆' // 默认值
    // id: xxx  如果还有全局状态也可以在这里添加
    // name:xxx
}
 
// 注册上面引入的各大模块
const store = new Vuex.Store({
    state,    // 共同维护的一个状态,state里面可以是很多个全局状态
    getters,  // 获取数据并渲染
    actions,  // 数据的异步操作
    mutations  // 处理数据的唯一途径,state的改变或赋值只能在这里
})
 
export default store  // 导出store并在 main.js中引用注册。

actions.js

// 给action注册事件处理函数。当这个函数被触发时候,将状态提交到mutations中处理
export function modifyAName({commit}, name) { // commit 提交;name即为点击后传递过来的参数,此时是 'A餐馆'
    return commit ('modifyAName', name)
}
export function modifyBName({commit}, name) {
    return commit ('modifyBName', name)
}
 
// ES6精简写法
// export const modifyAName = ({commit},name) => commit('modifyAName', name)

mutations.js

// 提交 mutations是更改Vuex状态的唯一合法方法
export const modifyAName = (state, name) => { // A组件点击更改餐馆名称为 A餐馆
    state.resturantName = name   // 把方法传递过来的参数,赋值给state中的resturantName
}
export const modifyBName = (state, name) => { // B组件点击更改餐馆名称为 B餐馆
    state.resturantName = name
}

getters.js

// 获取最终的状态信息
export const resturantName = state => state.resturantName

在后缀为.vue的文件中

像上面一样类似操作

方法二:新建一个名为modules的文件夹,modules文件夹下面分别新建自己对应需要的模块。

store文件夹下

每一个modules文件夹下的文件里

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

moudel.exports = moduleA 

index.js中注册使用的话


import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './modules/cart' // 导入相应的模块,使用*和from关键字来实现的模块的继承
import * as  ...
 
Vue.use(Vuex)

// 注册上面引入的各大模块
const store = new Vuex.Store({
   modules: {
    a: moduleA,
    ...
  } 
})
 
export default store  // 导出store并在 main.js中引用注册。

使用:

store.state.a.xx // -> moduleA 的状态

4.一些思考

(1)、vuex中的设计理念?为什么能实现各个组件间数据的监听?

(2)、vuex是如何实现在各个组件中,store里面的数据以响应的方式来实现实时跟新的?

(3)、vuex中的那些commit ,dispatch方法以及各种语法糖,更深层次是如何实现的?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351