[vue2]深入理解vuex

news2024/11/22 18:01:31

本节内容

  • 概述
  • 初始化仓库
  • 定义数据
  • 访问数据
  • 修改数据
  • 处理异步
  • 派生数据
  • 模块拆分
  • 案例-购物车

概述

vuex是一个vue的状态管理工具, 状态就是数据

场景

  1. 某个状态在很多个组件使用 (个人信息)
  2. 多个组件 共同维护 一份数据 (购物车)

优势

  1. 数据集中式管理
  2. 数据响应式变化

初始化仓库

  1. 安装vuex: yarn add vuex@3
  2. 创建仓库
// 这里面存放的就是 vuex 相关的核心代码
import Vue from 'vue'
import Vuex from 'vuex'

// 插件安装
Vue.use(Vuex)

// 创建仓库
const store = new Vuex.Store({ })

// 导出给main.js使用
export default store
  1. 挂载仓库
... ...
import store from '@/store/index'

// 挂载仓库
new Vue({
  render: h => h(App),
  store
}).$mount('#app')

定义数据

stata提供唯一的公共数据源, 所有共享的数据都要统一放在store的state中存储

... ...
// 创建仓库
const store = new Vuex.Store({
  // 严格模式 (有利于初学者,检测不规范的代码 => 上线时需要关闭)
  strict: true,
  // 通过 state 可以提供数据 (所有组件共享的数据)
  state: {
    title: '仓库大标题',
    count: 100,
    list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  }

})

访问数据

1>通过store访问数据

  • 获取store
    • this.$store
    • import 导入 store
  • 使用store
    • 模版中: {{ $store.state.xxx }}
    • 组件逻辑中: this.$store.state.xxx
    • JS模块中: store.state.xxx

2>通过辅助函数访问数据

mapState是辅助函数, 帮助我们把store中的数据 自动映射 到组件的计算属性中

  • 导入函数: import { mapState } from 'vuex'
  • 映射数据: computed: { ...mapState(['count']) }
  • 使用数据: this.count
  • 辅助函数的本质就是把state中的数据定义在计算属性中, 方便使用

修改数据

vuex同样遵循单向数据流, 组件中不能直接修改仓库数据, 而是提交申请后, 由仓库修改

默认情况, 直接修改数据也不会报错, 因为额外监控损失性能, 通过 strict: true 可以开启严格模式, 监控错误语法

state数据的修改只能通过mutations

1>定义mutations方法

定义mutations对象, 对象中存放修改state的方法

... ...
const store = new Vuex.Store({
  state: {
    count: 100,
  },
  // 通过 mutations 可以提供修改数据的方法
  mutations: {
    // 所有mutation函数,第一个参数都是 state
    addCount (state, n) {
      // 修改数据
      state.count += n
    },
  },
})
  1. 组件中提交commit调用mutations
  2. 语法: this.$store.commit('addCount', 10 )
  3. mutation函数的参数也叫载荷, 触发mutations传参也叫提交载荷(payload)
  4. 注意: mutation提交参数有且只能有一个,如果需要多个参数,包装成一个对象

2>通过辅助函数修改数据

mapMutations辅助函数, 可以把mutations中的方法提取出来, 映射到methods中, 更方便的使用

  • 导入函数: import { mapMutations } from 'vuex'
  • 映射方法: methods: { ...mapMutations(['subCount']) }
  • 使用方法: this.subCount(10)
  • 辅助函数的本质就是把mutations中的方法映射在methods中, 方便使用

3>输入框双向绑定

仓库的数据要遵循单向数据流, 所以输入框 绑定仓库数据 不能使用v-model

<template>
  <div id="app">
    <h1> 根组件 - {{ count }} </h1>
    <!-- 3, 使用:value绑定仓库的值 -->
    <input :value="count" @input="handleInput" type="text">
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  name: 'app',
  computed: {
    ...mapState(['count'])
  },
  methods: {
    handleInput (e) {
      // 1. 实时获取输入框的值
      const num = +e.target.value
      // 2. 提交mutation,调用mutation函数
      this.$store.commit('changeCount', num)
    }
  },
}
</script>
const store = new Vuex.Store({
  strict: true,
  state: {
    count: 100,
  },
  mutations: {
    // 更新 count 数据的方法
    changeCount (state, newCount) {
      state.count = newCount
    },
  },
})

处理异步

