7、Vue 核心技术与实战 day07

news2024/12/22 23:49:06

1.1 vuex概述

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

1.创建项目

vue create vuex-demo

2.创建三个组件, 目录如下

|-components
|--Son1.vue
|--Son2.vue
|-App.vue

3.源代码如下

App.vue在入口组件中引入 Son1 和 Son2 这两个子组件

<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>

main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')

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>

1.3 创建一个空仓库

PS:
vue2对应router3、vuex3
vue3对应router4、vuex4
因为创建vue时没有勾选vuex,所以要装包,勾选就不需要装包了

src → store → index.js

// 在这里存放的就是 Vuex 相关的核心代码
import Vue from 'vue'
import Vuex from 'vuex'

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

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

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

main.js

import Vue from 'vue'
import App from './App.vue'
import store from '@/store/index'

Vue.config.productionTip = false

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

1.4 核心概念 - state 状态

目标:明确如何给仓库 提供 数据,如何 使用 仓库的数据

1. 提供数据:

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

src - store - index.js

// 在这里存放的就是 Vuex 相关的核心代码
import Vue from 'vue'
import Vuex from 'vuex'

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

// 创建仓库
const store = new Vuex.Store({
  // 通过state可以提供数据(所有组件共享的数据)
  state: {
    title: '大标题',
    count: 100
  }
}
)

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

2. 使用数据:

① 通过 store 直接访问

App.vue

<template>
  <div id="app">
    <h1>根组件 - {{ $store.state.title }}</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',
  created () {
    console.log(this.$store.state.title)
  },
  data: function () {
    return {

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

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

main.js

import Vue from 'vue'
import App from './App.vue'
import store from '@/store/index'
console.log(store.state.count)

Vue.config.productionTip = false

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

② 通过辅助函数 (简化)
mapState是辅助函数,帮助我们把 store中的数据 自动 映射到 组件的计算属性中

App.vue

<template>
  <div id="app">
    <h1>根组件 - {{ title }}</h1>
    <input 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.$store.state.title)
  },
  computed: {
    ...mapState(['count', 'title'])
  },
  data: function () {
    return {

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

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

1.5 核心概念 - mutations


son.vue(不开严格模式可以用)

methods: {
    handleAdd () {
      // 错误代码(vue默认不会监测,检测需要成本)
    //   this.$store.state.count++
    //   console.log(this.$store.state.count)

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


str - store - index.js

 mutations: {
    // 所有mutation函数,第一个参数,都是state
    addCount (state) {
      // 修改数据
      state.count += 1
    },
    addFive (state) {
      state.count += 5
    }
  }

Son1.vue

methods: {
  handleAdd () {
    // 错误代码(vue默认不会监测,检测需要成本)
  //   this.$store.state.count++
  //   console.log(this.$store.state.count)

    // 应该通过 mutation 核心概念,进行修改数据
    this.$store.commit('addCount')
  },
  addFive () {
    this.$store.commit('addFive')
  }
}
}


str - store - index.js

mutations: {
    // 所有mutation函数,第一个参数,都是state
    addCount (state, n) {
      // 修改数据
      state.count += n
    },
    changeTitle (state, newTitle) {
      state.title = newTitle
    }
  }

Son1.vue

methods: {
  handleAdd (n) {
    // 错误代码(vue默认不会监测,检测需要成本)
  //   this.$store.state.count++
  //   console.log(this.$store.state.count)

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

    // 调用带参数的mutation函数
    this.$store.commit('addCount', n)
  },
  changeFn () {
    this.$store.commit('changeTitle', '黑马程序员')
  }
}

1.5.1 核心概念 - mutations - 练习1


str - store - index.js

 //   2.通过mutations可以提供修改数据的方法
  mutations: {
    // 所有mutation函数,第一个参数,都是state
    addCount (state, n) {
      // 修改数据
      state.count += n
    },
    subCount (state, n) {
      state.count -= n
    },
    changeTitle (state, newTitle) {
      state.title = newTitle
    }
  }

Son2.vue

 methods: {
    handleSub (n) {
      this.$store.commit('subCount', n)
    }
  }

1.5.2 核心概念 - mutations - 练习2

src - store - index.js

// 在这里存放的就是 Vuex 相关的核心代码
import Vue from 'vue'
import Vuex from 'vuex'

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

// 创建仓库
const store = new Vuex.Store({
  // 严格模式(有利于初学者,检测不规范的代码 => 上线时需要关闭)
  strict: true,
  // 通过state可以提供数据(所有组件共享的数据)
  state: {
    title: '仓库大标题',
    count: 100
  },
  //   2.通过mutations可以提供修改数据的方法
  mutations: {
    // 所有mutation函数,第一个参数,都是state
    addCount (state, n) {
      // 修改数据
      state.count += n
    },
    subCount (state, n) {
      state.count -= n
    },
    changeTitle (state, newTitle) {
      state.title = newTitle
    },
    changeCount (state, newCount) {
      state.count = newCount
    }
  }
}
)

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

Son2.vue

<template>
  <div id="app">
    <h1>根组件 - {{ title }}</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.$store.state.title)
  },
  computed: {
    ...mapState(['count', 'title'])
  },
  data: function () {
    return {

    }
  },
  components: {
    Son1,
    Son2
  },
  methods: {
    handleInput (e) {
      // console.log(+e.target.value)
      const num = +e.target.value
      this.$store.commit('changeCount', num)
    }

  }
}
</script>

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

