Vuex 是什么?

Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。由于SPA应用的模块化,每个组件都有它各自的数据(state)、视图(view)和方法(actions),当项目内容越来越多时,每个组件中的状态就变得很难管理。Vuex 就是采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

1、单个组件中的状态

看一下官网提供的计数示例:

<template>
<div>
<button class="btn btn-success" @click="increment">increment</button>
view: {{count}}
</div>
</template> <script>
export default {
// state
data () {
return {
count: 0
}
},
// actions
methods: {
increment () {
this.count++
}
}
}
</script>

运行结果:

从效果图中可以直观的看到,每点击一次按钮触发添加事件(actions),数据count(state)就会发生改变,然后映射到视图界面(view)中。

下图可以表示 “ 单项数据流 ” 理念的极简示意:

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

• state:驱动应用的数据源

• view:以声明方式将 state 映射到视图

• actions:响应在 view 上的用户输入导致的状态变化

2、多个组件中的状态

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

• 多个视图依赖于同一状态

• 来自不同视图的行为需要变更同一状态

同样是计数器,我们现在更换一种场景,两个相同的组件A和B,共享一个数据count,并且都有一个方法可以操作这个count(是不是跟上面提到的多组件共享状态描述的一样呢)

// 组件A
<template>
<div>
{{ $store.state.count }}
<button @click="increment">组件A</button>
</div>
</template> <script>
export default {
methods: {
increment () {
this.$store.commit('increment')
}
}
}
</script> //组件B
<template>
<div>
{{ $store.state.count }}
<button @click="increment">组件B</button>
</div>
</template> <script>
export default {
methods: {
increment () {
this.$store.commit('increment')
}
}
}
</script>

运行效果:

从图中可以看到,“组件A” 和 “组件B” 两个按钮 会同时改变两个 count 的数据,因为数据源 count 和 方法increment 都是全局的。如下图所示,我们把 全局数据源 state改变数据源的方法 mutations异步操作方法 actions 提取出来放到 store 中,实现全局数据状态单独管理的功能

安装 

1、使用 npm 安装并保存到 package.json 中

npm install vuex --save

package.json

"dependencies": {
...,
...,
...,
"vuex": "^2.4.1"
},

2、配置

// 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)
import Vue from 'vue'
import Vuex from 'vuex' Vue.use(Vuex) //创建Store实例
const store = new Vuex.Store({
// 存储状态值
state: {
...
},
// 状态值的改变方法,操作状态值
// 提交mutations是更改Vuex状态的唯一方法
mutations: {
...
},
// 在store中定义getters(可以认为是store的计算属性)。Getters接收state作为其第一个函数
getters: {
...
},
actions: {
...
}
})
// 要改变状态值只能通过提交mutations来完成 /* eslint-disable no-new */
const app = new Vue({
router,
i18n,
// 将 store 实例注入到根组件下的所有子组件中,子组件通过 this.$store 来访问store
    store,
...App
}) app.$mount('#app')

看一下官网提供的例子:

<template>
<div>
<p>{{ count }}</p>
<p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</p>
</div>
</template> <script>
export default {
computed: {
count () {
// 通过 store.state 来获取状态对象
return this.$store.state.count
}
},
methods: {
increment () {
// 通过 store.commit 方法触发状态变更
this.$store.commit('increment')
},
decrement () {
this.$store.commit('decrement')
}
}
}
</script>
// 创建 Store 实例
const store = new Vuex.Store({
// 存储状态值
state: {
count: 0
},
// 状态值的改变方法,操作状态值
// 提交 mutations 是更改Vuex状态的唯一方法
mutations: {
increment: state => state.count++,
decrement: state => state.count--
}
})

运行效果:

核心概念

1、State

state 就是全局的状态(数据源),从前面的例子中看到我们可以按如下方式获取 Vuex 的state 状态

// html 中
{{ $store.state.count }} // js 中
this.$store.state.count

2、Getter

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

如下官网提供的案例:

computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}

如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它,然而这两种方法都不是很理想,最佳方式当然是使用 getter 了

我们尝试使用下getter

(1)、定义 getter

const store = new Vuex.Store({
state: {
count: 0
},
getters: {
formatMoney: state => {
return '¥'+state.count.toFixed(2)+'元'
}
},
mutations: {
increment: state => state.count++
}
})

(2)、在组件中引用 getter

export default {
methods: {
increment () {
this.$store.commit('increment')
// 这里为了更清楚的看到计算后的值
let aaa = document.getElementById('aaa')
let p = document.createElement('p')
p.innerHTML = this.$store.getters.formatMoney
aaa.appendChild(p)
}
},
computed: {
formatMoney() {
return this.$store.getters.formatMoney
}
}
}

效果:

3、Mutation

