Vue学习笔记 (第七天2023黑马版)

本文详细介绍了Vue开发中的关键知识点,包括基于VueCLI创建项目、ESLint代码规范的应用、vuex状态管理及其核心概念(mutations、actions和getters),以及如何在实际场景中,如购物车功能,运用vuex进行数据共享和模块化管理。
摘要由CSDN通过智能技术生成

第七天

1.基于VueCli自定义创建项目

创建vue项目,配置选项如下
在这里插入图片描述

2.ESlint代码规范

代码规范:一套写代码约定规则
解决代码规范错误:

  1. 手动修正
    根据错误提示来修正代码,不认识语法报错可以去ESLint规则表中查找具体含义
  2. 自动修正
    基于vscode插件ESLint高亮错误,并通过配置自动帮助我们修复错误
	//当保存时,eslint自动帮我们修复错误
	"editor.codeActionsOnSave": {
        "source.fixAll": true
    },
    //保存代码,不自动格式化
    "editor.formatOnSave": false

3.vuex

vuex概述

1.定义:vuex是一个vue的状态管理工具,状态就是数据

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

3.优势:
- 共同维护一份数据,数据集中化管理
- 响应式变化
- 操作简洁

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

创建三个组件分别为App.vue,Son1.vue,Son2.vue
代码如下:

<!--App.vue-->
<template>
  <div id="app">
    <h1>根组件</h1>
    <input type="text">
    <Son1></Son1>
    <hr>
    <Son2></Son2>
  </div>
</template>

<script>
import Son1 from './components/Son1.vue'
import Son2 from './components/Son2.vue'

export default {
  name: 'app',
  data: function () {
    return {

    }
  },
  components: {
    Son1,
    Son2
  }
}
</script>

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

<!--Son1.vue-->
<template>
  <div class="box">
    <h2>Son1 子组件</h2>
    从vuex中获取的值: <label></label>
    <br />
    <button>值 + 1</button>
  </div>
</template>

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

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

h2 {
  margin-top: 10px;
}
</style>

<!--Son2.vue-->
<template>
  <div class="box">
    <h2>Son2 子组件</h2>
    从vuex中获取的值:<label></label>
    <br />
    <button>值 - 1</button>
  </div>
</template>

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

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

h2 {
  margin-top: 10px;
}
</style>

创建一个空仓库

目标: 安装vuex插件,初始化一个仓库

  1. 安装vuex
npm add vuex@3 --legacy-peer-deps
  1. 新建vuex模块文件
    在这里插入图片描述
  2. 创建仓库
//index.js
// vuex相关核心代码
import Vue from 'vue'
import Vuex from 'vuex'

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

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

// 导出给main.js使用
export default store

  1. main.js挂载
new Vue({
  render: h => h(App),
  store
}).$mount('#app')

如何提供&访问vuex的数据

核心概念 - state状态

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

const store = new Vuex.Store({
  state: {
    数据名: 数据值,
  }
})

2.使用数据:

  • 通过store直接访问
<!--模板中-->
{{ $store.state.xxx }}
//组件逻辑中
this.$store.state.xxx
//js模块中
store.state.xxx
  • 通过辅助函数
    ①导入mapState
import { mapState } from 'vuex'

②数组方式引入state
③展开运算符映射

computed: {
    ...mapState(['count'])
  }

④可以直接使用

<label>{{ count }}</label>

核心概念 - mutations

vuex同样遵循单向数据流,组件中不能直接修改仓库的数据
通过 strict:true 可以开启严格模式

const store = new Vuex.Store({
  strict: true,
  state: {
    title: '大标题',
    count: 100
  }
})
  1. 定义mutations对象,对象中存放修改state的方法
const store = new Vuex.Store({
  strict: true,
  state: {
    title: '大标题',
    count: 100
  },
  mutations: {
    addCount (state) {
      state.count += 1
    },
  }
})
  1. 组件中提交调用mutations
this.$store.commit('addCount')

mutations的传参语法
  1. 提供mutations函数(带参数 - 提交载荷 payload)
 mutations: {
    addCount (state, n) {
      state.count += n
    },
  }
  1. 页面中提交mutation(提交参数只能一个,如果有多个参数,包装成一个对象传递)
this.$store.commit('addCount', n)

辅助函数:mapMutations

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

methods: {
    ...mapMutations(['subCount', 'changeTitle']),//映射
    handleSub (n) {
      this.subCount(n)//直接调用
    }
  }

核心概念 - actions

mutations必须是同步的

actions处理异步操作
  1. 提供action方法
actions: {
    changeCountAction (context, num) {
    //一秒后,给个数,修改num值
      setTimeout(() => {
        context.commit('changeCount', num)
      }, 1000)
    }
  }
  1. 页面dispatch调用
this.$store.dispatch('changeCountAction', 666)

辅助函数 - mapActions

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

import { mapActions } from 'vuex'
//-----------------------------------------
methods: {
    ...mapActions(['changeCountAction']),
  }
//调用
this.changeCountAction(666)

核心概念 - getters

说明:除了state之外,有时我们还需要从state中派生出一些状态,这些状态时依赖state的,此时会用到getters
例如:state中定义了list,为1-10的数组,组件中,需要显示所有大于5的数据