1.6 辅助函数 - mapMutations


PS:就是在仓库里面写好方法,通过辅助函数mapMutation把仓库中的方法提取出来了,可以直接使用
Son2.vue

<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>
    </div>
  </template>

<script>
import { mapState, mapMutations } from 'vuex'
export default {
  name: 'Son2Com',
  computed: {
    ...mapState(['count', 'title'])
  },
  methods: {
    ...mapMutations(['subCount', 'changeCount'])
    // handleSub (n) {
    //   // this.$store.commit('subCount', 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>

1.7 核心概念 - actions


str - store - index.js

// 在这里存放的就是 Vuex 相关的核心代码
import Vue from 'vue'
import Vuex from 'vuex'

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

// 创建仓库
const store = new Vuex.Store({
  // 严格模式(有利于初学者,检测不规范的代码 => 上线时需要关闭)
  strict: true,
  // 通过state可以提供数据(所有组件共享的数据)
  state: {
    title: '仓库大标题',
    count: 100
  },
  //   2.通过mutations可以提供修改数据的方法
  mutations: {
    // 所有mutation函数,第一个参数,都是state
    addCount (state, n) {
      // 修改数据
      state.count += n
    },
    subCount (state, n) {
      state.count -= n
    },
    changeTitle (state, newTitle) {
      state.title = newTitle
    },
    changeCount (state, newCount) {
      state.count = newCount
    }
  },
  //   3.actions 处理异步
  // 注意: 不能直接操作 state, 操作 state, 还是需要 commit mutation
  actions: {
    // context 上下文(此处未分模块,可以当成store仓库)
    // context.commit('mutation名字',额外参数)
    changeCountAction (context, num) {
      // 这里是setTimeout模拟异步,以后大部分场景是发请求
      setTimeout(() => {
        context.commit('changeCount', num)
      }, 1000)
    }
  }

}
)

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

Son1.vue

<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="changeFn">改标题</button>
      <button @click="handleChange">一秒之后修改成666</button>
    </div>
  </template>

<script>
export default {
  name: 'Son1Com',
  methods: {
    handleAdd (n) {
      // 错误代码(vue默认不会监测,检测需要成本)
    //   this.$store.state.count++
    //   console.log(this.$store.state.count)

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

      // 调用带参数的mutation函数
      this.$store.commit('addCount', n)
    },
    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>

1.8 辅助函数 - mapActions


Son2.vue

<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)">一秒之后修改成888</button>
    </div>
  </template>

<script>
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
  name: 'Son2Com',
  computed: {
    ...mapState(['count', 'title'])
  },
  methods: {
    ...mapMutations(['subCount', 'changeCount']),
    ...mapActions(['changeCountAction'])
    // handleSub (n) {
    //   // this.$store.commit('subCount', 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>

1.9 核心概念 - getters


src - store - index.js

// 在这里存放的就是 Vuex 相关的核心代码
import Vue from 'vue'
import Vuex from 'vuex'

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

// 创建仓库
const store = new Vuex.Store({
  // 严格模式(有利于初学者,检测不规范的代码 => 上线时需要关闭)
  strict: true,
  // 通过state可以提供数据(所有组件共享的数据)
  state: {
    title: '仓库大标题',
    count: 100,
    list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  },
  //   2.通过mutations可以提供修改数据的方法
  mutations: {
    // 所有mutation函数,第一个参数,都是state
    addCount (state, n) {
      // 修改数据
      state.count += n
    },
    subCount (state, n) {
      state.count -= n
    },
    changeTitle (state, newTitle) {
      state.title = newTitle
    },
    changeCount (state, newCount) {
      state.count = newCount
    }
  },
  //   3.actions 处理异步
  // 注意: 不能直接操作 state, 操作 state, 还是需要 commit mutation
  actions: {
    // context 上下文(此处未分模块,可以当成store仓库)
    // context.commit('mutation名字',额外参数)
    changeCountAction (content, num) {
      // 这里是setTimeout模拟异步,以后大部分场景是发请求
      setTimeout(() => {
        content.commit('changeCount', num)
      }, 1000)
    }
  },
  //   4.getters 类似于计算属性
  getters: {
    // 注意点:
    // 1.形参第一个参数,就是state
    // 2.必须有返回值,返回值就是getters的值
    filterList (state) {
      return state.list.filter(item => item > 5)
    }

  }

}
)

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

方法一:通过 store 访问 getters

Son1.vue

<div>{{ $store.state.list }}</div>
<div>{{ $store.getters.filterList }}</div>

方法二:通过辅助函数 mapGetters 映射

Son2.vue

<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)">一秒之后修改成888</button>
      <hr>
    <div>{{ filterList }}</div>
    </div>
  </template>

<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
  name: 'Son2Com',
  computed: {
    // mapStore 和 mapGetters 都是映射属性
    ...mapState(['count', 'title']),
    ...mapGetters(['filterList'])
  },
  methods: {
    // mapMutation 和 mapActions 都是映射方法
    ...mapMutations(['subCount', 'changeCount']),
    ...mapActions(['changeCountAction'])
    // handleSub (n) {
    //   // this.$store.commit('subCount', 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>

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

1.10.1module 模块的创建


模块拆分:
user模块: store/modules/user.js


index.js
// 在这里存放的就是 Vuex 相关的核心代码
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import setting from './modules/setting'

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

// 创建仓库
const store = new Vuex.Store({
  // 严格模式(有利于初学者,检测不规范的代码 => 上线时需要关闭)
  strict: true,
  // 通过state可以提供数据(所有组件共享的数据)
  state: {
    title: '仓库大标题',
    count: 100,
    list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  },
  //   2.通过mutations可以提供修改数据的方法
  mutations: {
    // 所有mutation函数,第一个参数,都是state
    addCount (state, n) {
      // 修改数据
      state.count += n
    },
    subCount (state, n) {
      state.count -= n
    },
    changeTitle (state, newTitle) {
      state.title = newTitle
    },
    changeCount (state, newCount) {
      state.count = newCount
    }
  },
  //   3.actions 处理异步
  // 注意: 不能直接操作 state, 操作 state, 还是需要 commit mutation
  actions: {
    // context 上下文(此处未分模块,可以当成store仓库)
    // context.commit('mutation名字',额外参数)
    changeCountAction (content, num) {
      // 这里是setTimeout模拟异步,以后大部分场景是发请求
      setTimeout(() => {
        content.commit('changeCount', num)
      }, 1000)
    }
  },
  //   4.getters 类似于计算属性
  getters: {
    // 注意点:
    // 1.形参第一个参数,就是state
    // 2.必须有返回值,返回值就是getters的值
    filterList (state) {
      return state.list.filter(item => item > 5)
    }
  },
  //   5.modules模块
  modules: {
    user,
    setting
  }
})

// 导出给main.js使用
export default store
modules - setting.js
// setting模块
const state = {
  theme: 'light', // 主题色
  desc: '测试demo'
}
const mutations = {}
const actions = {}
const getters = {}
export default {
  state,
  mutations,
  actions,
  getters
}

1.10.2 state 的访问语法

方法一:直接通过模块名访问 $store.state.模块名.xxx

Son2.vue

<!-- 测试访问模块中的state - 原生 -->
      <div>{{ $store.state.user.userInfo.name }}</div>
      <div>{{ $store.state.setting.theme }}</div>
方法二:通过 mapState 映射

Son2.vue

<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)">一秒之后修改成888</button>
      <hr>
    <div>{{ filterList }}</div>
    <hr>
    <!-- 访问模块中的state -->
    <div>{{ user.userInfo.name }}</div>
    <div>{{ setting.theme }}</div>
    <div>user模块的数据{{ userInfo }}</div>
    <div>setting模块的数据{{ theme }}</div>
    </div>
  </template>

<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
  name: 'Son2Com',
  computed: {
    // mapStore 和 mapGetters 都是映射属性
    ...mapState(['count', 'title', 'user', 'setting']),
    ...mapState('user', ['userInfo']),
    ...mapState('setting', ['theme', 'desc']),
    ...mapGetters(['filterList'])
  },
  methods: {
    // mapMutation 和 mapActions 都是映射方法
    ...mapMutations(['subCount', 'changeCount']),
    ...mapActions(['changeCountAction'])
    // handleSub (n) {
    //   // this.$store.commit('subCount', 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>

setting.js

// setting模块
const state = {
theme: 'light', // 主题色
desc: '测试demo'
}
const mutations = {}
const actions = {}
const getters = {}
export default {
namespaced: true,
state,
mutations,
actions,
getters
}

1.10.3 getters 的访问语法

方法一:直接通过模块名访问 $store.getters['模块名/xxx ']

user.js

// user模块
const state = {
  userInfo: {
    name: 'zs',
    age: 18
  },
  score: 80
}
const mutations = {}
const actions = {}
const getters = {
  // 分模块后,state指代子模块的state
  UpperCaseName (state) {
    return state.userInfo.name.toUpperCase()
  }
}
export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
}

Son1.vue

<!-- 测试访问模块中的getters - 原生 - -->
      <div>{{ $store.getters['user/UpperCaseName'] }}</div>
方法二:通过 mapGetters 映射

Son2.vue

<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)">一秒之后修改成888</button>
      <hr>
    <div>{{ filterList }}</div>
    <hr>
    <!-- 访问模块中的state -->
    <div>{{ user.userInfo.name }}</div>
    <div>{{ setting.theme }}</div>
    <div>user模块的数据{{ userInfo }}</div>
    <div>setting模块的数据{{ theme }}</div>
    <hr>
    <!-- 访问模块中的getters -->
    <div>{{ UpperCaseName }}</div>
    </div>
  </template>

<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
  name: 'Son2Com',
  computed: {
    // mapStore 和 mapGetters 都是映射属性
    ...mapState(['count', 'title', 'user', 'setting']),
    ...mapState('user', ['userInfo']),
    ...mapState('setting', ['theme', 'desc']),
    ...mapGetters(['filterList']),
    ...mapGetters('user', ['UpperCaseName'])
  },
  methods: {
    // mapMutation 和 mapActions 都是映射方法
    ...mapMutations(['subCount', 'changeCount']),
    ...mapActions(['changeCountAction'])
    // handleSub (n) {
    //   // this.$store.commit('subCount', 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>

1.10.4 mutation 的调用语法

方法一:直接通过 store 调用

user.js

// user模块
const state = {
  userInfo: {
    name: 'zs',
    age: 18
  },
  score: 80
}
const mutations = {
  setUser (state, newUserinfo) {
    state.userInfo = newUserinfo
  }
}
const actions = {}
const getters = {
  // 分模块后,state指代子模块的state
  UpperCaseName (state) {
    return state.userInfo.name.toUpperCase()
  }
}
export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
}

Son1.vue

<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="changeFn">改标题</button>
      <button @click="handleChange">一秒之后修改成666</button>
      <hr>
      <div>{{ $store.state.list }}</div>
      <div>{{ $store.getters.filterList }}</div>

      <hr>
      <!-- 测试访问模块中的state - 原生 -->
      <div>{{ $store.state.user.userInfo.name }}</div>
      <button @click="updateUser">更改个人信息</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',
  methods: {
    handleAdd (n) {
      // 错误代码(vue默认不会监测,检测需要成本)
    //   this.$store.state.count++
    //   console.log(this.$store.state.count)

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

      // 调用带参数的mutation函数
      this.$store.commit('addCount', n)
    },
    changeFn () {
      this.$store.commit('changeTitle', '黑马程序员')
    },
    handleChange () {
      // 调用action
      // this.$store.dispatch('action名字', 额外参数)
      this.$store.dispatch('changeCountAction', 666)
    },
    updateUser () {
      // this.$store.commit('模块名/mutation名', 额外参数)
      this.$store.commit('user/setUser', {
        name: 'xiaowang',
        age: 25
      })
    }
  }
}
</script>

  <style lang="css" scoped>
  .box{
    border: 3px solid #ccc;
    width: 400px;
    padding: 10px;
    margin: 20px;
  }
  h2 {
    margin-top: 10px;
  }
  </style>
方法二:通过 mapMutations 映射

Son2.vue

<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)">一秒之后修改成888</button>
      <hr>
    <div>{{ filterList }}</div>
    <hr>
    <!-- 访问模块中的state -->
    <div>{{ user.userInfo.name }}</div>
    <div>{{ setting.theme }}</div>
    <div>user模块的数据{{ userInfo }}</div>
    <button @click="setUser({name: 'xiaoli', age: 80})">更新个人信息</button>
    <div>setting模块的数据{{ theme }}</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: {
    // mapStore 和 mapGetters 都是映射属性
    ...mapState(['count', 'title', 'user', 'setting']),
    ...mapState('user', ['userInfo']),
    ...mapState('setting', ['theme', 'desc']),
    ...mapGetters(['filterList']),
    ...mapGetters('user', ['UpperCaseName'])
  },
  methods: {
    // mapMutation 和 mapActions 都是映射方法
    // 全局级别的映射
    ...mapMutations(['subCount', 'changeCount']),
    ...mapActions(['changeCountAction']),
    // handleSub (n) {
    //   // this.$store.commit('subCount', n)
    //   this.subCount(n)
    // }
    // 分模块的映射
    ...mapMutations('setting', ['setTheme']),
    ...mapMutations('user', ['setUser'])
  }

}
</script>

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