更改 Vuex 的 store 中的状态的唯一方法就是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型(type)和一个 回调函数(handler),这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})

要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法

store.commit('increment')

(1)、提交载荷(Payload)

载荷(payload)就是说 可以向 store.commit 传入额外的参数

// ...
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
})

4、Action

Vuex 中一条重要的原则就是 mutation 必须是同步函数, action 类似于 mutation,不同之处在于:

• Action 提交的是 mutation,而不是直接变更状态

• Action 可以包含任意异步操作

const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
},
// 异步
incrementAsync (context) {
// 延时1秒
setTimeout(() => {
context.commit('increment')
}, 1000)
}
}
})

Action 函数接受一个与 store 实例具有相同方法和属性的context对象,因此,可以有以下调用方法

• context.commit  提交一个 mutation

• context.state  获取 state

• context.getters   获取 getters

不同于 mutation 使用 commit 方法,action 使用 dispatch 方法

store.dispatch('increment')

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

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

5、Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象,当应用变得非常复杂时,store 对象就有可能变得非常臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成 模块(module),每个模块拥有自己的 state、mutation、getter、action,甚至是嵌套子模块 --- 从上至下进行同样方式的分割

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 的状态

关于项目结构,我们可以看看官网提供的示例:

├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块

官网同时也提供了一个 购物车 示例:

app.js 文件如下:

import 'babel-polyfill'
import Vue from 'vue'
import App from './components/App.vue'
import store from './store'
import { currency } from './currency' Vue.filter('currency', currency) new Vue({
el: '#app',
store,
render: h => h(App)
})

index.js 文件如下:

import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import * as getters from './getters'
import cart from './modules/cart'
import products from './modules/products'
import createLogger from '../../../src/plugins/logger' Vue.use(Vuex) const debug = process.env.NODE_ENV !== 'production' export default new Vuex.Store({
actions,
getters,
modules: {
cart,
products
},
strict: debug,
plugins: debug ? [createLogger()] : []
})

getters.js 文件如下:

export const cartProducts = state => {
return state.cart.added.map(({ id, quantity }) => {
const product = state.products.all.find(p => p.id === id)
return {
title: product.title,
price: product.price,
quantity
}
})
}

actions.js 文件如下:

import * as types from './mutation-types'

export const addToCart = ({ commit }, product) => {
if (product.inventory > 0) {
commit(types.ADD_TO_CART, {
id: product.id
})
}
}

mutation-type.js 文件如下:

export const ADD_TO_CART = 'ADD_TO_CART'
export const CHECKOUT_REQUEST = 'CHECKOUT_REQUEST'
export const CHECKOUT_SUCCESS = 'CHECKOUT_SUCCESS'
export const CHECKOUT_FAILURE = 'CHECKOUT_FAILURE'
export const RECEIVE_PRODUCTS = 'RECEIVE_PRODUCTS'

cart.js 文件如下:

import shop from '../../api/shop'
import * as types from '../mutation-types' // initial state
// shape: [{ id, quantity }]
const state = {
added: [],
checkoutStatus: null
} // getters
const getters = {
checkoutStatus: state => state.checkoutStatus
} // actions
const actions = {
checkout ({ commit, state }, products) {
const savedCartItems = [...state.added]
commit(types.CHECKOUT_REQUEST)
shop.buyProducts(
products,
() => commit(types.CHECKOUT_SUCCESS),
() => commit(types.CHECKOUT_FAILURE, { savedCartItems })
)
}
} // mutations
const mutations = {
[types.ADD_TO_CART] (state, { id }) {
state.lastCheckout = null
const record = state.added.find(p => p.id === id)
if (!record) {
state.added.push({
id,
quantity: 1
})
} else {
record.quantity++
}
}, [types.CHECKOUT_REQUEST] (state) {
// clear cart
state.added = []
state.checkoutStatus = null
}, [types.CHECKOUT_SUCCESS] (state) {
state.checkoutStatus = 'successful'
}, [types.CHECKOUT_FAILURE] (state, { savedCartItems }) {
// rollback to the cart saved before sending the request
state.added = savedCartItems
state.checkoutStatus = 'failed'
}
} export default {
state,
getters,
actions,
mutations
}

products.js 文件如下:

import shop from '../../api/shop'
import * as types from '../mutation-types' // initial state
const state = {
all: []
} // getters
const getters = {
allProducts: state => state.all
} // actions
const actions = {
getAllProducts ({ commit }) {
shop.getProducts(products => {
commit(types.RECEIVE_PRODUCTS, { products })
})
}
} // mutations
const mutations = {
[types.RECEIVE_PRODUCTS] (state, { products }) {
state.all = products
}, [types.ADD_TO_CART] (state, { id }) {
state.all.find(p => p.id === id).inventory--
}
} export default {
state,
getters,
actions,
mutations
}

