【前端】Vuex笔记(超详细!!)

最近花了两周时间,完完全全的跟着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 = falseVue.filter('currency',currency)// 导入store
import store from '@/store/index'new Vue({el: '#app', // 使用storestore,render: h => h(App)
})



Vuex 简介

Vuex 4种方法 + 1种分类方式
  1. State 单一状态树,可以理解成是数据
  2. Getter 可以理解成是store的计算属性
  3. Mutation 用于更改数据(可以是set/update等)
  4. Action 类似于 mutation,但是它提交的是mutation,而不是直接变更状态,Action可以包含任意的异步操作
  5. Module 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决该问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

state/getters一般作为值来使用,一般放在computed内,使用计算属性作为“值”来用
mutations/actions可以传参,一般作为方法来使用,放在methods




调用Vuex中的内容


综合 | 各种方法调用
类型调用方式备注
Statestore.state.varName一般用在计算属性中作为一个“值”来使用
Getterstore.getter.methodName一般用在计算属性中作为一个“值”来使用
Mutationstore.commit('methodName',参数)一般作为方法来使用
Actionstore.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 = trueexport 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,代表根节点。

  1. 在Getter中调用

在Getter中使用其他Module中的state,首先我们需要传入至少三个参数,第三个参数为rootState,调用方式为rootState.moduleName.varName,以以上代码为示例的话,应该是rootState.moduleA.items

getters: {getterName(state,getter,rootState) {rootState.moduleA.items}
},
  1. 在Mutations中调用

无法获取

  1. 在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})}}
}

以上代码中:

  1. 通过moduleA/nutationName来指定调用moduleA中的方法
  2. commit表示是调用mutations
  3. {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 { // = methodsmethod1({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: { // = dataproducts: []},getters: { // = computed propertiesproductsCount() {}},actions: {fetchProducts() {// make the call}},mutations: {setProducts() {// update product}}
})

Vuex 包含5种状态:

  1. State 单一状态树,可以理解成是数据
  2. Getter 可以理解成是store的计算属性
  3. Mutation 用于更改数据(可以是set/update等)
  4. Action 类似于 mutation,但是它提交的是mutation,而不是直接变更状态,Action可以包含任意的异步操作
  5. 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: { // = dataproducts: []},getters: { // = computed propertiesproductsCount() {}},actions: {fetchProducts() {// make the call}},mutations: {setProducts(state,products) {// update productstate.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 propertiesavailableProducts(state,getters) {return state.products.filter(product => product.inventory > 0)}
}

随后,我们在components/ProductList.vue中使用getter,代码如下:

computed: {products() {// return store.state.js.productsreturn 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 productstate.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',// 注册storestore,render: h => h(App)
})

ProductList.vue中使用store