1.10.5 action 的调用语法 (同理 - 直接类比 mutation 即可)

方法一:直接通过 store 调用

user.js

const actions = {
  setUserSecond (context, newUserinfo) {
    // 将异步在action中进行封装
    setTimeout(() => {
      // 调用mutation  context上下文,默认提交的就是自己模块的action和mutation
      context.commit('setUser', newUserinfo)
    }, 1000)
  }
}	

Son1.vue

updaterUser2 () {
      // 调用action dispatch
      this.$store.dispatch('user/setUserSecond', {
        name: 'xiaohong',
        age: 28
      })
    },
方法二:通过 mapMutations 映射

Son2.vue

    ...mapActions('user', ['setUserSecond'])
    <button @click="setUserSecond({name: 'xiaoli', age: 80})">一秒后更新个人信息</button>

1.11 综合案例 - 购物车

1.目标:功能分析,创建项目,构建分析基本结构

  1. 功能模块分析
    ① 请求动态渲染购物车,数据存 vuex
    ② 数字框控件 修改数据
    动态计算 总价和总数量
  2. 脚手架新建项目 (注意:勾选vuex)
    vue create vue-cart-demo
  3. 将原本src内容清空,替换成素材的《vuex-cart-准备代码》并分析

2.目标:构建 cart 购物车模块

