十六、状态管理——Vuex(2)

news2024/11/17 23:59:37

本章概要

  • mapMutations
  • mapState
  • getter

16.4 mapMutations

继续完善购物车程序,为购物车添加删除商品功能。删除商品同样要修改 store 中保存的购物车商品数据,因此继续在 mutations 选项中定义一个 deleteItem mutation。编辑 store 目录下的 index.js 文件,如下:

store/index.js

import { createStore } from "vuex";
import books from "@/data/books.js";

const store = createStore({
    // 状态数据通过 state() 函数返回
    state() {
        return {
            items: books  // 使用导入的 books 对 items 进行格式化
        }
    },
    mutations:{
        pushItemToCart (state,book){
            state.items.push(book);
        },
        deleteItem (state,id){
            // 根据提交的 id 载荷,查找是否存在相同 id 的商品,返回商品的索引
            let index = state.items.findIndex(item => item.id === id);
            if(index>0){
                state.items.splice(index,1);
            }
        }
    }
})

export default store;

编辑 Cart.vue,为“删除”按钮添加 click 事件处理,提交 deleteItem mutation。如下:

Cart.vue

<td><button @click="$store.commit('deleteItem',book.id)">删除</button></td>

此时再次运行项目,单击“删除”,可以看到购物车中的商品项被成功删除。
在这里插入图片描述

如果组件中需要提交的 mutation 较多,使用 this.$store.commit() 方法提交就会很烦琐,为了简化 mutation 提交,可以使用 mapMutations() 辅助函数将组件中的方法映射为 store.commit() 调用。如下:

import { mapMutations } from 'vuex'
methods:mapMutations([
  // 将 this.increment() 映射为 this.$store.commit('increment')
  'increment',
  // 将 this.incrementBy(amount) 映射为 this.$store.commit('incrementBy',amount)
  'incrementBy'
])

除了使用字符串数组外,mapMutations() 函数的参数也可以是一个对象。如下:

import { mapMutations } from 'vuex'
methods:mapMutations({
  // 将 this.add() 映射为 this.$store.commit('increment')
  add:'increment'
})

在大多数情况下,组件还有自己的方法,在这种情况下,可以使用 ES6 的展开运算符提取 mapMutation() 函数返回的对象属性,赋值到 methods 选项中。如下:

import { mapMutations } from 'vuex'

export default {
  // ...
  methods:{
    ...mapMutations([
      // 将 this.increment() 映射为 this.$store.commit('increment')
      'increment',
      // mapMutations 也支持载荷
      // 将 this.incrementBy(amount) 映射为 this.$store.commit('increment',amount)
      'incrementBy'
    ]),
    ...mapMutations({
      // 将this.add() 映射为 this.$store.commit('increment')
      add:'increment'
    })
  }
}

修改 Cart.vue ,使用 mapMutations() 辅助函数简化 mutation 的提交。如下:

Cart.vue

<template>
    <div>
        <table>
            <tr>
                <td>商品编号</td>
                <td><input type="text" v-model.number="id" /></td>
            </tr>
            <tr>
                <td>商品名称</td>
                <td><input type="text" v-model.number="title" /></td>
            </tr>
            <tr>
                <td>商品价格</td>
                <td><input type="text" v-model.number="price" /></td>
            </tr>
            <tr>
                <td colspan="2"><button @click="addCart">加入购物车</button></td>
            </tr>
        </table>
        <table>
            <thead>
                <tr>
                    <th>编号</th>
                    <th>商品名称</th>
                    <th>价格</th>
                    <th>数量</th>
                    <th>金额</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="book in books" :key="book.id">
                    <td>{{ book.id }}</td>
                    <td>{{ book.title }}</td>
                    <td>{{ book.price }}</td>
                    <td>
                        <button>-</button>
                        {{ book.count }}
                        <button>+</button>
                    </td>
                    <td>金额</td>
                    <!-- <td><button @click="$store.commit('deleteItem',book.id)">删除</button></td> -->
                    <td><button @click="deleteItem(book.id)">删除</button></td>
                </tr>
            </tbody>
        </table>
        <span>总价:¥0.00</span>
    </div>
</template>
<script>
import { mapMutations } from 'vuex';