mutations必须是同步的(便于监测数据变化, 记录调试), 需要提供 actions 方法, 处理异步的相关逻辑

  1. 定义action方法, 处理异步的逻辑
const store = new Vuex.Store({
  strict: true,
  state: {
    count: 100,
  },

  mutations: {
    changeCount (state, newCount) {
      state.count = newCount
    },
  },

  // actions 处理异步
  // 注意:不能直接操作 state,操作 state,还是需要 commit mutation
  actions: {
    // context上下文 (未分模块,当成store仓库, 分模块后, 当成所在模块)
    // context.commit('mutation名字', 额外参数)
    changeCountAction (context, num) {
      // 这里是setTimeout模拟异步,以后大部分场景是发请求
      setTimeout(() => {
        context.commit('changeCount', num)
      }, 1000)
    }
  },
})
  1. 直接dispatch调用
<template>
  <div class="box">
    <button @click="handleChange">一秒后修改成666</button>
  </div>
</template>

<script>
export default {
  methods: {
    handleChange () {
      this.$store.dispatch('changeCountAction', 200)
    }
  }
}
</script>
  1. mapActions 辅助函数调用
<template>
  <div class="box">
    <button @click="handleChange">一秒后修改成666</button>
  </div>
</template>

<script>
import { mapActions } from 'vuex'
export default {
  methods: {
    ...mapActions(['changeCountAction']),

    change () {
      this.changeCountAction('100')
    }
  }
}
</script>
  1. 辅助函数的本质是把actions中的方法映射到组件methods中, 方便调用

派生数据

有时需要基于state 派生出一些数据, 这些数据依赖state, 就可以使用getters

1, 定义getters方法

const store = new Vuex.Store({
  strict: true,
  state: {
    list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  },

  // 4. getters 类似于计算属性
  getters: {
    // 注意点:
    // 1. 形参第一个参数,就是state
    // 2. 必须有返回值,返回值就是getters的值
    filterList (state) {
      return state.list.filter(item => item > 5)
    }
  },
})

2, 访问getters数据

3, 在getters中访问getters

export default {
  namespaced: true,
  state () {
    return {
      cartList: []
    }
  },
  getters: {
    // 选中的商品
    selCartList (state) {
      return state.cartList.filter(item => item.isChecked)
    },
    // 选中的商品总数
    selCount (stat, getterse) {
      // 注意:
      // 可以在getters中,通过第二个参数, 拿到其他getters
      return getterse.selCartList.reduce((sum, item) => sum + item.goods_num, 0)
    },
  }
}

模块拆分

由于vuex使用单一状态树, 应用的所有状态都会集中到一个大的对象, 当应用复杂时, store对象就会相当臃肿

步骤

  1. 新建子模块
// user模块
const state = {
  userInfo: {
    name: 'zs',
    age: 18
  },
  score: 80
}
const mutations = { }
const actions = { }
const getters = { }

export default {
  state,
  mutations,
  actions,
  getters
}
  1. 挂载子模块
// 1,引入子模块
import user from './modules/user'

const store = new Vuex.Store({
   ... ...

  // 2. 通过modules挂载子模块
  modules: {
    user,
  }
})
  1. 检测: 查看调试工具

开启命名空间

子模块开启命名名后, 每个子模块之间相互隔离, 访问子模块数据时, 会更加清晰

export default {
  // 开启子模块命名空间
  // 好处:数据独立,更加清晰
  namespaced: true,
  state,
  mutations,
  actions,
  getters
}

访问子模块数据

尽管已经分模块了, 但其实子模块的数据, 还是会挂载到根级别的 state中, 属性名就是模块名

  1. 直接通过模块名访问
  • $store.state.模块名.xxx
  1. 通过mapState映射数据
  • 不开启命名空间: mapState(['xxx'])
  • 开启命名空间: mapState('模块名', ['xxx']) (推荐)

修改子模块数据

mutation会被挂载到全局, 需要开启命名空间, 才会挂载到子模块

  1. 直接通过store访问
  • $store.commit('模块名/xxx', 额外参数)
  1. 通过mapMutations映射
  • 不开启命名空间: mapMutations(['xxx'])
  • 开启命名空间: mapMutations('模块名', ['xxx']) (推荐)

异步修改子模块数据

action会被挂载到全局, 需要开启命名空间, 才会挂载到子模块

  1. 直接通过store访问
  • $store.dispatch('模块名/xxx', 额外参数)
  1. 通过mapActions映射
  • 不开启命名空间: mapActions(['xxx'])
  • 开启命名空间: mapActions('模块名', ['xxx']) (推荐)

