最近花了两周时间,完完全全的跟着Vuex官方的视频学完了Vuex并且详详细细的做了笔记,其中总结部分是我对于整个视频课程的总结,视频部分是跟着视频做的笔记,如果总结部分有不懂的话,直接去视频部分查找对应的笔记即可(笔记比总结更详细)。
本内容来自Vue官方推荐课程,随堂代码可以在连接中找到,当然还是建议跟着手敲一遍。
随着文章的越来越长,我的编辑器都卡顿起来了,编辑此博客时,更是觉得浏览器遇到了莫大的挑战,导出PDF总共48页,创作不易,求个关注!
文章目录
- Vuex使用规范(总结)
- 安装/配置 Vuex
- 安装 Vuex
- 配置 Vuex
- Vuex 简介
- Vuex 4种方法 + 1种分类方式
- 调用Vuex中的内容
- 综合 | 各种方法调用
- 示例 1 | 调用 State
- 示例 2 | 调用 Getter
- 示例 3 | 动态Getter(让Getter可以接收参数)
- 示例 4 | 调用 Mutations
- 示例 5 | 调用 Actions
- 使用 Module 分组
- 示例 1 | 使用Module进行分组
- 1. 调用同一个Module中的State
- 2. 在Vue组件中调用state
- 3. 在moduleB中调用ModuleA中的state
- 使用 namespaced 对getters等三要素进行分组
- Mutations
- ModuleB中 调用 ModuleA
- 在组件中调用
- Actions
- 组件化使用
- VuexMapHelper 简介
- 视频课程(笔记)
- 1. 初始化项目 并 添加Mock数据
- 初始化项目
- 添加 Mock 数据
- 创建商品列表组件
- 代码解释
- 补充知识 | Callback 回调函数
- 2. 初步使用 Vuex
- 安装Vuex
- 创建 store/index.js
- 3. 简单的使用
- 1. 示例 1 | 设置值 与 获取值
- 3. Vuex | Getter
- 4. Vuex | Action
- 示例 1 | Action 调用 state
- 示例 2 | Action 调用 mutations
- 示例 3 | 在项目中使用 Action
- 补充 1 | 在Action中使用`{commit}`拆解对象
- 示例 4 | 通过 new Promise来实现异步调用结果
- 5. 全局注册store
- 6. 商品添加至购物车
- 1. 创建新的存储对象
- 7. 通过 Vue DevTools 查看 Vuex 的内容
- 8. 创建 ShoppingCard 组件
- 1. 引入`currency.js`
- 2. 创建`components/ShoppingCart.vue`组件
- 3. 在`App.vue`中引入组件并使用
- 4. 最终页面效果如下
- 9. 结账功能
- 10. 动态 getter | getter 接收参数
- 需求实现 | 商品数量为0后不可继续下单
- 需求优化 | 使用动态Getter
- 解释说明
- 动态Getter总结 | 让不可以接收参数的Getter方法变得可以接收参数
- 补充 | 在其他地方使用这个可接受参数的Getter
- 11. 使用 VuexMapHelper 减少代码
- 使用`mapState`前后代码量的对比
- 示例 1 | 简单使用 state
- 示例 2 | 重命名state
- 示例 3 | 使用方法传入state
- 示例 4 | 示例3的改进-配合组件的变量返回数组中某个具体位置的值
- 示例 5 | 同时使用多个 VuexMapHelper 的方式
- 示例 5.1 关于使用多个 VuexMapHelper 的简单说明
- 示例 5.2 | 探索 ES7 Object Spread Operator
- 示例 5.3 对项目中代码进行改造
- 12. 组件管理 1 |将各个组件单独存储
- 13. 组件管理 2 | Vuex Modules 1
- Modules 准备工作(代码)
- Modules.State 注意事项 1 | 在组件中的使用
- Modules.State 注意事项 2 | 在Store中使用
- rootState | 在Getters中使用
- rootState | 在Action中使用
- 13. 组件管理 3 | Vuex Modules - NameSpaceSpace
- 示例 1 | ModuleA 中调用 ModuleB 中的Getters示例
- 示例 2 | 在组件中调用Module中的方法
- 思考 | 在一个组件中调用两个Modules中的Getter方法
- 示例 3 | 在组件中调用Actions方法
- 示例 4 | 在ModuleA中调用ModuleB中的Actions与Mutations
Vuex使用规范(总结)
安装/配置 Vuex
安装 Vuex
使用下面的命令,可以在安装vuex
npm install vuex@version --save
如果不确定当前Vue应该使用的Vuex版本,可以执行下面命令让框架自己决定安装的版本。
npm install vue --save
配置 Vuex
创建store/index.html
文件,内容如下:
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
}
})
在main.js
中全局定义store,代码如下:
import Vue from 'vue'
import App from './App'
import {currency} from "@/currency";
Vue.config.productionTip = false
Vue.filter('currency',currency)
// 导入store
import store from '@/store/index'
new Vue({
el: '#app',
// 使用store
store,
render: h => h(App)
})
Vuex 简介
Vuex 4种方法 + 1种分类方式
- State 单一状态树,可以理解成是数据
- Getter 可以理解成是store的计算属性
- Mutation 用于更改数据(可以是set/update等)
- Action 类似于 mutation,但是它提交的是mutation,而不是直接变更状态,Action可以包含任意的异步操作
- Module 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决该问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
state/getters一般作为值来使用,一般放在
computed
内,使用计算属性作为“值”来用
mutations/actions可以传参,一般作为方法来使用,放在methods
中
调用Vuex中的内容
综合 | 各种方法调用
类型 | 调用方式 | 备注 |
---|---|---|
State | store.state.varName | 一般用在计算属性中作为一个“值”来使用 |
Getter | store.getter.methodName | 一般用在计算属性中作为一个“值”来使用 |
Mutation | store.commit('methodName',参数) | 一般作为方法来使用 |
Action | store.dispatch('methodName',参数) | 一般作为方法来使用 |
示例 1 | 调用 State
在组件中调用
computed: mapState(['products'])
computed: mapState({
allProduct: 'products'
})
computed: mapState({
// 使用匿名方法 返回第一个值
firstProduct1: state => state.products[0],
// 使用具名方法 返回第一个值
firstProduct2(state) {
return state.products[0]
}
})
示例 2 | 调用 Getter
computed: {
mapGetter(['xxx'])
}
computed: mapGetter({
allProduct: 'products'
})
computed: {
total() {
return store.getters.cartTotal
},
}
示例 3 | 动态Getter(让Getter可以接收参数)
Getter 默认不能接收参数,但是我们可以通过让Getter返回一个匿名方法的方式,给Getter 传递一个参数。
Vuex中的Getter代码如下:
productIsInStock() {
return (product) => {
return product.inventory > 0
}
}
在组件中调用并使用:
computed: {
// 注意这是在computed中
productIsInStock() {
return store.getters.productIsInStock
}
}
或者可以在方法中进行注册:
methods: {
// 注意这里是methods中
productIsInStock(product) {
return store.getters.productIsInStock(product)
}
}
随后调用
productIsInStock(参数)
即可
示例 4 | 调用 Mutations
store.commit('methodName',参数)
示例 5 | 调用 Actions
store.dispatch('methodName',参数)
使用 Module 分组
如果有非常多的不同业务的Vuex状态需要管理的话,将它们全部放在index.js中代码将会十分冗余,此时我们可以考虑将代码按照业务进行划分,将功能A的state/getters/mutations/getters放进ModuleA文件中,将B放入ModuleB中,以此来明确代码逻辑。
使用Module分组后,State会直接分组,此时要是调用state需要再前面加上Module的名称。
示例 1 | 使用Module进行分组
我们创建store/modules/moduleA.js
文件,代码如下:
export default {
state: {
// 仅做示例
items: []
},
getters: {
// ...
},
mutations: {
// ...
},
actions: {
// ...
}
}
随后,在store/index.js
中引入该Module,代码如下:
import Vuex from 'vuex'
import Vue from 'vue'
// 引入
import moduleA from "./modules/moduleA";
Vue.use(Vuex)
Vue.config.devtools = true
export default new Vuex.Store({
// 使用
modules: {
moduleA
}
})
如此一来,我们就定义了一个名为
moduleA
的分组。
正如上文所说,此时的State将会被分组,原本代表根节点的
state
此时会变为代表此文件。此时调用State分为三种情况,如下:
1. 调用同一个Module中的State
调用同一个Module中的State(如ModuleA中的getters方法调用ModuleA的state),此时与之前无异,不会发生改变:
store.state.items
2. 在Vue组件中调用state
在Vue组件中调用state,此时需要再state前面加上Module的名称,以上面写的示例Module为例的话,代码如下:
store.moduleA.state.items
3. 在moduleB中调用ModuleA中的state
在moduleB中调用ModuleA中的state,此时需要使用到一个名为rootState
的节点,且又分为三种情况,分别如下:
rootState简介:上面说到在没有进行分组的时候,我们可以使用
state
获取所有节点上的state(因为总共也就只有一个节点),但是分组后,state
仅能代表当前文件下的所有state,因此Vuex又引入了rootState
,代表根节点。
- 在Getter中调用
在Getter中使用其他Module中的state,首先我们需要传入至少三个参数,第三个参数为rootState,调用方式为
rootState.moduleName.varName
,以以上代码为示例的话,应该是rootState.moduleA.items
。
getters: {
getterName(state,getter,rootState) {
rootState.moduleA.items
}
},
- 在Mutations中调用
无法获取
- 在Actions中调用
在Actions中调用其他Module中的state,我们应该传入四个参数,并且第四个参数为rootState。
调用方式与上面类似,如下:
actions: {
actionMethodName({state, getters, commit, rootState}, varName){
rootState.moduleA.items
}
}
使用 namespaced 对getters等三要素进行分组
在上面的实例中,我们演示了使用module将不同业务的Vuex代码进行分组,但是上述代码中说明了以上代码只能对State分组,Getters/Mutations/Actions无法进行分组,此时在不同的module模块中,出现了两个名称相同的actions方法,那么我们在组件中调用的时候,这两个actions方法都将会被执行,这对于大型项目来说(多人协作开发)是非常危险的,因此我们可以使用
namespaced
对这三个部分也进行分组。
使用方式:在以上代码的基础上(使用Module进行分组的基础上),我们加入
namespaced: true
可以对此三种类型进行分组。
示例代码如下:
export default {
// 启用getters/mutations/actions方法分组
namespaced: true,
state: {
items: []
},
getters: {
get1() {}
},
mutations: {
// ...
},
actions: {
// ...
}
}
此时,State/Getter/Mutation/Action都被分组了。
Mutations
ModuleB中 调用 ModuleA
以在ModuleB的Actions中 调用MuduleA中的Mutations为例。
moduleB.js
文件内容如下:
export default {
actions: {
actionMethodName({state, getters, commit}) {
commit('moduleA/mutationName', variable, {root: true})
}
}
}
以上代码中:
- 通过moduleA/nutationName来指定调用moduleA中的方法
- commit表示是调用mutations
{root: true}
表示从根路径开始查找,作用等同于调用state时的rootState
这三者组合起来,才能在一个module中调用另一个module中的mutations方法
moduleA.js
中文件内容如下:
export default {
mutations: {
mutationName (state, variable) {
// 对variable的操作
}
}
}
在组件中调用
分组之后,在组件中调用Module如下即可:
this.$store.commit("moduleA/methodName",参数);
或者,可以先使用mapMutations导入之后再当做方法进行调用。
以下代码为.vue
组件中的代码:
export default {
methods: {
...mapMutations('moduleA',{
methed1: 'method1',
method2: 'method2'
}),
...mapMutations('moduleB',{
methed3: 'method3'
})
}
}
或者如下这么写:
export default {
methods: {
...mapMutations({
methed1: 'moduleA/method1',
method2: 'moduleA/method2',
methed3: 'moduleB/method3'
})
}
}
Actions
Actions分组后,在组件中的调用方式如下:
methods: {
...mapActions('moduleA', ['methodName'])
}
或
methods: {
...mapActions(['moduleA/methodName'])
}
组件化使用
除了使用Modules按照功能对Vuex代码进行分组之外,我们还可以按照state/getters/mutations/actions进行分组(以actions为例)。
首先,创建
store/actions.js
文件,将store/index.js
中actions的代码全部粘贴进去,如下:
actions.js文件:
export default { // = methods
method1({commit}) {
},
method2({state,getters,commit},product) {
},
method3({state,commit}) {
}
}
随后,我们在store/index.js
中引入该文件,代码如下:
import Vuex from 'vuex'
import Vue from 'vue'
import actions from "./actions";
export default new Vuex.Store({
state: {
},
getters: {
},
// 此处使用我们在actions.js中定义的actions方法
actions,
mutations: {
}
})
以上代码即将actions方法单独引入到一个文件中,这样分模块划分代码可以让index.js中的代码更清晰,但是使用起来与不分组没有区别(不需要像module那样
组名/方法名
,一切还挂在“根”上)。除了在actions.js文件中写actions方法,我们还可以再index.js中写actions方法,类似于特例与common的区别,使用起来没有任何不同。
要定义states,只需要创建states.js文件并export,随后在index.js中import并使用即可。
VuexMapHelper 简介
在上文中我们经常看到
...mapState({})
此类代码,这是为了让Vuex的引入更方便,减少我们的代码量,此种引用便是VuexMapHelper。
关于VuexMapHelper,下文有详细介绍,可以点此查看
视频课程(笔记)
🎥视频地址
1. 初始化项目 并 添加Mock数据
初始化项目
vue init vueschool/webpack-template shopping-cart
cd shopping-cart
yarn install
yarn dev
添加 Mock 数据
创建项目后,我们创建一个
api/shop.js
文件,代码可以如下 (也可以点此获取):
/**
* Mocking client-server processing
*/
const _products = [
{ 'id': 1, 'title': 'iPad 4 Mini', 'price': 500.01, 'inventory': 2 },
{ 'id': 2, 'title': 'H&M T-Shirt White', 'price': 10.99, 'inventory': 10 },
{ 'id': 3, 'title': 'Charli XCX - Sucker CD', 'price': 19.99, 'inventory': 5 }
]
export default {
getProducts (cb) {
setTimeout(() => cb(_products), 100)
},
buyProducts (products, cb, errorCb) {
setTimeout(() => {
// simulate random checkout failure.
(Math.random() > 0.5 || navigator.webdriver)
? cb()
: errorCb()
}, 100)
}
}
创建商品列表组件
创建
components/ProductList.vue
文件,并添加以下代码:
<template>
<div>
<h1>Product List</h1>
<ul>
<li v-for="product in products">{{product.title}} - {{product.price}}</li>
</ul>
</div>
</template>
<script>
import shop from '@/api/shop'
export default {
data() {
return {
products: []
}
},
created() {
shop.getProducts(products => {
this.products = products;
})
}
}
</script>
代码解释
我们的mock数据中有一个
getProducts (cb)
方法,该方法的参数cd
是一个函数,当我们调用该方法的时候,需要传递一个函数进去,该函数会调用一个定时函数,并在100ms之后将mock数据作为参数传递给该方法。
getProducts (cb) {
setTimeout(() => cb(_products), 100)
},
我们在
ProductList.vue
中调用该方法,传递一个箭头函数进去,该箭头函数的参数是products
,该方法传递到mock方法之后,如上所述,mock中的方法会将假数据作为参数放入该方法中,此时该箭头函数的参数就是我们创造的假数据,该箭头函数会将参数赋值给本地的参数,也就是this.products
。
shop.getProducts(products => {
this.products = products;
})
这样一来,我们就通过传递函数的方式,将伪造的数据传递到了组件中去,这样的方式称为回调函数(Callback)。
补充知识 | Callback 回调函数
阅读资料
2. 初步使用 Vuex
视频地址
安装Vuex
yarn add vuex
创建 store/index.js
我们将创建一个用于存放项目状态的store,代码如下:
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
new Vuex.Store({
state: { // = data
products: []
},
getters: { // = computed properties
productsCount() {
}
},
actions: {
fetchProducts() {
// make the call
}
},
mutations: {
setProducts() {
// update product
}
}
})
Vuex 包含5种状态:
- State 单一状态树,可以理解成是数据
- Getter 可以理解成是store的计算属性
- Mutation 用于更改数据(可以是set/update等)
- Action 类似于 mutation,但是它提交的是mutation,而不是直接变更状态,Action可以包含任意的异步操作
- Module 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决该问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
我们可以在actions
中写Ajax请求的方法(异步),在mutations
中定义更新变量状态的方法。
3. 简单的使用
1. 示例 1 | 设置值 与 获取值
我们丰富一下示例代码,如下:
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export default new Vuex.Store({
state: { // = data
products: []
},
getters: { // = computed properties
productsCount() {
}
},
actions: {
fetchProducts() {
// make the call
}
},
mutations: {
setProducts(state,products) {
// update product
state.products = products
}
}
})
以上代码中,我们在store中创建了一个
products
,并且在mutations
中创建了setProducts
方法用以设置其值。
随后我们可以在Vue组件中使用它们,我们可以通过
store.state.products
的方式调用其值,也可以通过store.commit('setProducts',products)
的方式修改其值,示例代码如下:
<script>
import shop from '@/api/shop'
import store from '@/store/index'
export default {
computed: {
products() {
return store.state.products
}
},
created() {
shop.getProducts(products => {
store.commit('setProducts',products)
})
}
}
</script>
以上代码中,我们在create中调用了
shop.js
中的回调函数,通过store.commit('mutations方法名',参数)
的方式将mock数据存储到Vuex中去;随后我们通过计算属性,将
store.state.products
(Vuex中products)的值重命名为products
以方便使用。
3. Vuex | Getter
视频地址
在之前的文章中我们使用
store.state.products
获取了所有的商品,现在我们稍微修改下代码,我们通过Vuex的getters
,获取所有的库存大于0的商品,getters代码如下:
getters: { // = computed properties
availableProducts(state,getters) {
return state.products.filter(product => product.inventory > 0)
}
}
随后,我们在
components/ProductList.vue
中使用getter,代码如下:
computed: {
products() {
// return store.state.js.products
return store.getters.availableProducts
}
}
以上代码中,我们定义了一个计算属性,并且返回Vuex中的getter方法,这样的话,我们就可以获取所有可用的商品。
当前商品的页面如下:
如果我将一个商品的库存修改为0(代码如下):
const _products = [
{ 'id': 1, 'title': 'iPad 4 Mini', 'price': 500.01, 'inventory': 0 },
{ 'id': 2, 'title': 'H&M T-Shirt White', 'price': 10.99, 'inventory': 10 },
{ 'id': 3, 'title': 'Charli XCX - Sucker CD', 'price': 19.99, 'inventory': 5 }
]
此时的商品页面将会减少一个商品:
成功!
4. Vuex | Action
视频地址
Action是Vuex的方法,我们可以使用异步方法等内容,我们可以在Action中通过
context.commit
调用mutations
,或者使用context.state
调用state
中的变量。
示例 1 | Action 调用 state
context.state.products
示例 2 | Action 调用 mutations
// 会将旧的state替换为products
store.commit('setProducts',products)
示例 3 | 在项目中使用 Action
在之前的项目中,我们在
ProductList.vue
中,我们先在created中通过state.products = products
给Vuex进行赋值,现在我们将这个方法抽离出来也放到Vuex中的Action中去,store/index.js
中的代码如下:
actions: {
fetchProducts(context) {
shop.getProducts(products => {
context.commit('setProducts',products)
})
}
},
mutations: {
setProducts(state,products) {
// update product
state.products = products
}
}
如此一来,我们可以仅仅通过
store/index.js
就可以初始化mock数据,随后我们只需要在ProductList.vue
中调用该Action方法即可。通过这种方式,我们将原本在
ProductList.vue
中进行的赋值操作(需要在ProductList.vue
中import shop)移动到了store/index.js
中(在store/index.js
中import shop),代码更加简洁,ProductList.vue
作为数据的调用方,仅仅调用数据即可,而不用关心数据的初始化操作。
computed: {
products() {
return store.getters.availableProducts
}
},
created() {
store.dispatch('fetchProducts')
}
补充 1 | 在Action中使用{commit}
拆解对象
我们还可以通过传入
{commit}
的方式,拆解对象,使用时不再需要context.commit
,直接使用commit
即可,代码如下所示:
actions: {
fetchProducts({commit}) {
shop.getProducts(products => {
commit('setProducts', products)
})
}
}
示例 4 | 通过 new Promise来实现异步调用结果
我们将
store/index.js
中的代码做如下修改:
actions: {
fetchProducts({commit}) {
return new Promise((resolve,reject) => {
shop.getProducts(products => {
commit('setProducts',products)
resolve()
})
})
}
}
随后,在ProductList中,我们可以获取最终的执行结果:
created() {
console.log(this.loading)
store.dispatch('fetchProducts')
.then(() => console.log('执行结束了'))
}
5. 全局注册store
视频地址
上面的方式我们每次使用store,都需要
import store from 'xxx'
,这是很不方便的,我们可以直接在main.js
中导入store,随后在需要使用的地方只需要this.$store
就可以了,main.js
中代码如下:
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
// 导入store
import store from '@/store/index'
new Vue({
el: '#app',
// 注册store
store,
render: h => h(App)
})
在
ProductList.vue
中使用store
:
<script>
export default {
data() {
return {
loading: false
}
},
computed: {
products() {
// 使用 1
return this.$store.getters.availableProducts
}
},
created() {
this.loading = true
console.log(this.loading)
// 使用 2
this.$store.dispatch('fetchProducts')
.then(() => this.loading = false)
}
}
</script>
6. 商品添加至购物车
视频地址
1. 创建新的存储对象
本章节中,我们在store中创建一个新的存储对象cart,代码如下:
state: { // = data
products: [],
cart: []
},
随后,我们给该对象创建一个Action方法,其中调用三次Mutations方法,代码如下:
addProductToCart(context,product) {
if(product.inventory > 0) {
const cartItem = context.state.cart.find(item => item.id === product.id)
if(!cartItem) {
context.commit('pushProductToCart',product.id)
} else {
context.commit('incrementItemQuantity',cartItem)
}
context.commit('decrementProductInventory',product)
}
}
三个Mutations方法代码如下:
pushProductToCart(state,productId){
state.cart.push({
id: productId,
quantity: 1
})
},
incrementItemQuantity(state,cartItem) {
cartItem.quantity ++
},
decrementProductInventory(state,product) {
product.inventory --
}
随后,我们在ProductList中调用Action方法,入参传入一个Product即可,代码如下:
<template>
<div>
<h1>Product List</h1>
<img v-if="loading" src="../../../imgs/1.png" alt="Gif" style="width: 200px">
<ul v-else>
<li v-for="product in products">{{product.title}} - {{product.price}}
<button @click="addProductToCart(product)">Add to Cart</button>
</li>
</ul>
</div>
</template>
<script>
import store from '@/store/index'
export default {
data() {
return {
loading: false
}
},
computed: {
products() {
// return store.state.js.products
return store.getters.availableProducts
}
},
methods: {
addProductToCart(product) {
store.dispatch('addProductToCart',product)
}
},
created() {
this.loading = true
console.log(this.loading)
store.dispatch('fetchProducts')
.then(() => this.loading = false)
}
}
</script>
- 以上代码中,我们在商品列表上点击加入购物车,此时会调用Action方法
- Action方法会判断当前商品的剩余数量是否大于0,否则不执行任何操作,是则继续向下执行
- 此时商品数量大于0,判断购物车中是否有该商品,如果有该商品则对购物车中该商品的数量+1,否则(购物车中没有该商品),向购物车中添加该商品的id与数量,数量是1
- 将商品的数量-1
7. 通过 Vue DevTools 查看 Vuex 的内容
略
8. 创建 ShoppingCard 组件
视频地址
接下来我们创建一个
ShoppingCard
组件,并将该组件添加到App.vue
中,该组件主要显示我们已经添加到cart中的内容,以及当前cart中商品的总金额,所以我们还需要创建两个getter
方法,来获取这些数据。为了让金额显示的更像金额(前面带有$符号),我们需要引入一个小组件,点此可以下载(该步骤非必须)
1. 引入currency.js
- 在根目录下创建
currency.js
文件,将上方链接中的代码拷贝进去。 - 在main.js中添加以下代码:
import {currency} from "@/currency";
Vue.filter('currency',currency)
- 将页面中的金额做以下修改(添加
| currency
)
{{product.price | currency}}
2. 创建components/ShoppingCart.vue
组件
ShoppingCart.vue 代码如下:
<template>
<div>
<h1>Shopping Cart</h1>
<ul>
<li v-for="product in products">
{{ product.title }} - {{ product.price | currency}} - {{product.quantity}}
</li>
</ul>
<p>Total: {{total | currency}}</p>
</div>
</template>
<script>
import store from "@/store/index";
export default {
computed: {
products() {
return store.getters.cartProducts
},
total() {
return store.getters.cartTotal
}
}
}
</script>
在sotre/index.js
中添加以下两个getter
// 用以获取商品列表
cartProducts(state) {
return state.cart.map(cartItem => {
const product = state.products.find(product => product.id === cartItem.id)
return {
title: product.title,
price: product.price,
quantity: cartItem.quantity
}
})
},
// 用以获取购物车总金额
cartTotal(state,getters) {
return getters.cartProducts.reduce((total,product) => total + product.price * product.quantity,0)
}
3. 在App.vue
中引入组件并使用
<template>
<div id="app">
<ProductList/>
<hr>
<ShoppingCart/>
</div>
</template>
<script>
import ProductList from './components/ProductList'
import ShoppingCart from './components/ShoppingCart'
export default {
name: 'app',
components: {
ProductList,
ShoppingCart
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
4. 最终页面效果如下
9. 结账功能
视频地址
在
shop.js
中有一个结账方法,代码如下:
buyProducts (products, cb, errorCb) {
setTimeout(() => {
// simulate random checkout failure.
(Math.random() > 0.5 || navigator.webdriver)
? cb()
: errorCb()
}, 100)
}
为了模拟结账时成功或失败,这里做了一个随机数,如果随机数大于0.5则结账成功,否则失败,我们需要再Vuex中创建一个Action方法来模拟结账,代码如下:
checkout({state,commit}) {
shop.buyProducts (
state.cart,
() => {
commit('emptyCart')
commit('setCheckoutStatus','success')
},
() => {
commit('setCheckoutStatus','fail')
}
)
}
在这个Action方法中,我们调用了两个Mutations方法,其中
emptyCart
用于清空购物车,setCheckoutStatus
用于当前的结账状态,我们在mutations
中分别实现它们,代码如下:
setCheckoutStatus(state,status) {
state.setCheckoutStatus = status
},
emptyCart(state) {
state.cart = []
}
在
setCheckoutStatus
中,我们还用到了一个名为setCheckoutStatus
的新的state,我们在state
中添加它,代码如下:
state: { // = data
products: [],
cart: [],
setCheckoutStatus: null
},
随后,我们在
components/ShoppingCart.vue
中调用Action方法,就可以了,最终的使用代码如下:
<template>
<div>
<h1>Shopping Cart</h1>
<ul>
<li v-for="product in products">
{{ product.title }} - {{ product.price | currency}} - {{product.quantity}}
</li>
</ul>
<p>Total: {{total | currency}}</p>
<!-- 在此添加按钮,调用我们自己定义的checkout方法 -->
<button @click="checkout">Checkout</button>
<!-- 这里显示结算成功/失败信息 -->
<p>{{checkoutMessage}}</p>
</div>
</template>
<script>
import store from "@/store/index";
export default {
computed: {
products() {
return store.getters.cartProducts
},
total() {
return store.getters.cartTotal
},
checkoutMessage() {
return store.state.setCheckoutStatus
}
},
// 在方法中调用store.Action的checkout方法,如果全局注册的话还可以直接@click="$store.dispatch('checkout')"
methods: {
checkout() {
store.dispatch('checkout')
}
}
}
</script>
10. 动态 getter | getter 接收参数
视频地址
注意:Getter方法是不可以接收参数的(原本Getter只可以操作Vuex中的元素),这一章节的内容就是为了“让不能接收参数的Getter方法接收参数”
需求实现 | 商品数量为0后不可继续下单
在以上代码中,我们的商品展示页面显示的都是剩余数量 > 0的商品,这有些不符合实际需求,我们想要显示所有的商品,并且在商品数量 <= 0的时候,商品的下单按钮变为Disabled。
这其实非常简单,我们只需要将获取商品的计算属性从getter替换到state,再在按钮中添加一个disabled状态就可以,代码如下:
<template>
<div>
<h1>Product List</h1>
<img v-if="loading" src="../../../imgs/1.png" alt="Gif" style="width: 200px">
<ul v-else>
<li v-for="product in products">
{{product.title}} - {{product.price | currency}} - {{product.inventory}}
<!-- 原本button按钮没有disabled -->
<button :disabled="!product.inventory > 0" @click="addProductToCart(product)">Add to Cart</button>
</li>
</ul>
</div>
</template>
<script>
import store from '@/store/index'
export default {
data() {
return {
loading: false
}
},
computed: {
products() {
// 原本是 return store.getters.cartProducts
return store.state.products
}
},
methods: {
addProductToCart(product) {
store.dispatch('addProductToCart',product)
}
},
created() {
this.loading = true
console.log(this.loading)
store.dispatch('fetchProducts')
.then(() => this.loading = false)
}
}
</script>
现在页面已经可以符合我们的需求了,页面中的效果显示如下:
可以看到,当商品数量为0的时候,商品还在显示,只不过无法继续下单了。
需求优化 | 使用动态Getter
虽然我们已经实现了需求,但是方法却并不够“优雅”,我们想能否将
:disabled="!product.inventory > 0"
中的状态值也存储到Vuex中去,这样的话我们只需要调用Vuex的Getter方法,就可以获取当前商品的数量是否>0。
我们在普通的方法中去做这个功能的话,非常的简单,我们只需要在方法的参数中接收一个
product
,随后返回product.inventory > 0
就可以了,但是Vuex的Getter方法与普通方法有一个很大的不同就是:Getter方法无法接受参数!!!,因此传入参数这种方式就无法使用了,那有没有什么方法可以实现呢?答案当然是有的:我们可以通过Getter返回一个方法,用该方法接收product
参数,随后返回product.inventory > 0
即可,接下来简单尝试下。
store/index.js
中getter的方法:
productIsInStock() {
return (product) => {
return product.inventory > 0
}
}
随后在需要使用的地方定义一个computed
计算属性:
computed: {
// 注意这是在computed中
productIsInStock() {
return store.getters.productIsInStock
}
}
最后我们可以在button标签中使用这个计算属性:
<button :disabled="!productIsInStock(product)" @click="addProductToCart(product)">Add to Cart</button>
解释说明
看到这里也许你会有两个疑问:
- 明明这个计算属性都没有接收参数,为什么使用的时候还需要传入一个参数呢?
- 将这个计算属性修改为方法可不可以呢?
问题1:我们在Vuex的getter方法中,实际是返回了一个方法,这个方法有一个参数
product
,返回的内容是product
的inventory
是否大于0。这时候我们在计算属性中调用这个getter(一定注意这边是计算属性),因此getter中返回的返回的内容也就被我们重命名成为了计算属性的方法名,也就是(product) => return product.inventory > 0
这个方法的名称被重命名成了productIsInStock
这个计算属性,此时我们在:disabled="!productIsInStock(product)"
中调用的时候,实际调用的就是getter中返回的函数,那么当然可以传入一个参数啦。
问题2:那么能不能不写在计算属性中,写在方法中呢?答案是可以(我本以为不可以,思考了下其实也行)因为我们计算属性的作用是重命名,可以直接将getter中返回的内容进行重命名,但是方法没有这个作用,如果要写在方法中的话,首先这个方法需要接收一个product参数,其次将这个参数传递进入getter方法,最后返回这个函数的值才可以。代码如下:
methods: {
// 注意这里是methods中
productIsInStock(product) {
return store.getters.productIsInStock(product)
}
}
经测试:可行
以上代码中,由于这个getter方法返回的是一个函数,所以这里的store.getters.productIsInStock
直接可以看做是这个返回的函数,然后当然就可以对这个函数进行传参了。
动态Getter总结 | 让不可以接收参数的Getter方法变得可以接收参数
总结:动态Getter其实就是通过让Getter返回一个函数的方式,让原本不可以接收参数的Getter方法变得可以接收参数。
补充 | 在其他地方使用这个可接受参数的Getter
修改store中的Action方法,代码如下:
addProductToCart({state,getters,commit},product) {
if(product.inventory > 0) {
const cartItem = state.cart.find(item => item.id === product.id)
if(!cartItem) {
commit('pushProductToCart',product.id)
} else {
commit('incrementItemQuantity',cartItem)
}
commit('decrementProductInventory',product)
}
}
11. 使用 VuexMapHelper 减少代码
视频地址
在之前的代码中,我们使用
computed
计算属性来处理Vuex中的值,我们每一个Vuex中的内容都需要三行代码才能在Vue组件中表达(Action用Methods,其它用Computed)。
如果使用MapHelper的话,首先要知道我们有
mapState
、mapGetter
、mapAction
…这意味着我们可以使用不同的Map来导入不同的内容(这一点很重要)。
使用mapState
前后代码量的对比
视频地址
正如上面所说,我们可以使用
mapState
来替换掉冗余的代码,首先我们需要从vuex
中引入mapState
,随后可以使用,使用的方式有很多,下面来一一列举。
下面是不使用mapState/与使用mapState的对比
// 不使用mapState至少5行代码
computed: {
products() {
return store.state.products
}
}
// 使用mapState只需要一行代码
computed: mapState(['products'])
可见,代码量确实大大降低了。
示例 1 | 简单使用 state
我们可以通过数组
[]
来导入state对象(代码如下),使用时直接调用state的名称即可。请注意:这种情况下不支持重命名state,也就是说我们在Vuex.state中定义的内容是什么,在组件中就必须使用这个名称。
computed: mapState(['products'])
示例 2 | 重命名state
示例1中我们无法对state进行重命名,但是我们可以通过对象
{}
来解决这个问题。
computed: mapState({
allProduct: 'products'
})
如此一来,我们就可以对state对象进行重命名,在这个组件中我们无法使用
products
来获取state中的值,只能使用allProducts
来获取值。
示例 3 | 使用方法传入state
以下代码中,我们实现两种使用方法传入state的方式,他们的名称都为
firstProduct$
,并且传入这个数组的第一个值。
computed: mapState({
// 使用匿名方法 返回第一个值
firstProduct1: state => state.products[0],
// 使用具名方法 返回第一个值
firstProduct2(state) {
return state.products[0]
}
})
示例 4 | 示例3的改进-配合组件的变量返回数组中某个具体位置的值
以下代码中,我们有一个
indexOfProduct
的组件变量,在获取组件值的mapState中,我们在funGetProduct$
中数组的下标[x]
指定为该值,此时我们可以获取这个state的第indexOfProduct
个值,且随着此值发生变化,computed中的两个计算属性(funGetProduct$
x2)返回的值也会发生变化。
<script>
export default {
data() {
return {
indexOfProduct: 0
}
},
computed: mapState({
// 使用匿名方法 获得具体位置的值
funGetProduct1: state => state.products[this.indexOfProduct],
// 使用具名方法 获得具体位置的值
funGetProduct2(state) {
return state.products[this.indexOfProduct]
}
})
}
</script>
示例 5 | 同时使用多个 VuexMapHelper 的方式
示例 5.1 关于使用多个 VuexMapHelper 的简单说明
最开始的时候,我们代码量非常多的时候,我们向
computed:{}
中传入的都是一个{}
对象,在上面四个示例中,我们探寻mapState
的使用方式,传入的都是一个mapState
对象,这两种传入方式的对比如下所示:
// 方式1 传统方式
computed: {
products() {
return store.state.products
}
}
// 方式2 使用mapState方式
computed: mapState(['products'])
我们在Vuex中,除了state-mapState,还有getter-mapGetters、action-mapAction,如果仅仅采用上述中的方式2,是绝对达不到要求呢,那么能不能采用下述方式呢?
computed: {
mapGetter(['xxx']),
mapAction(['xxx'])
}
很明显,就从对齐方式上来说,似乎也是不可以的。且这几个方法返回的都是键值对,那么这样一来上述代码的computed就变成了
{ {}, {} }
了,这也不符合我们的需求,有没有方法可以解决这个问题呢?答案是:使用对象展开运算符(Object Spread Operator)。
computed: {
...mapGetters(['xxx']),
...mapAction(['xxx'])
}
示例 5.2 | 探索 ES7 Object Spread Operator
上文中我们提到了ES7的新特性:对象展开运算符(Object Spread Operator)。对象展开运算符
...{}
是指将{}
中的对象展开为key:value,key:value
的方式,下面我们来做简单的实验。
打开浏览器,点击F12,进入console,依次输入下面的代码:
const person = {firstName: 'Montgomery'}
const professor = {laseName: 'Montgomery',profession: 'Herpetologist'}
let result = {...person}
// result的值为 {firstName: 'Montgomery'}
result = {...person,...professor}
// result的值为 {firstName: 'Montgomery',laseName: 'Montgomery',profession: 'Herpetologist'}
result = {...person, ...professor, profession: 'Doctor'}
// result的值为 {firstName: 'Montgomery',laseName: 'Montgomery',profession: 'Doctor'}
result = {...person,...professor,age: 20}
// result的值为 {firstName: 'Montgomery',laseName: 'Montgomery',profession: 'Herpetologist',age: 20}
可见,对象展开运算符有以下几个特点:
- 使用的时候需要将被展开对象放进
{}
中,这样展开之后还是一个对象- 对象展开运算符可以拼接多个对象与键值对,如果存在重复内容,后面的内容会覆盖前面的内容
- 注意:可以将展开对象与键值对进行拼接
示例 5.3 对项目中代码进行改造
- 改造
ProductList.vue
代码
computed:{
...mapState({
product: 'products',
}),
...mapGetters({
productIsInStock: 'productIsInStock'
}),
},
// computed: {
// products() {
// // return store.state.js.products
// return store.state.js.products
// },
// // 注意这是在computed中
// productIsInStock() {
// return store.getters.productIsInStock
// }
// },
- 改造
ShoppingCart.vue
代码
computed: {
...mapState({
checkoutMessage: 'setCheckoutStatus'
}),
...mapGetters({
products: 'cartProducts',
total: 'cartTotal'
}),
// products() {
// return store.getters.cartProducts
// },
// total() {
// return store.getters.cartTotal
// },
// checkoutMessage() {
// return store.state.js.setCheckoutStatus
// }
}
12. 组件管理 1 |将各个组件单独存储
视频地址
我们可以将actions/getters/mutations/state单独存放到一个文件中,随后在index.js中引入并使用它们,方便代码进行管理,下面我们创建一个
store/actions.js
文件,并在文件中粘贴以下代码:
store/actions.js
import shop from '@/api/shop.js'
export default {
fetchProducts({commit}) {
return new Promise((resolve,reject) => {
shop.getProducts(products => {
commit('setProducts',products)
resolve()
})
})
},
addProductToCart({state,getters,commit},product) {
if(product.inventory > 0) {
const cartItem = state.cart.find(item => item.id === product.id)
if(!cartItem) {
commit('pushProductToCart',product.id)
} else {
commit('incrementItemQuantity',cartItem)
}
commit('decrementProductInventory',product)
}
},
checkout({state,commit}) {
shop.buyProducts (
state.cart,
() => {
commit('emptyCart')
commit('setCheckoutStatus','success')
},
() => {
commit('setCheckoutStatus','fail')
}
)
}
}
在store/index.js中引入并使用actions
import Vuex from 'vuex'
import Vue from 'vue'
// 引入actions
import actions from "./actions";
Vue.use(Vuex)
Vue.config.devtools = true
export default new Vuex.Store({
// 使用actions
actions,
// ... 其余部分代码省略
})
13. 组件管理 2 | Vuex Modules 1
视频地址
在上一部分中,我们描述了将store中的四大模块分开存储,这样的话虽然降低了index.js中的代码量,但是对于整个新项目的逻辑条理并没有什么帮助。
我们可以看到,在该项目中主要是有一个商品(仓库)以及一个购物车组成的,那么我们能否将这两者进行区分,放到单独的js文件中,这样一来的话,我们可以对每一个模块进行区分,不仅可以简化index中的代码量,还可以让条理更加清晰。
Modules 准备工作(代码)
创建store/modules/cart.js
文件,代码如下:
import shop from "@/api/shop";
export default {
state: {
cart: [],
setCheckoutStatus: null
},
getters: {
cartProducts(state) {
return state.cart.map(cartItem => {
const product = state.products.find(product => product.id === cartItem.id)
return {
title: product.title,
price: product.price,
quantity: cartItem.quantity
}
})
},
cartTotal(state,getters) {
return getters.cartProducts.reduce((total,product) => total + product.price * product.quantity,0)
}
},
mutations: {
// const cartItem = {id: 123,quantity: 2}
pushProductToCart(state,productId){
state.cart.push({
id: productId,
quantity: 1
})
},
incrementItemQuantity(state,cartItem) {
cartItem.quantity ++
},
setCheckoutStatus(state,status) {
state.setCheckoutStatus = status
},
emptyCart(state) {
state.cart = []
}
},
actions: {
addProductToCart({state,getters,commit},product) {
if(product.inventory > 0) {
const cartItem = state.cart.find(item => item.id === product.id)
if(!cartItem) {
commit('pushProductToCart',product.id)
} else {
commit('incrementItemQuantity',cartItem)
}
commit('decrementProductInventory',product)
}
},
checkout({state,commit}) {
shop.buyProducts (
state.cart,
() => {
commit('emptyCart')
commit('setCheckoutStatus','success')
},
() => {
commit('setCheckoutStatus','fail')
}
)
}
}
}
创建store/modules/cart.js
文件,代码如下:
import shop from '@/api/shop.js'
export default {
state: {
products: []
},
getters: {
availableProducts(state,getters) {
return state.products.filter(product => product.inventory > 0)
},
productIsInStock() {
return (product) => {
return product.inventory > 0
}
}
},
mutations: {
setProducts(state,products) {
// update product
state.products = products
},
decrementProductInventory(state,product) {
product.inventory--
}
},
actions: {
fetchProducts({commit}) {
return new Promise((resolve,reject) => {
shop.getProducts(products => {
commit('setProducts',products)
resolve()
})
})
}
}
}
在store/index.js
中引入并使用modules,代码如下:
import Vuex from 'vuex'
import Vue from 'vue'
// 引入
import cart from "./modules/cart";
import products from "./modules/products";
Vue.use(Vuex)
Vue.config.devtools = true
export default new Vuex.Store({
// 使用
modules: {
cart,
products
}
})
在以上代码中,我们将代码按照功能分为了两个不同的Modules,并且在index.js引入并注册,随后我们就可以使用我们定义的内容了。
此时进入浏览器的Vue插件中,Vuex存储内容如下图所示:
可以看到,所有的state都变成了文件名.state名称
(state按照Modules名称进行分组了,但是Getters/Mutations/Actins不会分组)。因此,我们对State的使用方式也会发生一些变化,请看第二部分。
Modules.State 注意事项 1 | 在组件中的使用
我们将State按照其功能用Modules分组之后,State也会按照Modules名称进行分组,但是Getters/Mutations/Actions不会进行分组,仍然挂在全局的Namespace下。
因此,我们要是在组件中使用该state,我们需要通过
state.modules.var
的方式使用,代码如下所示:
ProductList.vue中computed部分的代码:
computed:{
...mapState({
// 注意这一部分的代码,从原本的state.products,变成了state.products.products
products: state => state.products.products
}),
...mapGetters({
productIsInStock: 'productIsInStock'
}),
}
ShoppingCart.vue中computed部分的代码:
computed: {
...mapState({
// checkoutMessage: 'setCheckoutStatus' 此种写法在Modules情况下不可用
// 可以看到这一部分也百年成了state.modules.var的形式
checkoutMessage: state => state.cart.checkoutMessage
}),
...mapGetters({
products: 'cartProducts',
total: 'cartTotal'
}),
},
只有这样,我们才能正常的调用到分组后的State中的内容。
Modules.State 注意事项 2 | 在Store中使用
注意事项1中我们已经说明了,Store在划分Modules之后,State会分组,此时某个ModuleA中我们在Action/Getter中传入的
{state}
代表的仅仅是Local的State(也就是该文件内的),我们无法通过ModuleA中的{state}(参数)调用ModuleB中的state(值),我们可以引入一个新的变量{rootState}
来表示根State,通过rootState
可以调用到获所有的State。
rootState | 在Getters中使用
Getters 中第三个参数是rootState,这里我们没有使用到第二个参数getter,但是仍要保留在这里。
cartProducts(state,getter,rootState) {
return state.cart.map(cartItem => {
// 我们在这一行需要调用Mudule-products中的State,因此这里需要使用rootState
const product = rootState.products.products.find(product => product.id === cartItem.id)
return {
title: product.title,
price: product.price,
quantity: cartItem.quantity
}
})
}
rootState | 在Action中使用
在Action中是第四个参数
addProductToCart({state,getters,commit,rootState},product) {
if(product.inventory > 0) {
const cartItem = state.cart.find(item => item.id === product.id)
if(!cartItem) {
commit('pushProductToCart',product.id)
} else {
commit('incrementItemQuantity',cartItem)
}
commit('decrementProductInventory',product)
}
}
13. 组件管理 3 | Vuex Modules - NameSpaceSpace
视频地址
Vuex通过Module分组后有一个特点:如果ModuleA与ModuleB中存在一个同名的Action方法acfun()(请注意Action方法不分组),那么当我们在组件中调用acfun()方法的时候,两个Modules中的Action方法都会被执行。
为了解决这一问题,我们可以使用
NameSpace
来区分不同组中的Action/Mutation/Getters。这样一来,可以避免多个开发者协同开发时的冲突问题。
我们在Module文件中写上
namespace: true
即对该Module启用了分组,示例代码如下:
import shop from '@/api/shop.js'
export default {
// 此行代码开启Getter/Action/Mutation的分组功能
namespaced: true,
state: {
},
getters: {
},
mutations: {
},
actions: {
}
}
添加
namespaced: true
之后,该Module内的Getters/Actions/Mutations都将会分组,因此将不会出现同名Action都被调用的问题。但是我们对分组后的此三种(Actions/Mutations/Getters)的调用方式也得修改一下。需要注意的是,在分组后,ModuleA调用ModuleA(同组内)中的三种方法的方式还是不会发生改变,但是在ModuleA调用ModuleB中的方法,需要使用到
rootGetters/rootActions/rootMutations
,下面以Getters举例:
示例 1 | ModuleA 中调用 ModuleB 中的Getters示例
不加namespaced或者同Module中调用的代码如下:
actionMethod({state,getters},aVar) {
getters.getterMethodName(aVar);
}
加上namespaced之后再ModuleA中调用ModuleB中的示例如下:
actionMethod({state,getters,rootGetters},aVar) {
rootGetters['moduleB/getterMethodName'](aVar)
}
可以看到以上两个示例中,rootGetters是第三个参数,aVar是传入的参数,此时我们可以使用rootGetters调用根下的所有Getter。
示例 2 | 在组件中调用Module中的方法
如果不加namespaced,那么我们可以直接像下面这样调用:
computed: {
...mapGetters({
products: 'cartProducts',
total: 'cartTotal'
})
}
加上namespaced之后,我们可以像下面这样调用:
computed: {
...mapGetters({
products: 'cart/cartProducts',
total: 'cart/cartTotal'
})
}
以上代码中,cart是Module名称,
cartProducts
与cartTotal
是两个Getter方法。
我们还可以像下面这样稍作简化,将cart
提前:
computed: {
...mapGetters('cart',{
products: 'cartProducts',
total: 'cartTotal'
})
}
思考 | 在一个组件中调用两个Modules中的Getter方法
如果不使用namespaced,那么我们的调用代码如下:
假设调用ModuleA中fun1与fun2方法/ModuleB中Fun3这三个Getter方法
computed: {
...mapGetters({
fun1: 'fun1',
fun2: 'fun2',
fun3: 'fun3'
})
}
加上namespaced之后:
computed: {
...mapGetters({
fun1: 'ModuleA/fun1',
fun2: 'ModuleA/fun2',
fun3: 'ModuleB/fun3'
})
}
简化之后:
computed: {
...mapGetters('ModuleA',{
fun1: 'fun1',
fun2: 'fun2'
}),
...mapGetters('ModuleB',{
fun3: 'fun3'
})
}
这边比较有意思,简单思考下,我就不解释了。
示例 3 | 在组件中调用Actions方法
假设调用ModuleA中的fun1/fun2和ModuleB中的fun3
不使用namespaced:
methods: {
...mapActions({
fun1: 'fun1',
fun2: 'fun2',
fun3: 'fun3'
})
}
使用namespaced之后:
methods: {
...mapActions({
fun1: 'moduleA/fun1',
fun2: 'moduleA/fun2',
fun3: 'moduleB/fun3'
})
}
拆分为两个VuexMapHelper
methods: {
...mapActions('moduleA',{
fun1: 'fun1',
fun2: 'fun2'
}),
...mapActions('moduleB',{
fun3: 'fun3'
})
}
示例 4 | 在ModuleA中调用ModuleB中的Actions与Mutations
看到这里必须得提个醒,State/Getters/Actions/Mutations的调用方式如下表:
类型 | 调用方式 | 备注 |
---|---|---|
State | store.state.varName | 一般用在计算属性中作为一个“值”来使用 |
Getter | store.getter.methodName | 一般用在计算属性中作为一个“值”来使用 |
Mutation | store.commit('methodName',参数) | 一般作为方法来使用 |
Action | store.dispatch('methodName',参数) | 一般作为方法来使用 |
文章至此,我们已经描述了分组后的State如何调用,以及namespaced之后的Getter如何调用,这两种“值”都只是
moduleName/getter
即可。但是,Mutation/Action不是通过
store.mutation/action
这样的方式调用的,这导致要在这两种“方法”中调用其它Module中的方法与Getter/State稍有区别,我们无法通过store.dispatch('moduleName/actionName')
的方式来调用其它组件中的方法,而是要通过下面这种方式:
actions: {
actionName(xxx) {
// 调用其它Module中的Mutations方法
commit('mutationMethodName',{root: true})
// 调用其它Module中的Actions方法
dispatch('actionMethodName',{root: true})
}
}