state: {
    list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
  1. 定义getters
    注意点:
    getters函数的第一个参数是state
    getters函数必须要有返回值
getters: {
    filterList (state) {
      return state.list.filter(item => item > 5)
    }
  }
  1. 访问getters
    ①通过store访问getters
{{ $store.getters.filterList }}

②通过辅助函数mapGetters映射

import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
//------------------------------------------------
 computed: {
    ...mapGetters(['filterList'])
},
{{ filterList }}

核心概念 - 模块module

user模块:store/modules/user.js

const state = {
  userInfo: {
    name: 'zs',
    age: 18
  },
  score: 80
}
const mutations = {}
const actions = {}
const getters = {}

export default {
  state,
  mutations,
  actions,
  getters
}

//在index中导入
import user from './modules/user'

const store = new Vuex.Store({
  modules: {
    user,
  }
})

使用模块中的数据
  1. 直接通过模块名访问$store.state.模块名.xxx
 <div>{{ $store.state.setting.theme }}</div>
  1. 通过mapState映射
    默认根级别的映射 mapState([‘xxx’])
 ...mapState(['user', 'setting']),

子模块的映射 mapState(‘模块名’,[‘xxx’]) - 需要开启命名空间

//user.js
export default {
  namespaced: true,//开启命名空间
  state,
  mutations,
  actions,
  getters
}
...mapState('user', ['userInfo']),

使用getters中的数据
  1. 直接通过模块名访问$store.getter[‘模块名/xxx’]
<div>{{ $store.getters['user/UpperCaseName'] }}</div>
  1. 通过mapGetters映射
    默认根级别的映射 mapGetters([‘xxx’])
    子模块的映射 mapState(‘模块名’,[‘xxx’]) - 需要开启命名空间
    ...mapGetters('user', ['UpperCaseName'])

调用子模块的mutation
  1. 直接通过模块名访问$store.commit(‘模块名/xxx’, 额外参数)
this.$store.commit('setting/setTheme', 'pink')
  1. 通过mapMutations映射
    默认根级别的映射 mapMutations([‘xxx’])
    子模块的映射 mapMutations(‘模块名’,[‘xxx’]) - 需要开启命名空间
...mapMutations('setting', ['setTheme']),

调用子模块中action
  1. 直接通过store访问$store.dispatch(‘模块名/xxx’, 额外参数)
this.$store.dispatch('user/setUserSecond', {
        name: 'xiaohong',
        age: 28
      })
  1. 通过mapActions映射
    默认根级别的映射 mapActions([‘xxx’])
    子模块的映射 mapActions(‘模块名’,[‘xxx’]) - 需要开启命名空间
    ...mapActions('user', ['setUserSecond']),

4.综合案例 - 购物车

分析

  1. 构建cart购物车模块
    ①新建store/moudles/cart.js
    ②挂载到vuex仓库上store/index.js
  2. 基于json-server工具,准备后端接口服务环境
    ①安装全局工具json-server(以管理员身份运行)
yarn global add json-server

npm install -g json-server

②代码根目录新建一个db目录
③将资料index.json移入db目录
④进入db目录,执行命令,启动后端接口服务

json-server index.json

⑤访问接口测试http://localhost:3000/cart

  1. 请求获取数据存入vuex,映射渲染
    ①安装axios
npm install axios --save --legacy-peer-deps

②准备actions和mutations
③调用action获取数据

mutations: {
    updateList (state, newList) {
      state.list = newList
    }
  },
  actions: {
    async getList (context) {
      const res = await axios.get('http://localhost:3000/cart')
      context.commit('updateList', res.data)
    }
  },

④动态渲染

  1. 修改数量功能
    ①点击事件
    ②页面中dispatch
this.$store.dispatch('cart/updateCountAsync', {
        id,
        newCount
      })

③提供action函数(更改后端数据)

    async updateCountAsync (context, obj) {
      await axios.patch(`http://localhost:3000/cart/${obj.id}`, {
        count: obj.newCount
      })
      context.commit('updateCount', {
        id: obj.id,
        newCount: obj.newCount
      })
    }

④提供mutation函数(更改前端数据)

    updateCount (state, obj) {
      const goods = state.list.find(item => item.id === obj.id)
      goods.count = obj.newCount
    }
  1. 底部getters统计
    ①提供getters
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)
    }
  }

②使用getters

  computed: {
    ...mapGetters('cart', ['total', 'totalPrice'])
  }

核心代码

//cart.js
import axios from 'axios'

export default {
  namespaced: true,
  state () {
    return {
      list: []
    }
  },
  mutations: {
    updateList (state, newList) {
      state.list = newList
    },
    updateCount (state, obj) {
      const goods = state.list.find(item => item.id === obj.id)
      goods.count = obj.newCount
    }
  },
  actions: {
    async getList (context) {
      const res = await axios.get('http://localhost:3000/cart')
      context.commit('updateList', res.data)
    },
    async updateCountAsync (context, obj) {
      await axios.patch(`http://localhost:3000/cart/${obj.id}`, {
        count: obj.newCount
      })
      context.commit('updateCount', {
        id: obj.id,
        newCount: obj.newCount
      })
    }
  },
  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)
    }
  }
}

<!--cart-footer.vue-->
<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>

<!--App.vue-->
<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>

```html
<!--cart-item.vue-->
<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>

//index.js
import Vue from 'vue'
import Vuex from 'vuex'
import cart from './modules/cart'

Vue.use(Vuex)

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

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值