访问子模块getters

  1. 直接通过模块名访问
  • $store.store.getters['模块名/xxx']
  1. 通过mapGetters映射
  • 不开启命名空间: mapGetters(['xxx'])
  • 开启命名空间: mapGetters('模块名', ['xxx']) (推荐)

跨模块调用mutations

export default {
  namespaced: true,
  state () {
    return {
      // 个人权证相关
      userInfo: getInfo()
    }
  },
  mutations: {
    // 设置用户信息
    SET_USER_INFO (state, userInfo) {
      state.userInfo = userInfo
      setInfo(userInfo) // 存储用户信息到本地
    }
  },
  actions: {
    // 退出登录
    logout ({ commit }) {
      // 清空个人信息
      commit('SET_USER_INFO', {})
      // 情况购物车信息(跨模块调用mutations)
      // commit('模块名/方法名', 传值/null, { root: true(开启全局) })
      commit('cart/setCartList', [], { root: true })
    }
  },
  getters: {}
}

案例-购物车

效果展示

功能分析

  1. 请求动态渲染购物车, 数据存veux
  2. 数据框控件 修改数据
  3. 动态计算总价和总数量

搭建环境

通过脚手架新建项目, 清空src文件夹, 替换准备好的素材

mock数据

基于 json-server 工具, 模拟后端接口服务

  1. 官网: json-server - npm
    1. 全局安装: npm install json-server -g
  1. 代码根目录新建db文件夹
  2. 准备index.json文件, 放入db目录
  3. 进入db目录, 执行命令启动接口服务: json-server index.json
  4. 访问接口测试: http://localhost:3000/cart

创建cart模块

export default {
  // 开启命名空间
  namespaced: true,
  // 分模块存储, 官方建议使用函数方式提供数据
  state () {
    return {
      list: []
    }
  }
}
import cart from './modules/cart'

export default new Vuex.Store({
  modules: {
    cart
  }
})

请求数据渲染

import axios from 'axios'
export default {
  // 开启命名空间
  namespaced: true,
  // 数据源
  state () {
    return {
      list: []
    }
  },
  mutations: {
    // 同步更新数据
    updataList (state, list) {
      state.list = list
    }
  },
  actions: {
    // 异步请求数据
    async getList (context) {
      const res = await axios.get('http://localhost:3000/cart')
      context.commit('updataList', res.data)
    }
  },
  getters: {
  }
}
<template>
  <div class="app-container">
    ... ...
    <!-- 商品 Item 项组件 -->
    <cart-item v-for="item in list" :key="item.id" :item="item"></cart-item>
    ... ...
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  name: 'App',
  created () {
    // 触发异步请求
    this.$store.dispatch('cart/getList')
  },
  computed: {
    // 映射仓库数据
    ...mapState('cart', ['list'])
  }
}
</script>

修改数量

import axios from 'axios'
export default {
  // 开启命名空间
  namespaced: true,
  // 数据源
  state () {
    return {
      list: []
    }
  },
  mutations: {
    updataItem (state, obj) {
      const goods = state.list.find(item => item.id === obj.id)
      goods.count = obj.newNnm
    }
  },
  actions: {
    async updateList (context, obj) {
      // 修改后台数据
      const res = await axios.get(`http://localhost:3000/cart/${obj.id}`, {
        count: obj.newNnm
      })
      // 更新仓库数据
      context.commit('updataItem', obj)
    }
  },
}
<template>
  <div class="goods-container">
    ...
    <div class="btns">
          <!-- 按钮区域 -->
          <button class="btn btn-light" @click="changNum(-1)">-</button>
          <span class="count">{{ item.count }}</span>
          <button class="btn btn-light" @click="changNum(1)">+</button>
      </div>
    ...
  </div>
</template>

<script>
export default {
  name: 'CartItem',
  props: {
    item: {
      type: Object,
      required: true
    }
  },
  methods: {
    changNum (n) {
      const newNnm = this.item.count + n
      const id = this.item.id
      if (newNnm === 0) return
      this.$store.dispatch('cart/updateList', { id, newNnm })
    }
  }
}
</script>

计算底部数据