export default {
    data() {
        return {
            id: null,
            title: '',
            price: '',
            quantity: 1
        }
    },
    computed: {
        books() {
            // return this.$store.state.items;
            return this.$store.state.items;
        }
    },
    methods: {
        ...mapMutations({
            addItemToCart:'pushItemToCart'
        }),
        ...mapMutations([
            'deleteItem'
        ]),
        addCart() {
            // this.$store.commit('pushItemToCart', {
            this.addItemToCart({
                id: this.id,
                title: this.title,
                price: this.price,
                count: this.quantity
            })
            this.id = '';
            this.title = '';
            this.price = '';
        }
    }
}
</script>
<style scoped>
div {
    width: 800px;
}

table {
    border: 1px solid black;
    width: 100%;
    margin-top: 20px;
}

th {
    height: 50px;
}

th,
td {
    border-bottom: 1px solid #ddd;
    text-align: center;
}

span {
    float: right;
}
</style>

代码中为了演示 mapMutations() 辅助函数的用法,采用了两种方式映射 mutation,实际开发中不必如此,采用统一的映射方式更有助于代码的维护和修改。

16.5 mapState

当一个组件需要使用多个 store 状态属性时,将这些状态都声明为计算属性就会有些重复和冗余。为了解决这个问题,可以使用 mapState() 辅助函数生成计算属性。例如,在 store 中定义了两个状态。如下:

const store = createStore({
  state() {
    return {
      count:0,
      message:'biubiubiu'
    }
  }
})

在组件中使用 mapState() 辅助函数生成计算属性。如下:

import { mapState } from 'vuex'

export default {
  // ...
  computed:mapState({
    count:'count',
    msg:'message'
  })
}

mapState 代码块中,冒号前是计算属性的名字,冒号后是 store 中状态属性的名字,以字符串形式给出。上述代码等价于下面的代码:

import { mapState } from 'vuex'

export default {
  // ...
  computed:mapState({
    count:function(state){
      return state.count;
    },
    msg:(state) => state.message
  })
}

可以看到,不管是使用普通函数,还是箭头函数,都没有直接使用字符串方便。但如果在计算属性中还要访问组件内的数据属性,那么就只能使用普通函数的方式。如下:

import { mapState } from 'vuex'

export default {
  data(){
    return {
      price:99
    }
  },
  computed:mapState({
    totalPrice(state){
      return this.price * state.count
    }
  })
}

如果计算属性的名字和 store 中状态属性的名字相同,那么还可以进一步简化,直接给 mapState() 函数传递一个字符串数组即可。如下:

computed:mapState([
  // 映射 this.count 为 store.state.count
  'count',
  // 映射 this.message 为 store.state.message
  'message'
])

与 mapMutations() 一样,mapState() 函数返回的也是一个对象,因此可以使用展开运算符将它和组件内的本地计算属性结合一起使用。如下:

computed:{
  localComputed(){
    // ...
  },
  // 使用对象展开运算符将此对象混入外部对象中
  ...mapState({
    // ...
  })
}

接下来修改 Cart.vue ,使用 mapState() 辅助函数生成 books 计算属性。如下:

Cart.vue

import { mapMutations,mapState } from 'vuex'
...
computed:{
  // books() {
  //     return this.$store.state.items;
  // },
  ...mapState({
      books: 'items'
  }),
}

16.6 getter

假如在 store 的状态中定义了一个图书数组。如下:

const store = createStore({
  state(){
    return {
      books:[
        {id:1,title:'duangduang',siSold:false},
        {id:2,title:'pengpeng',siSold:true},
        {id:3,title:'biubiu',siSold:true}
      ]
    }
  }
})

在组件内需要得到正在销售的图书,于是定义一个计算属性 sellingBooks,对 state 中的 books 进行过滤。如下:

computed:{
  sellingBooks(){
    return this.$store.state.books.filter(book => book.isSold === true)
  }
}

这没有什么问题,但如果多个组件都需要用到 sellingBooks 属性,那么应该怎么办呢?是复制代码,还是抽取为共享函数在多处导入?显然,这都不理想。
Vuex 允许我们在 store 中定义 getters(可以认为是 store 的计算属性)。与计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有在它的依赖项发生改变时才会重新计算。
getter 接收 state 作为其第一个参数。如下:

const store = createState({
  state(){
    return {
      books:[
        {id:1,title:'duangduang',siSold:false},
        {id:2,title:'pengpeng',siSold:true},
        {id:3,title:'biubiu',siSold:true}
      ]
    }
  },
  getters:{
    sellingBooks:state => state.books.filter(book => book.isSold === true)
  }
})

我们定义的 getter 将作为 store.getter 对象的属性来访问。如下:

<ul>
  <li v-for="book in this.$store.getter.sellingBooks" :key="book.id">
    {{ book.title }}
  </li>
