Vue—Vuex

1、Vuex是什么?

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

它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

什么是状态管理模式?

下面是一个简单的 Vue 计数应用:

<template>
  <div id="app">
    <h2>{{count}}</h2>
    <button @click="count--">-</button>
    <button @click="count++">+</button>
  </div>
</template>

这个状态自管理应用包含以下几个部分:

  • state,组件的数据(count);
  • view,以声明方式将状态显示到视图(将count显示到网页);
  • actions,用户的操作导致的状态变化(-和+按钮)。

以上就是一个表示“单向数据流”的例子,简单示意图如下:

但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

  • 多个视图依赖于同一状态。
  • 来自不同视图的行为需要变更同一状态。

这时候,我们就需要一个“管家”来管理多个组件之间共享的状态。

当然我们可以自己封装一个对象来管理,但是Vuex提供的最大的便利就是响应式——当数据发生变化时,view能够实时的发生变化。

2、安装

NPM

npm install vuex --save

3、开始

在这里插入图片描述

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

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

最简单的Store

index.js

const store = new Vuex.Store({
  state: {
    count: 1000
  },
  mutations: {
    dedcrement(state) {
      state.count--
    },
    increment (state) {
      state.count++
    }
  }
})

App.vue

<template>
  <div id="app">
    <h2>{{$store.state.count}}</h2>
    <button @click="$store.commit('dedcrement')">-</button>
    <button @click="$store.commit('increment')">+</button>
  </div>
</template>

4、核心概念

1)、State

单一状态树

Vuex 使用单一状态树——用一个对象就包含了全部的状态。使它作为唯一的数据源而存在。

2)、Getter

getter 可以认为是 store 的计算属性。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

Getter 接受 state 作为其第一个参数:

index.js

const store = new Vuex.Store({
  state: {
    student: [
      {id:1, name: 'yanjundong', age: 18},
      {id:2, name: 'wangwu', age: 36},
      {id:3, name: 'li', age: 10},
      {id:4, name: 'zhangsan', age: 24}
    ]
  },
  getters:{
    greaterAgeStus: state => {
      return state.student.filter(s => s.age >= 20)
    }
  },
})

通过属性访问

Getter 会暴露为 store.getters 对象,你可以以属性的形式访问这些值:

$store.getters.greaterAgeStus	//	->	[ { "id": 2, "name": "wangwu", "age": 36 }, { "id": 4, "name": "zhangsan", "age": 24 } ]

Getter 也可以接受其他 getter 作为第二个参数:

const store = new Vuex.Store({
  state: {
    ...
  },
  getters:{
    ...
    greaterAgeCount: (state, getters) => {
      return getters.greaterAgeStus.length
    }
  },
})
$store.getters.greaterAgeCount	//	->	2

注意,getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。

通过方法访问

你也可以通过让 getter 返回一个函数,来实现给 getter 传参。

getters:{
    ...
    findByAge: state => {
      return (age) => {
        return state.student.filter(s => s.age >= age)
      }
    }
  },
$store.getters.findByAge(25)	//	->	[ { "id": 2, "name": "wangwu", "age": 36 } ]

3)、Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。

以上的开始中已经有了Mutation的简单应用

提交载荷(Payload)——携带参数

你可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload)

// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
store.commit('increment', 10)

在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读。

// ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})

另外一种提交方式

直接使用包含 type 属性的对象:

store.commit({
  type: 'increment',
  amount: 10
})

当使用对象风格的提交方式,整个的对象都作为参数传给mutations函数

mutations: {
  increment (state, payload) {	//payload:	{type: "increment", count: 10}
    state.count += payload.amount
  }
}

Mutation 需遵守 Vue 的响应规则

Vuex 的 store 中的状态是响应式的,当我们变更状态时,监视状态的 Vue 组件也会自动更新。

因此我们需要遵守Vue的一些规则:

  1. 最好提前在 store 中初始化好所有所需属性。

  2. 当需要给对象上添加新属性时,应该

    • 使用 Vue.set(obj, 'newProp', 123)
    • 用新对象给旧对象重新赋值
    state: {
      ...
      info: {
          name: 'iphone',
          version: '8plus',
          storage: '64G'
        }
    },
    mutations: {
        updateInfo(state) {
          //state.info.storage = '128G';  //响应式
          //state.info.price = '¥4300'; //做不到响应式
          Vue.set(state.info, 'price', '¥4300');  //响应式
    
          //delete state.info.storage;  //不能响应
          //Vue.delete(state.info, 'storage');  //响应式
        }
      }
    

使用常量替代 Mutation 事件类型

使用常量替代 mutation 事件类型,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})
//App.vue
import { SOME_MUTATION } from './mutation-types'
this.$store.commit(SOME_MUTATION);

Mutation 必须是同步函数

Vuex要求Mutation里的方法必须是同步方法,主要原因是:

  • devtools可以帮助我们捕捉mutation的快照
  • 然而, mutation 中的异步函数中的回调让这不可能完成

4)、Action

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

一个简单的 action:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    //在 action 内部可以执行异步操作
    incrementAsync (context) {	//context是与store 实例具有相同方法和属性的对象
    setTimeout(() => {
      context.commit('increment')
    }, 1000)
  }
  }
})

分发 Action

Action 通过 store.dispatch 方法触发:

this.$store.dispatch('increment')

载荷(Payload)

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

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

执行后回调

  1. 可以利用 Action 的载荷来实现——在参数中传递一个参数

  2. 利用promise实现

    //index.js,actions中的函数返回一个promise对象
    actions: {
        incrementAsync (context, message) {
          return new Promise(((resolve, reject) => {
            setTimeout(() => {
              console.log(message);
              context.commit('increment')
              resolve('成功了!');  //成功
            }, 1000)
          }))
        }
      }
    
    this.$store
              .dispatch('incrementAsync', '111')
              .then((msg) => {	//成功之后调用
                console.log(msg);
              })
    					.catch((msg) => {	//失败之后调用
                console.log(msg);
              })
    

    this.$store.dispatch('incrementAsync', '111') 执行之后就会把返回结果替代为自身。

5)、Module

因为使用了单一状态树,只允许有一个Store对象,但是当应用变得非常复杂时,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 的状态

模块的局部状态

const moduleA = {
  state: {
    name: 'zhangsan'
  },
  mutations: {
    updateName(state) { // 这里的 `state` 对象是模块的局部状态
      state.name = 'lisi';
    }
  },
  actions: {
    asyncUpdateName(context) {
      console.log(context);
      context.commit('updateName')
    }
  },
  getters: {
    fullName(state, getters, rootState) {
      return state.name + rootState.count;
    }
  }
}

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

$store.state.a.name
this.$store.commit('updateName')
this.$store.dispatch('asyncUpdateName')
this.$store.getters.fullName

5、项目结构

Vuex规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  3. 异步逻辑都应该封装到 action 里面。
├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值