export default {
  ... ...
  getters: {
    total (state) {
      return state.list.reduce((sum, item) => sum + item.count, 0)
    },

    totalPrice (state) {
      return state.list.reduce((sum, item) => sum + item.count * item.price, 0)
    }
  }
}
<template>
  <div class="footer-container">
    <!-- 中间的合计 -->
    <div>
      <span>共 {{ total }} 件商品,合计:</span>
      <span class="price">¥{{ totalPrice }}</span>
    </div>
    ... ...
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
export default {
  name: 'CartFooter',
  computed: {
    ...mapGetters('cart', ['total', 'totalPrice'])
  }
}
</script>

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

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

相关文章

30字以内免费翻译维吾尔语,汉维翻译工具推荐,维吾尔文字母OCR识别神器《维汉翻译通》App!

维吾尔文OCR文字识别 《维汉翻译通》App内置的OCR技术&#xff0c;能够快速识别图片中的文字和字母&#xff0c;无论是路标、菜单还是书籍&#xff0c;都能迅速转换为用户所需的语言&#xff0c;让语言障碍不再是问题。针对维吾尔语更是进行了专门的优化&#xff0c;即便是手写…

【Ardiuno】实验ESP32单片机自动配置Wifi功能(图文)

这里小飞鱼按照ESP32的示例代码&#xff0c;实验一下wifi的自动配置功能。所谓的自动配置&#xff0c;就是不用提前将wifi的名称和密码写到程序里&#xff0c;这样可以保证程序在烧录上传后&#xff0c;可以通过手机端的软件来进行配置&#xff0c;可以避免反复修改代码&#x…

事件、方法实现 on_radioGreen_clicked ,on_chkBoxUnder_clicked,Qfont,QPalette

Vertical Layout 、Horizontal Layout 实验窗体自适应布局 接上篇界面布局&#xff0c; 实验checkBox、radioBox 的事件槽&#xff0c; 使用Qfont组件变更纯文本框QPlainTextEdit中字体的下划线、加粗、斜体效果 使用调色板组QPalette变更纯文本框QPlainTextEdit中文本颜色 UI…

Qt全局快捷键QGlobalHotKey的自研之路

这两天对Qt的快捷键格外感兴趣。 前两天在使用QHotKey的过程中&#xff0c;发现不能定义小键盘键盘码&#xff0c;自己二次修改了该库之后已经可以设置小键盘快捷键了。文章在这里&#xff1a;Qt第三方库QHotKey设置小键盘数字快捷键。 昨天突发奇想&#xff1a;目前所有的快…

20240613解决飞凌的OK3588-C的核心板的USB3.0接口不读U盘的问题

20240613解决飞凌的OK3588-C的核心板的USB3.0接口不读U盘的问题 2024/6/13 15:21 缘起&#xff0c;由于USB3.0的CC芯片在飞凌的OK3588-C的开发板的底板上&#xff0c;一切正常。 如果你单独使用核心板&#xff0c;很容易出现这个问题【省成本没有使用CC芯片】&#xff1a;不读U…

VScode中连接并使用docker容器

前提条件&#xff1a; 1.在windows下安装Docker Desktop(方法可见下面的教程) Docker Desktop 安装使用教程-CSDN博客 2.在vscode安装3个必备的插件 3.先在ubuntu中把docker构建然后运行 4.打开vscode&#xff0c;按下图顺序操作 调试好之后上传到git上&#xff0c;然后后面…

随心笔记,第六更

目录 一、 三步构建 XML转成java bean 1.XML转XSD 2.XSD转JavaBean 3.jaxb 工具类 4.测试 &#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是「Leen」。刚工作几年&#xff0c;想和大家一同进步&am…

Java中Transactional在不同方法间的穿透性,rollbackFor参数含义

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 在Java开发中&#xff0c;经常会遇到需要在一个事务中执行多个操作的场景。为了确保这些操作的原子性&#xff0c;可以使用Spring框架提供的Transactional注解来实现事务管理。然而&#xff0c;在实际开发过程中&…

Centos: ifconfig command not found且ip addr查不到服务器IP

前段时间部门新派发了服务器&#xff0c;让我过去使用U盘装机&#xff0c;装完后使用ifconfig查不到服务器IP地址&#xff0c;ip addr也是查不到 ifconfig&#xff1a;command not found (这两个图片先用虚拟机的替代一下) 在网上找资料(CSDN&#xff0c;博客园&#xff0c;知乎…

【权威出版/稳定检索】2024年气象应用、勘查与灾害应急国际会议(AEMT 2024)