</ul>

getter 也可以接收其他 getter 作为第二个参数。如下:

getters:{
  sellingBooks: state => state.books.filter(book => book.isSold === true),
  sellingBooksCount:(state,getters) => {
    return getters.sellingBooks.length
  }
}

在组件内,要简化 getter 的调用,同样可以使用计算属性。如下:

computed:{
  sellingBooks(){
    return this.$store.getters.sellingBooks;
  },
  sellingBooksCount(){
    return this.$store.getters.sellingBooksCount;
  }
}

要注意,作为属性访问的 getter 作为 Vue 响应式系统的一部分被缓存。
如果想简化上述 getter 在计算属性中的访问形式,则可以使用 mapGetter() 辅助函数,这个辅助函数的用法和 mapMutations() 、mapState() 类似,如下:

computed:{
  // 使用对象展开运算符将 getter 混入 computed 中
  // 传递数组作为参数
  ...mapGetters([
    'sellingBooks',
    'sellingBooksCount',
    // ...
  ]),
  // 传递对象作为参数
  ...mapGetters({
    // 将 this.booksCount 映射为 this.$store.getters.sellingBooksCount
    booksCount:'sellingBooksCount'
  })
}

getter 还有更灵活的用法,通过让 getter 返回一个函数来实现给 getter 传参。例如,下面的 getter 根据图书 id 查找图书对象。

getters:{
  ...
  getBookById:function(state){
    return function(id){
      return state.books.find(book => book.id === id);
    }
  }
}

也可以使用箭头函数简化上述代码的编写。如下:

getters:{
  ...
  getBookById:state => id => state.books.find(book => book.id === id)
}

下面在组件模板中的调用将返回 {“id”:2,“title”:“pengpeng”,“isSold”:true}。

<p>{{ $store.getters.getBookById(2) }}</p>

下面完成购物车中单项商品价格和所有商品总价的计算,单项商品价格是商品的价格乘以数量,总价是单项商品价格相加的结果。
由于购物车中的商品是存储在 store 中的,因此单项商品价格和所有商品总价的计算应该通过 getter 完成,而不是直接在 组件内 定义计算属性来完成。
编辑 store 目录下的 index.js ,添加计算单项商品价格和所有商品总价的 getter。如下:

store/index.js

import { createStore } from "vuex";
import books from "@/data/books.js";

const store = createStore({
    // 状态数据通过 state() 函数返回
    state() {
        return {
            items: books  // 使用导入的 books 对 items 进行格式化
        }
    },
    mutations:{
        pushItemToCart (state,book){
            state.items.push(book);
        },
        deleteItem (state,id){
            // 根据提交的 id 载荷,查找是否存在相同 id 的商品,返回商品的索引
            let index = state.items.findIndex(item => item.id === id);
            if(index>0){
                state.items.splice(index,1);
            }
        }
    },
    getters:{
        cartItemPrice(state){
            return function (id){
                let item = state.items.find(item => item.id === id);
                if(item){
                    return item.price * item.count;
                }
            }
        },
        cartTotalPrice(state){
            return state.items.reduce((total,item) => {
                return total + item.price * item.count;
            },0)
        }
    }
})

export default store;

如果 getter 要接收参数,则需要 getter 返回一个函数来实现给 getter 传参。
编辑 Cart.vue ,在computed 选项中使用 mapGetters() 映射上述两个 getter ,然后修改模板代码,完善单项商品价格计算和购物车中所有商品总价的计算。如下:

Cart.vue

...
<!-- <td>金额</td> -->
<td>{{ itemPrice(book.id) }}</td>
<!-- <td><button @click="$store.commit('deleteItem',book.id)">删除</button></td> -->
<td><button @click="deleteItem(book.id)">删除</button></td>
...
<span>总价:¥{{ totalPrice }}</span>
...
import { mapMutations,mapState,mapGetters } from 'vuex';
...
computed: {
    // books() {
    //     return this.$store.state.items;
    // },
    ...mapState({
        books: 'items'
    }),
    ...mapGetters({
        itemPrice: 'cartItemPrice',
        totalPrice: 'cartTotalPrice'
    }),
},
...

下面实现购物车中商品数量加 1 和 减 1 的功能,这个功能的实现与 getter 无关,因为要修改 store 中所存储的商品的数量,因此是通过 mutation 实现商品数量的变化。
编辑 store 目录下的 index.js 文件,修改后的代码如下:

store/index.js

