当前位置: 首页 > article >正文

Vue(7)

一.Vuex

(1)概述

1.是什么

vuex是一个vue的状态管理工具,状态就是数据,可以帮助管理vue通用的数据(多组件共享的数据)

2.场景

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

3.优势

①共同维护一份数据,数据集中化管理
②响应式变化
③操作简单(提供了辅助函数)

(2)构建vuex[多组件数据共享]环境

效果是三个组件,共享一份数据,任意一个组件都可以修改,三个组件的数据是同步的

<template>
  <div class="box">
    <h2>Son1 子组件</h2>
    从vuex中获取的值: <label>{{ $store.state.count }}</label>
    <br>
    <button @click="handleAdd(1)">值 + 1</button>
    <button @click="handleAdd(5)">值 + 5</button>
    <button @click="handleAdd(10)">值 + 10</button>
    <button @click="handleChange">一秒后修改成666</button>
    <button @click="changeFn">改标题</button>

    <hr>
    <!-- 计算属性getters -->
    <div>{{ $store.state.list }}</div>
    <div>{{ $store.getters.filterList }}</div>

    <hr>
    <!-- 测试访问模块中的state - 原生 -->
    <div>{{ $store.state.user.userInfo.name }}</div>
    <button @click="updateUser">更新个人信息</button>
    <button @click="updateUser2">一秒后更新信息</button>

    <div>{{ $store.state.setting.theme }}</div>
    <button @click="updateTheme">更新主题色</button>
    <hr>
    <!-- 测试访问模块中的getters - 原生 -->
    <div>{{ $store.getters['user/UpperCaseName'] }}</div>
  </div>
</template>

<script>
export default {
  name: 'Son1Com',
  created () {
    console.log(this.$store.getters)
  },
  methods: {
    updateUser () {
      // $store.commit('模块名/mutation名', 额外传参)
      this.$store.commit('user/setUser', {
        name: 'xiaowang',
        age: 25
      })
    },
    updateUser2 () {
      // 调用action dispatch
      this.$store.dispatch('user/setUserSecond', {
        name: 'xiaohong',
        age: 28
      })
    },
    updateTheme () {
      this.$store.commit('setting/setTheme', 'pink')
    },
    handleAdd (n) {
      // 错误代码(vue默认不会监测,监测需要成本)
      // this.$store.state.count++
      // console.log(this.$store.state.count)

      // 应该通过 mutation 核心概念,进行修改数据
      // 需要提交调用mutation
      // this.$store.commit('addCount')

      // console.log(n)
      // 调用带参数的mutation函数
      this.$store.commit('addCount', {
        count: n,
        msg: '哈哈'
      })
    },
    changeFn () {
      this.$store.commit('changeTitle', '传智教育')
    },
    handleChange () {
      // 调用action
      // this.$store.dispatch('action名字', 额外参数)
      this.$store.dispatch('changeCountAction', 666)
    }
  }
}
</script>

<style lang="css" scoped>
.box{
  border: 3px solid #ccc;
  width: 400px;
  padding: 10px;
  margin: 20px;
}
h2 {
  margin-top: 10px;
}
</style>

<template>
  <div id="app">
    <h1>
      根组件
      - {{ title }}
      - {{ count }}
    </h1>
    <input :value="count" @input="handleInput" type="text">
    <Son1></Son1>
    <hr>
    <Son2></Son2>
  </div>
</template>

<script>
import Son1 from './components/Son1.vue'
import Son2 from './components/Son2.vue'
import { mapState } from 'vuex'
// console.log(mapState(['count', 'title']))

export default {
  name: 'app',
  created () {
    // console.log(this.$router) // 没配
    console.log(this.$store.state.count)
  },
  computed: {
    ...mapState(['count', 'title'])
  },
  data: function () {
    return {

    }
  },
  methods: {
    handleInput (e) {
      // 1. 实时获取输入框的值
      const num = +e.target.value
      // 2. 提交mutation,调用mutation函数
      this.$store.commit('changeCount', num)
    }
  },
  components: {
    Son1,
    Son2
  }
}
</script>

<style>
#app {
  width: 600px;
  margin: 20px auto;
  border: 3px solid #ccc;
  border-radius: 3px;
  padding: 10px;
}
</style>

二.创建一个空仓库

1.yarn add vuex@3
安装vuex → 新建vuex模块文件 → 创建仓库 → main . js导入挂载
2.新建store / index . js专门存放vuex
在这里插入图片描述
3.Vue . use(Vuex)创建仓库 new Vuex . store()
4.在main . js中导入挂载到Vue实例上

三.核心概念 - state状态

1.提供数据:
State提供唯一的公共数据源,所有共享的数据都要统一放在Store中的State中存储,在state对象中可以添加我们要共享的数据

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