2024 International Conference on Meteorological Applications, Exploration, and Disaster Emergency Response 2024年气象应用、勘查与灾害应急国际会议 【会议信息】 会议简称&#xff1a;AEMT 2024 大会时间&#xff1a;请查看官网 截稿时间&#xff1a;点击查看 大会地…

自动化办公03 用xlrd和xlwt库操作excel.xls文件(老版本)

目录 一、读操作 二、写操作 三、设置单元格格式 0.综合案例 1.设置行高和列宽 2.设置字体样式 3.设置边框样式 4.设置对齐方式 5.设置背景颜色 6.合并单元格 四、 xlutils修改Excel⽂件内容 1.安装 2.使用 一、读操作 import xlrd# 1. 打开excel文件 wb xlrd.op…

MT7981B+MT7976C+MT7531A RF定频测试方法

1、从下面网址下载QA软件包&#xff0c;然后在WIN系统下安装QA环境。 https://download.csdn.net/download/zhouwu_linux/89428691?spm1001.2014.3001.5501 在WINDOWS 7系统下先安装WinPcap_4_1_3.exe。 2、搭建硬件环境&#xff0c;电脑先连接仪器&#xff0c;主板网络与电…

Deepstream用户手册——DeepStream应用及配置文件

DeepStream参考应用 - DeepStream-app 应用架构 下图显示了NVIDIADeepStream参考应用程序的架构。 DeepStream 参考应用程序是一个基于 GStreamer 的解决方案&#xff0c;由一组封装低级 API 以形成完整图形的GStreamer插件组成。参考应用程序能够接受来自各种来源的输入&…

算法day28

第一题 295. 数据流的中位数 本题我们是求解给定数组的中位数。且由于需要随时给数组添加元素&#xff0c;所以我们要求解该动态数组的中位数&#xff0c;所以本题最关键的就是维护数组在添加元素之后保持有序的排序&#xff0c;这样就能很快的求解中位数&#xff1b; 解法&am…

提升消费者满意度的五星售后服务认证

在当今竞争激烈的市场环境中&#xff0c;消费者满意度是企业取得成功的重要因素。五星售后服务认证作为一种权威性认证&#xff0c;可以显著提高消费者满意度&#xff0c;增强企业的竞争力。本文将从四个方面探讨五星售后服务认证如何提高消费者满意度。 五星售后服务认证是由国…

RedHat9 | iSCSI磁盘配置与管理

一、实验环境 1、iSCSI介绍 iSCSI&#xff08;Internet Small Computer System Interface&#xff09;是一种基于因特网及SCSI-3协议下的存储技术&#xff0c;也称为IP-SAN。iSCSI是由IETF&#xff08;Internet Engineering Task Force&#xff09;提出&#xff0c;并于2003年…

wms海外仓系统有哪些?选择的时候怎么避坑

虽然说wms海外仓系统能够在很大程度上提升海外仓的经营效率&#xff0c;但是如果在选择wms海外仓系统的时候没有慎重考虑&#xff0c;也是非常容易踩坑的。 这样不只是不能提升自己海外仓的效率&#xff0c;反倒是浪费了大量的预算和精力&#xff0c;这就得不偿失了。今天我们…

检查子树00

题目链接 检查子树 题目描述 注意点 树的节点数目范围为[0, 20000] 解答思路 递归判断t1和t2的val是否相同&#xff0c;如果相同&#xff0c;则继续递归判断其左右子树的值是否都相同&#xff0c;如果都相同则返回true&#xff1b;如果不相同&#xff0c;则继续递归判断t1…

剧本新纪元:探索短剧系统的魔力

在现代社会&#xff0c;随着科技的迅猛进步和生活节奏的不断加快&#xff0c;传统的长篇电视剧和电影已不能完全满足所有人的需求。短剧&#xff0c;由于其简短、快速、直接的特性&#xff0c;正在逐步成为一种文化新趋势。短剧系统正是这一趋势的典型代表&#xff0c;它以独特…

tracetcp下载 安装 使用 网络工具 windows trace工具 tcp协议

省流 Tracetcp是一个类似于Tracert的工具&#xff0c;使用如下&#xff1a; 1. 安装winpcap &#xff0c; 下载链接&#xff1a;WinPcap Download 2.下载tracetcp软件&#xff0c;下载链接&#xff1a; https://github.com/0xcafed00d/tracetcp/releases 命令&#xff1a;…