...
mutations:{
    pushItemToCart (state,book){
        state.items.push(book);
    },
    deleteItem (state,id){
        // 根据提交的 id 载荷,查找是否存在相同 id 的商品,返回商品的索引
        let index = state.items.findIndex(item => item.id === id);
        if(index>0){
            state.items.splice(index,1);
        }
    },
    incrementItemCount(state,{id,count}){
        let item = state.items.find(item => item.id === id);
        if(item){
            item.count += count; //如果count 为1,则加1;如果 count 为 -1 则减 1
        }
    }
},
...

编辑 Cart.vue ,在 methods 选项中使用 mapMutations() 辅助函数映射 incrementItemCount,并为减号按钮添加 click 事件的处理代码。如下:

Cart.vue

...
<td>
    <button :disabled="book.count === 0" @click="increment({id:book.id,count:-1})">-</button>
    {{ book.count }}
    <button @click="increment({id:book.id,count:1})">+</button>
</td>
...
...mapMutations({
    addItemToCart:'pushItemToCart',
    increment:'incrementItemCount'
}),
...

运行项目,访问 localhost:8080 ,随意增加某项商品的数量,如下:
在这里插入图片描述

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

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

相关文章

介绍idea 几个常用的插件

介绍idea 几个常用的插件1. Lombok2. MyBatis Log Free3. Database4. jsonparser5. Restful Fast Request5.1 插件配置5.1.1 基础配置5.1.2 全局请求头配置5.1.3 String生成配置5.2 插件使用5.2.1 调式接口&#xff08;测试看&#xff09;5.2.2 快速配置token5.2.3 保存、搜索A…

集成分布式锁架包(MySQL、Redis、Zookeeper)

前言&#xff1a; 疫情当下&#xff0c;大环境不好&#xff0c;自己又去了一家令人非常失望的单位&#xff0c;一直在996加班忙于业务代码&#xff0c;技术方面几乎等于零成长。但是&#xff0c;作为一个Coder&#xff0c;必须要挤出时间去学习与总结&#xff0c;不然就会被无情…

Emmet 使用 lorem 快捷生成随机文本填充html页面

快速使用 在编程中&#xff0c;我们也可以使用Lorem ipsum来填充页面&#xff0c;测试显示效果。主要是通过编辑器中自带的 Emmet 插件&#xff0c;识别以 lorem 开头的短语&#xff0c;生成指定格式的内容。以下介绍均以 vscode 为测试载体。 注意&#xff1a;lorem 短语需要…

Java-String 类·下

Java-String 类下5. 字符, 字节与字符串5.1 字符与字符串5.2 字节与字符串5.3 小结6.字符串常见操作6.1 字符串比较6.2 字符串查找6.3 字符串替换6.4 字符串拆分6.5 字符串截取6.6 其他操作方法7. StringBuffer 和 StringBuilder补充大家好&#xff0c;我是晓星航。今天为大家带…

计算机网络体系结构

目录常见的计算机网络体系结构计算机网络体系结构分层的必要性计算机网络体系结构分层思想举例计算机网络体系结构中的专用术语常见的计算机网络体系结构 TCP/IP体系结构相当于将OSI体系结构的物理层和数据链路层合并为网络接口层。并去掉了会话层和表示层。 由于TCP/IP在网络…

Java爬虫 爬取某招聘网站招聘信息

Java爬虫 爬取某招聘网站招聘信息一、系统介绍二、功能展示1.需求爬取的网站内容2.实现流程2.1数据采集2.2页面解析2.3数据存储三、获取源码一、系统介绍 系统主要功能&#xff1a;本项目爬取的XX招聘网站 二、功能展示 1.需求爬取的网站内容 2.实现流程 爬虫可以分为三个模…

[Kettle] Kettle界面介绍

启动Kettle后&#xff0c;弹出Kettle的欢迎界面 有关界面的构成和说明如下所示 ①标题栏&#xff1a;显示界面标题名称 ②菜单栏&#xff1a;分别有【文件】|【编辑】|【视图】|【执行】|【工具】|【帮助】六个菜单栏 ③工具图标栏&#xff1a;显示图形化的常用和重要的菜单项…

SAP MM采购定价过程字段解析

下面我们针对每一个字段进行解释和用途分析 &#xff1a; 1、 步骤&#xff1a;代表了创建PO时&#xff0c;哪个条件类型放到前面&#xff0c;哪个放到后面&#xff0c;如果步骤号相同&#xff0c;那就以谁先选择出来谁就在前面。 2、 计数&#xff1a;没有任何实际意义&a…