说明:既然明确数据要存 vuex,建议分模块存,购物车数据存 cart 模块,将来还会有 user 模块,article 模块…

、

3.目标:基于 json-server 工具,准备后端接口服务环境

  1. 安装全局工具 json-server (全局工具仅需要安装一次)官网
    yarn global add json-server 或 npm i json-server -g
  2. 代码根目录新建一个 db 目录
  3. 将资料 index.json 移入 db 目录
  4. 进入 db 目录,执行命令,启动后端接口服务
    json-server index.json
  5. 访问接口测试 http://localhost:3000/cart

推荐: json-server --watch index.json (可以实时监听 json 文件的修改)

4.目标:请求获取数据存入 vuex, 映射渲染


npm install axios --save --legacy-peer-deps

  • 1.安装axios
npm i axios

安装过程中出现如下报错: code ERESOLVE ERESOLVE could not resolve
出现问题的原因:由于npm不同版本库之间命令不兼容。

解决方法:
终端输入命令:npm install axios -save --legacy-peer-deps

5.目标:修改数量功能完成

6.目标:底部 getters 统计


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
  }
})

modules - cart.js

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

components - 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) {
      // console.log(step)
      const newCount = this.item.count + step
      const id = this.item.id
      // console.log(id, newCount)
      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-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>

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

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

相关文章

利用LLM模型微调的短课程;钉钉宣布开放智能化底座能力

&#x1f989; AI新闻 &#x1f680; 钉钉宣布开放智能化底座能力AI PaaS&#xff0c;推动企业数智化转型发展 摘要&#xff1a;钉钉在生态大会上宣布开放智能化底座能力AI PaaS&#xff0c;与生态伙伴探寻企业服务的新发展道路。AI PaaS结合5G、云计算和人工智能技术的普及和…

【TS】typescript基础知识

一、类型注解 : number就是类型注解&#xff0c;为变量添加类型约束的方式&#xff0c;约定了什么类型&#xff0c;就只能给变量赋什么类型的值 let age: number 18二、变量命名规则和规范 命名规则&#xff1a;变量名称只能出现数字&#xff0c;字母&#xff0c;下划线(_)…

无涯教程-Python - 环境设置

Python在包括Linux和Mac OS X在内的各种平台上都可用。让无涯教程了解如何安装设置Python环境。 最新的源代码,二进制文件,文档,新闻等可在Python的官方网站上找到https://www.python.org/ 您可以从https://www.python.org/doc/该文档有HTML,PDF和PostScript格式。 安装Pyth…

SD内存卡格式化后如何数据恢复教程

SD内存卡是一种常用的存储设备&#xff0c;可用于储存各种类型的数据&#xff0c;包括照片、音频、视频等。在使用过程中&#xff0c;你是否因为SD卡格式化而失去了重要数据而感到困扰&#xff1f;那么这篇文章的内容将能够帮助您快速有效地恢复格式化后的数据。 图片来源于网络…

python编写四画面同时播放swap视频

当代技术让我们能够创建各种有趣和实用的应用程序。在本篇博客中&#xff0c;我们将探索一个基于wxPython和OpenCV的四路视频播放器应用程序。这个应用程序可以同时播放四个视频文件&#xff0c;并将它们显示在一个GUI界面中。 C:\pythoncode\new\smetimeplaymp4.py 准备工作…

Unity实现广告滚动播放、循环播放、鼠标切换的效果

效果&#xff1a; 场景结构&#xff1a; 特殊物体&#xff1a;panel下面用排列组件horizent layout group放置多个需要显示的面板&#xff0c;用mask遮罩好。 using System.Collections; using System.Collections.Generic; using DG.Tweening; using UnityEngine; using Unity…

达梦数据库curd的简单使用

目录 添加表空间 删除表空间 添加表空间 create tablespace "test_db" datafile E:\dm_data\DM6.DBF size 32--制定路径 create tablespace “test_db”&#xff1a;创建一个名为test_db的表空间 datafile ‘DM6.DBF’&#xff1a;表示数据文件名为TDM6.DBF&…

胖小酱之听话

首先&#xff0c;我们来看看何为“物质奖励”&#xff1f; 所谓的物质奖励&#xff0c;简单通俗地说就是运用物质上的满足&#xff0c;去调动孩子的积极、主动和创造能力&#xff0c;其中物质奖励的形式一般体现在各种实物的需求方面&#xff0c;比如游戏机、玩具车、手机…

富仕转债上市价格预测

富仕转债 基本信息 转债名称&#xff1a;富仕转债&#xff0c;评级&#xff1a;AA-&#xff0c;发行规模&#xff1a;5.7亿元。 正股名称&#xff1a;四会富仕&#xff0c;今日收盘价&#xff1a;36.29元&#xff0c;转股价格&#xff1a;41.77元。 当前转股价值 转债面值 / 转…

Windows共享文件夹,用户密码访问

Windows共享文件夹&#xff0c;用户密码访问 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.先创建一个用户 计算机右键----管理----本地用户和组----点击用户进去---右键新建用户 这里以kk为例 2.找到你想共享的文件夹 3.共享-想共享的文件夹---右键---属性---共…

优化Python代理爬虫的应用

当我们在资源受限的环境中使用Python代理爬虫时&#xff0c;我们需要采取一些优化措施&#xff0c;以确保程序的高效性和稳定性。在本文中&#xff0c;我将分享一些关于如何优化Python代理爬虫在资源受限环境下的应用的实用技巧。 首先我们来了解&#xff0c;哪些情况算是资源…

Anolis 8.6 下 Redis 7.2.0 集群搭建和配置

Redis 7.2.0 搭建和集群配置 一.Redis 下载与单机部署1.Redis 下载2.虚拟机配置3.Redis 单机源码安装和测试4.Java 单机连接测试1.Pom 依赖2.配置文件3.启动类4.配置类5.单元测试6.测试结果 二.Redis 集群部署1.主从1.从节点配置2.Java 测试 2.哨兵1.哨兵节点配置2.复制一个哨兵…

tkinter自定义多参数对话框

文章目录 参数对话框自定义参数对话框 参数对话框 tkinter提供了三种参数对话框&#xff0c;用于输出浮点型、整型和字符串&#xff0c;分别是askfloat, askinteger以及askstring&#xff0c;使用方法如下 代码如下 import tkinter as tk from tkinter.simpledialog import *…

使用StreamLold写入 Starrocks报错:Caused by org

问题描述 使用StreamLoad写入Starrocks报错&#xff0c;报这个错误:Caused by: org.apache.http.ProtocolException: Content-Length header already present 代码案例 引入依赖 <!-- Starrocks使用StreamLoad发送Http请求 --><dependency><groupId>or…

三维模型OBJ格式轻量化压缩变形现象分析

三维模型OBJ格式轻量化压缩变形现象分析 三维模型的OBJ格式轻量化压缩是一种常见的处理方法&#xff0c;它可以减小模型文件的体积&#xff0c;提高加载和渲染效率。然而&#xff0c;在进行轻量化压缩过程中&#xff0c;有时会出现模型变形的现象&#xff0c;即压缩后的模型与…

【面试高频题】值得仔细推敲的贪心及其证明

题目描述 这是 LeetCode 上的 「1846. 减小和重新排列数组后的最大元素」 &#xff0c;难度为 「中等」。 Tag : 「贪心」 给你一个正整数数组 arr。 请你对 arr 执行一些操作&#xff08;也可以不进行任何操作&#xff09;&#xff0c;使得数组满足以下条件&#xff1a; arr 中…

Cesium.Entity图片纹理在不同观察角度有不同亮度

Cesium.Entity图片纹理在不同观察角度有不同亮度 测试代码&#xff1a; viewer.entities.add({rectangle: {coordinates: Cesium.Rectangle.fromDegrees(-92.0, 30.0, -76.0, 40.0),material: "../images/rect.png",} }); 测试图片&#xff1a; rect.png 这个图片…

vue 学习笔记 简单实验

1.代码(html) <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"counter">Counter: {{ counter }} </div> <script> const Counter {data() {return {counter: 5}} } Vue.cr…

★80交流驱动器通过rs485接口设置速度(附ascii表)

1抓取的数据及解析 2手册上的通信协议及数据帧说明 说明:双向传输项目 3硬件接线注意事项 用的RSJ45端子&#xff0c;双传项目中&#xff0c;一头用的pin6的水晶头子&#xff08;直流离心机上用过是可以的&#xff09;&#xff0c;另一个用的pin8的水晶头子&#xff0c;这里最…

Linux常用命令——dhcrelay命令

在线Linux命令查询工具 dhcrelay 使用dhcrelay命令可以提供中继DHCP和BOOTP请求 补充说明 dhcrelay命令使用dhcrelay命令可以提供中继DHCP和BOOTP请求&#xff0c;从一个没有DHCP服务器的子网直接连接到其它子网内的一个或多个DHCP服务器。该命令在DHCP中继服务器上使用&am…