<script>
export default {data() {return {loading: false}},computed: {products() {// 使用 1return this.$store.getters.availableProducts}},created() {this.loading = trueconsole.log(this.loading)// 使用 2this.$store.dispatch('fetchProducts').then(() => this.loading = false)}
}
</script>



6. 商品添加至购物车

视频地址

1. 创建新的存储对象

本章节中,我们在store中创建一个新的存储对象cart,代码如下:

state: { // = dataproducts: [],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.productsreturn store.getters.availableProducts}},methods: {addProductToCart(product) {store.dispatch('addProductToCart',product)}},created() {this.loading = trueconsole.log(this.loading)store.dispatch('fetchProducts').then(() => this.loading = false)}}
</script>
  1. 以上代码中,我们在商品列表上点击加入购物车,此时会调用Action方法
  2. Action方法会判断当前商品的剩余数量是否大于0,否则不执行任何操作,是则继续向下执行
  3. 此时商品数量大于0,判断购物车中是否有该商品,如果有该商品则对购物车中该商品的数量+1,否则(购物车中没有该商品),向购物车中添加该商品的id与数量,数量是1
  4. 将商品的数量-1



7. 通过 Vue DevTools 查看 Vuex 的内容

8. 创建 ShoppingCard 组件

视频地址

接下来我们创建一个ShoppingCard组件,并将该组件添加到App.vue中,该组件主要显示我们已经添加到cart中的内容,以及当前cart中商品的总金额,所以我们还需要创建两个getter方法,来获取这些数据。

为了让金额显示的更像金额(前面带有$符号),我们需要引入一个小组件,点此可以下载(该步骤非必须)

1. 引入currency.js
  1. 在根目录下创建currency.js文件,将上方链接中的代码拷贝进去。
  2. 在main.js中添加以下代码:
import {currency} from "@/currency";
Vue.filter('currency',currency)
  1. 将页面中的金额做以下修改(添加| 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: { // = dataproducts: [],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.cartProductsreturn store.state.products}},methods: {addProductToCart(product) {store.dispatch('addProductToCart',product)}},created() {this.loading = trueconsole.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. 明明这个计算属性都没有接收参数,为什么使用的时候还需要传入一个参数呢?
  2. 将这个计算属性修改为方法可不可以呢?

问题1:我们在Vuex的getter方法中,实际是返回了一个方法,这个方法有一个参数product,返回的内容是productinventory是否大于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的话,首先要知道我们有mapStatemapGettermapAction…这意味着我们可以使用不同的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}

可见,对象展开运算符有以下几个特点:

  1. 使用的时候需要将被展开对象放进{}中,这样展开之后还是一个对象
  2. 对象展开运算符可以拼接多个对象与键值对,如果存在重复内容,后面的内容会覆盖前面的内容
  3. 注意:可以将展开对象与键值对进行拼接
示例 5.3 对项目中代码进行改造
  1. 改造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
//   }
// },
  1. 改造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 = trueexport default new Vuex.Store({// 使用actionsactions,// ... 其余部分代码省略
})



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 productstate.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 = trueexport 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.productsproducts: 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中使用
我们无法通过ModuleA中的{state}参数调用ModuleB中的state值,但是{rootState}参数可以调用到所有的State值。

注意事项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,因此这里需要使用rootStateconst 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名称,cartProductscartTotal是两个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的调用方式如下表:

类型调用方式备注
Statestore.state.varName一般用在计算属性中作为一个“值”来使用
Getterstore.getter.methodName一般用在计算属性中作为一个“值”来使用
Mutationstore.commit('methodName',参数)一般作为方法来使用
Actionstore.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})}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/19338.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ios:文本框默认的copy、past改成中文复制粘贴

问题 ios 开发&#xff0c;对于输入框的一些默认文案展示&#xff0c;如复制粘贴是英文的&#xff0c;那么如何改为中文的呢 解决 按照路径找到这个文件 ios/项目/Info.plist&#xff0c;增加 <key>CFBundleAllowMixedLocalizations</key> <true/> <…

Keras深度学习框架实战(1):图像分类识别

1、绪论 1.1 图像分类的定义 图像分类是计算机视觉领域中的一项基本任务&#xff0c;其定义是将输入图像分配给预定义类别中的一个或多个。具体来说&#xff0c;图像分类系统接受一个图像作为输入&#xff0c;并输出一个或多个类别标签&#xff0c;这些标签描述了图像中的内容…

第十三届蓝桥杯物联网试题(国赛)

还是那句话不能掉以轻心&#xff0c;全力以赴吧&#xff0c;遇事不要慌&#xff0c;该做的都做了&#xff0c;冷静沉稳的处理&#xff0c;看看配置&#xff0c;看看代码&#xff0c;还是不行就重启&#xff0c;都没问题换个板子 下面对比较复杂的部分的处理过程进行展现&#x…

git报错prohibited by Gerrit: not permitted: update

git push报错&#xff1a; Push to refs/for/[branch] to create a review, or get Push rights to update the branch. Contact an administrator to fix the permissions (prohibited by Gerrit: not permitted: update)原因&#xff1a; 使用Gerrit代码审核时&#xff0c;本…

CentOS 7基础操作02_优化Linux操作系统中的服务

1、实验环境 公司在文件服务器中新安装了CentOS系统.由于默认启动的服务程序较多&#xff0c;系统运行缓慢。现需要对系绞服务进行适当优化&#xff0c;减少一些不必要的自启动服务.并设置系统在开机后直接进入字符模式。 2、需求描述 根据实际使用需求对CentOS 7操作系统中的…

postgressql——PGPROC XLOG(6)

PGPROC相关结构 在共享内存中,核心数据结构围绕PROC_HDR指向的两个list:PROC和XACT PRCO内存连续,维护链表结构方便申请释放,对应每个后台服务进程,PID为OS标识、PGPROCNO为内部标识 XACT内存连续,维护快照需要的xmin和xid,XACT从PROC拆出来是为了更高的cache line命中…

IBM开源Granite Code模型,多尺寸可选,支持多种代码任务,性能媲美 CodeLlama

前言 近年来&#xff0c;大型语言模型&#xff08;LLM&#xff09;在代码领域展现出惊人的潜力&#xff0c;为软件开发流程带来了革命性的改变。代码 LLM 不仅能够生成高质量代码&#xff0c;还能帮助程序员修复错误、解释代码、编写文档等等&#xff0c;极大地提高了软件开发…

【kubernetes】探索k8s集群的存储卷、pvc和pv

目录 一、emptyDir存储卷 1.1 特点 1.2 用途 1.3部署 二、hostPath存储卷 2.1部署 2.1.1在 node01 节点上创建挂载目录 2.1.2在 node02 节点上创建挂载目录 2.1.3创建 Pod 资源 2.1.4访问测试 2.2 特点 2.3 用途 三、nfs共享存储卷 3.1特点 3.2用途 3.3部署 …

Web程序设计-实验05 DOM与BOM编程

题目 【实验主题】 影视网站后台影视记录管理页设计 【实验任务】 1、浏览并分析多个网站后台的列表页面、编辑页面&#xff08;详见参考资源&#xff0c;建议自行搜索更多后台页面&#xff09;的主要元素构成和版面设计&#xff0c;借鉴并构思预期效果。 2、新建 index.h…

正则匹配优化:匹配排除多个字符串的其他字符串

(^entity|^with|...)\w优化 (?!entity|with|has|index|associations|input)\w(?!): 匹配排除项 效果 继续优化 匹配会过滤掉带有关键字的字段&#xff0c;在过滤的时候是可以加上尾部结束匹配符的 效果&#xff1a;

thinkphp6 自定义的查询构造器类

前景需求&#xff1a;在查询的 时候我们经常会有一些通用的&#xff0c;查询条件&#xff0c;但是又不想每次都填写一遍条件&#xff0c;这个时候就需要重写查询类&#xff08;Query&#xff09; 我目前使用的thinkphp版本是6.1 首先自定义CustomQuery类继承于Query <?p…

【C语言回顾】预处理

前言1. 简单概要2. 预处理命令讲解结语 上期回顾: 【C语言回顾】编译和链接 个人主页&#xff1a;C_GUIQU 归属专栏&#xff1a;【C语言学习】 前言 各位小伙伴大家好&#xff01;上期小编给大家讲解了C语言中的编译和链接&#xff0c;接下来我们讲解一下预处理&#xff01; …

【香橙派 AIpro】新手保姆级开箱教程:Linux镜像+vscode远程连接

香橙派 AIpro 开发板 AI 应用部署测评 写在最前面一、开发板概述官方资料试用印象适用场景 二、详细开发前准备步骤1. 环境准备2. 环境搭建3. vscode安装ssh插件4. 香橙派 AIpro 添加连接配置5. 连接香橙派 AIpro6. SSH配置 二、详细开发步骤1. 登录 juypter lab2. 样例运行3. …

【IDEA】-使用IDEA查看类之间的依赖关系

1、父子类的继承、实现关系 1.1、使用CTRL Alt U 选择 java class 依据光标实际指向的类位置 用实心箭头表示泛化关系 是一种继承的关系&#xff0c;指向父类 可以提前设置需要显示的类的属性、方法等信息 快捷键 Ctrl Alt S &#xff0c;然后搜索 Diagrams 1.2、使用…

python知识继续学习

1、计算机表示小数是有误差的&#xff0c;下面的5就是误差 2、在python中&#xff0c;所有的非0数字都是True&#xff0c;零是False。所有的非空字符串都是True&#xff0c;空字符串是False。空列表是False。在python的基本数据类型中&#xff0c;表示空的东西都是False&#x…

数据结构(三)循环链表 约瑟夫环

文章目录 一、循环链表&#xff08;一&#xff09;概念&#xff08;二&#xff09;示意图&#xff08;三&#xff09;操作1. 创建循环链表&#xff08;1&#xff09;函数声明&#xff08;2&#xff09;注意点&#xff08;3&#xff09;代码实现 2. 插入&#xff08;头插&#x…

【linux】运维-基础知识-认知hahoop周边

1. HDFS HDFS&#xff08;Hadoop Distributed File System&#xff09;–Hadoop分布式文件存储系统 源自于Google的GFS论文&#xff0c;HDFS是GFS的克隆版 HDFS是Hadoop中数据存储和管理的基础 他是一个高容错的系统&#xff0c;能够自动解决硬件故障&#xff0c;eg&#xff1a…

【Linux 网络编程】网络的背景、协议的分层知识!

文章目录 1. 计算机网络背景2. 认识 "协议"3. 协议分层 1. 计算机网络背景 网络互联: 多台计算机连接在一起, 完成数据共享; &#x1f34e;局域网&#xff08;LAN----Local Area Network&#xff09;: 计算机数量更多了, 通过交换机和路由器连接。 &#x1f34e; 广…

多模态模型入门:BLIP与OWL-ViT

BLIP 数据预处理 CapFilt&#xff1a;标题和过滤 由于多模态模型需要大量数据集&#xff0c;因此通常必须使用图像和替代文本 (alt-text) 对从互联网上抓取这些数据集。然而&#xff0c;替代文本通常不能准确描述图像的视觉内容&#xff0c;使其成为噪声信号&#xff0c;对于…

MAC M1 —— Install

文章目录 MAC M1 —— Install安装IDEA安装JDK安装Maven安装brew无法创建文件 /data/serverMac 修改终端用户名&#xff08;主机名&#xff09;PyCharm MAC M1 —— Install 安装IDEA 关键词&#xff1a;2020到2021.3的激活步骤。找下Download文件夹 安装JDK 在个人的电脑上…