2.使用数据
①通过store直接访问
②通过辅助函数(简化)
import { mapState } from ‘vuex’
在这里插入图片描述

{{ count}}
//把state中的数据,定义在组件内的计算属性中
computed:{
	count () {
		return this.$store.state.count
	}
}

四.核心概念 - mutations状态

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

// 通过 mutations 可以提供修改数据的方法
  mutations: {
    // 所有mutation函数,第一个参数,都是 state
    // 注意点:mutation参数有且只能有一个,如果需要多个参数,包装成一个对象
    // 错误代码(vue默认不会监测,监测需要成本)
      // this.$store.state.count++
      // console.log(this.$store.state.count)

      // 应该通过 mutation 核心概念,进行修改数据
     

    addCount (state, obj) {
      console.log(obj)
      // 修改数据
      state.count += obj.count
    },
    subCount (state, n) {
      state.count -= n
    },
    changeCount (state, newCount) {
      state.count = newCount
    },
    changeTitle (state, newTitle) {
      state.title = newTitle
    }
  },

2.组件中提交调用mutations

 // 需要提交调用mutation
      this.$store.commit('addCount')

(1)mutations传参语法

1.提交mutation函数(带参数 - 提交载荷payload)

mutations: {
	...
 	addCount (state, n) {
      state.count += n
    },

2.页面中提交调用mutation

this.$store.commit('addCount',10)

(2)练习


```css
<template>
  <div class="box">
    <h2>Son2 子组件</h2>
    从vuex中获取的值:<label>{{ count }}</label>
    <br />
    <button @click="subCount(1)">值 - 1</button>
    <button @click="subCount(5)">值 - 5</button>
    <button @click="subCount(10)">值 - 10</button>
    <button @click="changeCountAction(888)">1秒后改成888</button>
    <button @click="changeTitle('前端程序员')">改标题</button>
    <hr>
    <div>{{ filterList }}</div>
    <hr>
    <!-- 访问模块中的state -->
    <div>{{ user.userInfo.name }}</div>
    <div>{{ setting.theme }}</div>
    <hr>
    <!-- 访问模块中的state -->
    <div>user模块的数据:{{ userInfo }}</div>
    <button @click="setUser({ name: 'xiaoli', age: 80 })">更新个人信息</button>
    <button @click="setUserSecond({ name: 'xiaoli', age: 80 })">一秒后更新信息</button>

    <div>setting模块的数据:{{ theme }} - {{ desc }}</div>
    <button @click="setTheme('skyblue')">更新主题</button>
    <hr>
    <!-- 访问模块中的getters -->
    <div>{{ UpperCaseName }}</div>
  </div>
</template>

<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
  name: 'Son2Com',
  computed: {
    // mapState 和 mapGetters 都是映射属性
    ...mapState(['count', 'user', 'setting']),
    ...mapState('user', ['userInfo']),
    ...mapState('setting', ['theme', 'desc']),
    ...mapGetters(['filterList']),
    ...mapGetters('user', ['UpperCaseName'])
  },
  methods: {
    // mapMutations 和 mapActions 都是映射方法
    // 全局级别的映射
    ...mapMutations(['subCount', 'changeTitle']),
    ...mapActions(['changeCountAction']),

    // 分模块的映射
    ...mapMutations('setting', ['setTheme']),
    ...mapMutations('user', ['setUser']),
    ...mapActions('user', ['setUserSecond'])
    // handleSub (n) {
    //   this.subCount(n)
    // }
  }
}
</script>

<style lang="css" scoped>
.box {
  border: 3px solid #ccc;
  width: 400px;
  padding: 10px;
  margin: 20px;
}
h2 {
  margin-top: 10px;
}
</style>

(3)辅助函数:mapMutations

mapMutations和mapState很像,它是把位于Mutations中的方法提取出来,映射到组件methods中

subCount (state, n) {
      state.count -= n
    },
import { mapMutations } from 'vuex'

methods:{
	...mapMutations(['subCount']),
}
 methods: {
   subCount(n){
      this.$store.commit('subCount', n)
    }
  }

五.核心概念 - actions

说明:mutations必须是同步的(便于检测数据变化,记录调式)

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

1.提供action方法

  actions: {
    setAsynCount (context, num) {
      // 一秒后,给一个数,去修改num
      setTimeout(() => {
        context.commit('changeCount', num)
      }, 1000)
    }
  },

2.页面中dispatch调用

this.$store.dispatch('setAsyncCount',200)

辅助函数 - mapActions

maoActions是把位于actions中的方法提取出来,映射到组件methods中

 actions: {
    // context 上下文 (此处未分模块,可以当成store仓库)
    // context.commit('mutation名字', 额外参数)
    changeCountAction (context, num) {
      // 这里是setTimeout模拟异步,以后大部分场景是发请求
      setTimeout(() => {
        context.commit('changeCount', num)
      }, 1000)
    }
  },
import {  mapActions } from 'vuex'

 methods: {
    ...mapActions(['changeCountAction']),
 }
methods:{
	changeCountAction(n){
		this.$store.dispatch('changeCountAction',n)
	},
}

六.核心概念 - getters

说明:从state中派生出一些状态,这些状态是依赖state,此时会用到getters
1.定义getters

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

2.访问getters
①通过store访问getters{{ $store.getters.filterList }}
②通过辅助函数mapGetters映射

computed:{
	...mapGetters(['filterList'])
},

七.核心概念 - 模块module(进阶语法)

由于vuex使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿,变得难以维护
在这里插入图片描述
模块拆分:store/modules/user.js

// user模块
const state = {
  userInfo: {
    name: 'zs',
    age: 18
  },
  score: 80
}
const mutations = { }
const actions = {}
const getters = {}
export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
}

尽管已经分模块了,但其实子模块的状态,还是会挂到根级别的state中,属性名就是模块名
使用模块总的数据:
①直接通过模块名访问$store.state.模块名.xxx
②通过mapState(‘模块名’,[‘xxx’]),需要开启命名空间

export default {
  namespaced: true
}
// user模块
const state = {
  userInfo: {
    name: 'zs',
    age: 18
  },
  score: 80
}
const mutations = {
  setUser (state, newUserInfo) {
    state.userInfo = newUserInfo
  }
}
const actions = {
  setUserSecond (context, newUserInfo) {
    // 将异步在action中进行封装
    setTimeout(() => {
      // 调用mutation   context上下文,默认提交的就是自己模块的action和mutation
      context.commit('setUser', newUserInfo)
    }, 1000)
  }
}
const getters = {
  // 分模块后,state指代子模块的state
  UpperCaseName (state) {
    return state.userInfo.name.toUpperCase()
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
}

购物车案例

cart-footer

<template>
  <div class="footer-container">
    <!-- 中间的合计 -->
    <div>
      <span>共 {{ total }} 件商品,合计:</span>
      <span class="price">¥{{ totalPrice }}</span>
    </div>
    <!-- 右侧结算按钮 -->
    <button class="btn btn-success btn-settle">结算</button>
  </div>
</template>

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

<style lang="less" scoped>
.footer-container {
  background-color: white;
  height: 50px;
  border-top: 1px solid #f8f8f8;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding: 0 10px;
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  z-index: 999;
}

.price {
  color: red;
  font-size: 13px;
  font-weight: bold;
  margin-right: 10px;
}

.btn-settle {
  height: 30px;
  min-width: 80px;
  margin-right: 20px;
  border-radius: 20px;
  background: #42b983;
  border: none;
  color: white;
}
</style>

cart-header

<template>
  <div class="header-container">购物车案例</div>
</template>

<script>
export default {
  name: 'CartHeader'
}
</script>

<style lang="less" scoped>
.header-container {
  height: 50px;
  line-height: 50px;
  font-size: 16px;
  background-color: #42b983;
  text-align: center;
  color: white;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 999;
}
</style>

cart-item

<template>
  <div class="goods-container">
    <!-- 左侧图片区域 -->
    <div class="left">
      <img :src="item.thumb" class="avatar" alt="">
    </div>
    <!-- 右侧商品区域 -->
    <div class="right">
      <!-- 标题 -->
      <div class="title">{{ item.name }}</div>
      <div class="info">
        <!-- 单价 -->
        <span class="price">¥{{ item.price }}</span>
        <div class="btns">
          <!-- 按钮区域 -->
          <button class="btn btn-light" @click="btnClick(-1)">-</button>
          <span class="count">{{ item.count }}</span>
          <button class="btn btn-light" @click="btnClick(1)">+</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'CartItem',
  methods: {
    btnClick (step) {
      const newCount = this.item.count + step
      const id = this.item.id

      if (newCount < 1) return

      this.$store.dispatch('cart/updateCountAsync', {
        id,
        newCount
      })
    }
  },
  props: {
    item: {
      type: Object,
      required: true
    }
  }
}
</script>

<style lang="less" scoped>
.goods-container {
  display: flex;
  padding: 10px;
  + .goods-container {
    border-top: 1px solid #f8f8f8;
  }
  .left {
    .avatar {
      width: 100px;
      height: 100px;
    }
    margin-right: 10px;
  }
  .right {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    flex: 1;
    .title {
      font-weight: bold;
    }
    .info {
      display: flex;
      justify-content: space-between;
      align-items: center;
      .price {
        color: red;
        font-weight: bold;
      }
      .btns {
        .count {
          display: inline-block;
          width: 30px;
          text-align: center;
        }
      }
    }
  }
}

.custom-control-label::before,
.custom-control-label::after {
  top: 3.6rem;
}
</style>

cart

import axios from 'axios'
export default {
  namespaced: true,
  state () {
    return {
      // 购物车数据 [{}, {}]
      list: []
    }
  },
  mutations: {
    updateList (state, newList) {
      state.list = newList
    },
    // obj: { id: xxx, newCount: xxx }
    updateCount (state, obj) {
      // 根据 id 找到对应的对象,更新count属性即可
      const goods = state.list.find(item => item.id === obj.id)
      goods.count = obj.newCount
    }
  },
  actions: {
    // 请求方式:get
    // 请求地址:http://localhost:3000/cart
    async getList (context) {
      const res = await axios.get('http://localhost:3000/cart')
      context.commit('updateList', res.data)
    },
    // 请求方式:patch
    // 请求地址:http://localhost:3000/cart/:id值  表示修改的是哪个对象
    // 请求参数:
    // {
    //   name: '新值',  【可选】
    //   price: '新值', 【可选】
    //   count: '新值', 【可选】
    //   thumb: '新值'  【可选】
    // }
    async updateCountAsync (context, obj) {
      // 将修改更新同步到后台服务器
      await axios.patch(`http://localhost:3000/cart/${obj.id}`, {
        count: obj.newCount
      })
      // 将修改更新同步到 vuex
      context.commit('updateCount', {
        id: obj.id,
        newCount: obj.newCount
      })
    }
  },
  getters: {
    // 商品总数量 累加count
    total (state) {
      return state.list.reduce((sum, item) => sum + item.count, 0)
    },
    // 商品总价格 累加count * price
    totalPrice (state) {
      return state.list.reduce((sum, item) => sum + item.count * item.price, 0)
    }
  }
}