DaVinci:神奇遮罩

调色页面&#xff1a;神奇遮罩Color&#xff1a;Magic Mask神奇遮罩 Magic Mask基于人工智能技术&#xff0c;在检视器中绘制一个笔画&#xff0c;就能识别出笔画所在的对象&#xff0c;并以此自动创建遮罩。先确定要对画面上的物体还是人体做遮罩。若是对人体做遮罩&#xff0…

【闲来无聊写个几个小特效——五角星,小光圈,探照灯】

五角星&#xff0c;见过吧&#xff0c;如果是你&#xff0c;你如何使用代码写一个五角星呢&#xff1f;思考一下&#xff0c;你会说&#xff0c;先这样在那样就好啦&#xff0c;可是真正上手的时候却修修改改磕磕绊绊来看一下今天的五角星如何用几行代码实现 1.绘制五角星 四行…

Pytorch进行自定义Dataset 和 Dataloader 原理

1、自定义加载数据 在pytorch中&#xff0c;数据集对象被抽象为Dataset类&#xff0c;实现自定义的数据集需要继承Dataset&#xff0c;并实现相应的方法。 在学习Pytorch的教程时&#xff0c;加载数据许多时候都是直接调用torchvision.datasets里面集成的数据集&#xff0c;直…

GO第 4 章:运算符

第 4 章 运算符 4.1 运算符的基本介绍 运算符是一种特殊的符号&#xff0c;用以表示数据的运算、赋值和比较等 运算符是一种特殊的符号&#xff0c;用以表示数据的运算、赋值和比较等 算术运算符 赋值运算符 比较运算符/关系运算符 逻辑运算符 位运算符 其它运算 4.2 …

Java开发环境安装

总步骤 第一步&#xff1a;安装JDK&#xff08;Java Development Kit&#xff0c;Java软件开发工具包&#xff09; 第二步&#xff1a;安装IDEA&#xff08;是Java语言的集成开发环境&#xff09; 一、安装JDK Windows下最简单的Java环境安装指南 - 大博哥VV6 - 博客园 (cnblo…

微信小程序框架

框架 小程序开发框架的目标是通过尽可能简单、高效的方式让开发者可以在微信中开发具有原生 APP 体验的服务。 整个小程序框架系统分为两部分&#xff1a;逻辑层&#xff08;App Service&#xff09;和 视图层&#xff08;View&#xff09;。小程序提供了自己的视图层描述语言…

【Linux】进程创建、进程终止和进程等待

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;进程创建&…

力扣刷题记录——231. 2 的幂、228. 汇总区间、242. 有效的字母异位词

本专栏主要记录力扣的刷题记录&#xff0c;备战蓝桥杯&#xff0c;供复盘和优化算法使用&#xff0c;也希望给大家带来帮助&#xff0c;博主是算法小白&#xff0c;希望各位大佬不要见笑&#xff0c;今天要分享的是——《231. 2 的幂、228. 汇总区间、242. 有效的字母异位词》。…

【王道操作系统】2.2.4 作业进程调度算法(FCFS先来先服务、SJF短作业优先、HRRN高响应比优先)

作业进程调度算法(FCFS先来先服务、SJF短作业优先、HRRN高响应比优先) 文章目录作业进程调度算法(FCFS先来先服务、SJF短作业优先、HRRN高响应比优先)1.先来先服务(FCFS)2.短作业优先(SJF)3.高响应比优先(HRRN)4.三种算法的对比和总结1.先来先服务(FCFS) 先来先服务调度算法(F…

区间选点 and 最大不相交区间

区间选点 题目描述 给定 N 个闭区间 [ai,bi]&#xff0c;请你在数轴上选择尽量少的点&#xff0c;使得每个区间内至少包含一个选出的点。 输出选择的点的最小数量。 位于区间端点上的点也算作区间内。 输入输出及样例 最大不相交区间 题目描述 给定 N 个闭区间 [ai,bi]&…

ArcGIS基础实验操作100例--实验32计算栅格行列号

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验32 计算栅格行列号 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&am…

GPU存储器架构-- 全局内存 本地内存 寄存器堆 共享内存 常量内存 纹理内存

上表表述了各种存储器的各种特性。作用范围栏定义了程序的哪个部分能使用该存储器。而生存期定义了该存储器中的数据对程序可见的时间。除此之外&#xff0c;Ll和L2缓存也可以用于GPU程序以便更快地访问存储器。 总之&#xff0c;所有线程都有一个寄存器堆&#xff0c;它是最快…