购物车运行效果:

Vuex 学习笔记的更多相关文章

  1. vuex学习笔记

    一.vuex的目的 把组件的共享状态抽取出来,以一个全局单例模式管理.在这种模式下,组件树构成了一个巨大的视图,不管在树的哪个位置,任何组件都能获取状态或触发行为. 二.vuex集中式管理数据 安装 ...

  2. Vuex学习笔记(-)安装vuex

    什么是Vuex? vuex是一个专门为vue.js应用程序开发的状态管理模式.即data中属性同时有一个或几个组件同时使用,就是data中共用的属性. 安装vuex(前提是已经安装好vue-cli脚手 ...

  3. Vuex 学习笔记一

    一.定义 Vuex是一个专为Vue.js应用程序开发的状态管理模式. 状态管理模式 简单的demo new Vue({ // state data () { return { count: 0 } } ...

  4. Vue学习—— Vuex学习笔记

    组件是Vue最强大的功能之一,而组件实例的作用域是相互独立的,意味着不同组件之间的数据是无法相互使用.组件间如何传递数据就显得至关重要,这篇文章主要是介绍Vuex.尽量以通俗易懂的实例讲述这其中的差别 ...

  5. 【整理】解决vue不相关组件之间的数据传递----vuex的学习笔记,解决报错this.$store.commit is not a function

    解决vue不相关组件之间的数据传递----vuex的学习笔记,解决报错this.$store.commit is not a function https://www.cnblogs.com/jaso ...

  6. 【Vue学习笔记】—— vuex的语法 { }

    学习笔记 作者:o_Ming vuex Vuex ++ state ++ (用于存储全局数据) 组件访问 state 中的全局数据的方式1: this.$store.state.全局数据 组件访问 s ...

  7. 7 种 Javascript 常用设计模式学习笔记

    7 种 Javascript 常用设计模式学习笔记 由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用. 有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式 本文的 ...

  8. vuex学习详细解(主页目录

    学习vuex过程中,通过 vue-cli命令来配置和使用vuex笔记整理 vue-cli中配置vuex流程和注意事项 vuex目录配置 vuex的states.js vuex的getters.js v ...

  9. Vue学习笔记-Vue.js-2.X 学习(四)===>脚手架Vue-CLI(基本工作和创建)

    (五) 脚手架Vue-CLI 一 Vue-CLI前提(nodejs和webpack) 二  Vue学习-nodejs按装配置,Node.js 就是运行在服务端的 JavaScript. 1. 去nod ...

随机推荐

  1. Django连接mysql数据库

    1.app中对应的models.py配置相关表结构信息 from django.db import models class Question(models.Model): question_text ...

  2. C++类静态成员与类静态成员函数

       当将类的某个数据成员声明为static时,该静态数据成员只能被定义一次,而且要被同类的所有对象共享.各个对象都拥有类中每一个普通数据成员的副本,但静态数据成员只有一个实例存在,与定义了多少类对象 ...

  3. 前端开发【第2篇:CSS】

    鸡血 样式的属性多达几千个,但别担心,按照80-20原则,常用的也就几十个,你完全可以掌握它. Css初识 HTML的诞生 早期只有HTML的时候为了让HTML更美观一点,当时页面的开发者会把颜色写到 ...

  4. 详解session

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp30 一.术语session 在我的经验里,session这个词被滥用的程度 ...

  5. 第1阶段——uboot分析之启动函数bootm命令 (9)

    本节主要学习: 详细分析UBOOT中"bootcmd=nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0"中怎么实现bootm命令 ...

  6. Linux-tar命令(2)

    tar 用于可以为文件和目录创建档案.可以为某一特定文件打包(备份文件), 也可以在档案中改变文件,或者向档案中加入新的文件. 主选参数: 主选参数仅能存在一个!不可同时存在!因为不可能同时压缩与解压 ...

  7. Android学习记录:Paint,Canvas和Bitmap

    在Java中,利用过双缓冲技术,先将画笔画在内存上,再转化为图片,调出来. 当画的东西过多造成处理不过来时,双缓冲技术将防止闪屏. 在Paint方法下,我们这样写: BufferedImage tmp ...

  8. 使用imageLoader加载图片资源

  9. 从web图片裁剪出发:了解H5中的canvas

    本篇内容不针对canvas文档对每个api进行逐个的详解! 本篇内容不针对canvas文档对每个api进行逐个的详解! 本篇内容不针对canvas文档对每个api进行逐个的详解! 重说三,好了,现在进 ...

  10. Java Object中的equals和hashCode

    Java的Object对象中有两个方法比较有意思,一个是equals(),一个是hashCode(),那么这两个的作用有些同学可能还不是很清楚,那么同学们现在就进一步了解一下吧. 下面咱们写一个简单的 ...