app

<template>
  <div class="app-container">
    <!-- Header 区域 -->
    <cart-header></cart-header>

    <!-- 商品 Item 项组件 -->
    <cart-item v-for="item in list" :key="item.id" :item="item"></cart-item>

    <!-- Foote 区域 -->
    <cart-footer></cart-footer>
  </div>
</template>

<script>
import CartHeader from '@/components/cart-header.vue'
import CartFooter from '@/components/cart-footer.vue'
import CartItem from '@/components/cart-item.vue'

import { mapState } from 'vuex'

export default {
  name: 'App',
  created () {
    this.$store.dispatch('cart/getList')
  },
  computed: {
    ...mapState('cart', ['list'])
  },
  components: {
    CartHeader,
    CartFooter,
    CartItem
  }
}
</script>

<style lang="less" scoped>
.app-container {
  padding: 50px 0;
  font-size: 14px;
}
</style>


http://www.kler.cn/a/538973.html

相关文章:

  • sqlite 查看表结构
  • (done) openMP学习 (Day13: 线程私有数据和如何支持库(Pi again),蒙特卡洛计算 Pi,线性同余法)
  • 企业FTP替代升级,实现传输大文件提升100倍!
  • 2025年日祭
  • 解构赋值在 TypeScript 中的妙用:以 Babylon.js 的 loadModel 函数为例
  • Docker安装Mysql
  • 单元测试的入门实践与应用
  • java-异常家族梳理(流程图)
  • Academy Sports + Outdoors EDI:体育零售巨头的供应链“中枢神经”
  • 掌握 CSS Flexbox 布局,轻松实现复杂网页设计
  • 利用MATLAB计算梁单元刚度矩阵,并组装成总体刚度矩阵
  • python:面向对象案例烤鸡翅
  • Cherry Studio:一站式多模型AI交互平台深度解析 可配合大模型搭建私有知识库问答系统
  • 基于DeepSeek API和VSCode的自动化网页生成流程
  • 详解策略模式
  • idea如何使用AI编程提升效率-在IntelliJ IDEA 中安装 GitHub Copilot 插件的步骤-卓伊凡
  • matlab simulink 汽车四分之一模型轮胎带阻尼
  • 体验 DeepSeek-R1:解密 1.5B、7B、8B 版本的强大性能与应用
  • 掌握API和控制点(从Java到JNI接口)_38 JNI从C调用Java函数 01
  • Coze(扣子)+ Deepseek:多Agents智能体协作开发新范式
  • Mysql系列之--字符集
  • 全星8D客诉管理软件系统——高效解决内外部客诉处理跟踪管理效率
  • Linux中设置开机运行指令
  • MySQL 主从读写分离实现方案(一)—MariaDB MaxScale实现mysql8读写分离
  • JavaScript 在 VSCode 中的优势与应用
  • 李飞飞团队 S1 技术思路:低成本实现高效推理模型