[TOC]

00-事件总线

目的:掌握非父子组件之间数据通讯
首先:明确一件事件,组件的自定义事件,只能由组件自己来触发。
然后:假设A组件想传值给B组件,AB组件的非父子。其实也是依赖自定义事件来传递

  • 在A组件触发一个自定义事件(myEvent),触发的时候可以传参,参数可以是A组件数据
  • 在B组件触发绑定自定义事件(myEvent),事件的函数接收传参,参数其实是A组件数据
  • 触发事件和绑定事件由另外一个组件负责,A导入它触发事件,B导入它绑定事件,满足自定义事件触发绑定条件。
  • 另外一个组件:我们称为 事件总线 或者 eventBus

画图:
image.png
代码:
eventBus.js

import Vue from 'vue'
export default new Vue({})

com-a.vue

<template>
<div>A组件 <button @click="toB">传递数据给B组件</button></div>
</template>
<script>
  import eventBus from './eventBus.js'    
  export default {
    name: 'ComA',
    data () {
      return {
        count: 911  
      }  
    },
    methods: {
      toB () {
        eventBus.$emit('myEvent', this.count)  
      }  
    }  
  }
</script>

com-b.vue

<template>
  <div>B组件 {{myCount}}</div>
</template>
<script>
import eventBus from './eventBus.js'    
export default {
  name: 'ComA',
  data () {
    return {
      myCount: null  
    }  
  },
  created () {
    eventBus.$on('myEvent', data => {
      this.myCount = data  
    })  
  } 
}
</script>

总结: 事件总线eventBus就是一个对象,提供事件绑定和事件触发功能,其他组件使用这个eventBus进行事件绑定和事件触发进行传参,可以解决非父子组件传值问题。

01-vuex介绍

目的:记住vuex的核心概念:state mutations actions
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
我们理解:

  • vuex是采用集中式管理组件依赖的共享数据的一个工具vue插件,可以解决不同组件数据共享问题。

image.png
看图结论:

  • state 管理数据,管理的数据是响应式的,当数据改变时驱动视图更新。
  • mutations 更新数据,state中的数据只能使用mutations去改变数据。
  • actions 请求数据,响应成功后把数据提交给mutations

image.png

02-vuex使用

目的:掌握vuex基本步骤
具体步骤:

  • 第一步:npm i vuex —save
  • 第二步: 创建store/index.js
  • import vue from ‘vue’
  • import Vuex from ‘vuex’
  • 第三步:Vue.use(Vuex)
  • 第四步:const store = new Vuex.Store({…配置项})
  • 第五步:导出 export default store
  • 第六步:导入main.js 在根实例配置 store 选项指向 store 实例对象 ```javascript // 初始化一个vuex的实例(数据仓库) 导出即可 import Vuex from ‘vuex’ import Vue from ‘vue’

// 使用安装 Vue.use(Vuex)

// 初始化 const store = new Vuex.Store({ // 配置(state|mutations|actions) })

export default store

```javascript
+import store from '@/store'

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

总结: 和vue-router的使用方式一样。初始化 new Vuex.Store()

03-vuex的state

目的:掌握在vuex中如何定义数据,在组件中如何使用数据。
大致内容:

  • 定义数据
  • 使用数据
    • 通过this直接使用
    • computed选项中使用
    • mapState辅助函数使用

落地代码:

  • 定义数据

    // 初始化vuex对象
    const store = new vuex.Store({
    state: {
      // 管理数据
      count: 0
    }
    })
    
  • 使用数据

通过this直接使用

<div>A组件 state的数据:{{$store.state.count}}</div>
created () {
  console.log(this.$store.state.count)
}

computed选项中使用

computed: {
  count () {
    return this.$store.state.count
  }
}
<div>A组件 state的数据:{{count}}</div>

mapState辅助函数使用

import {mapState } from vuex
computed: {
    ...mapState(['count'])
}
<div>A组件 state的数据:{{count}}</div>

04-vuex的mutations

目的:知道定义mutations函数修改vuex中的数据
大致内容:

  • 定义mutations函数
  • 使用mutations函数
    • 通过this直接使用
    • mapMutations辅助函数使用

落地代码:

  • 定义mutations函数

    const store = new vuex.Store({
    state: {
      count: 0
    },
    mutations: {
      // 修改数据的函数  
      add (state) {
        state.count ++
      },
      // 带参数修改数据的函数
      add2 (state, payload) {
        // payload 是传参的意思
        state.count += payload  
      }  
    }
    })
    
  • 使用mutations函数

通过this直接使用

this.$store.commit('add')
this.$store.commit('add2', 10)

mapMutations辅助函数使用

import {mapMutations } from vuex
methods: {
  ...mapMutations(['add','add2'])    
}
<button @click="add">累加1</button>
<button @click="add2(10)">累加10</button>

05-vuex的actions

目的:知道定义actions函数获取数据
大致内容:

  • 定义actions函数
  • 使用actions函数
    • 通过this直接使用
    • mapActions辅助函数使用

落地代码:

  • 定义actions函数

    actions: {
    // 异步获取数据  
    getData (ctx, payload) {
      // ctx 是vuex的执行上下文,理解成this  
      setTimeout(()=>{
        const data = 100
        // 通过mutations修改数据
        ctx.commit('add2', data)
      },1000)
    }
    }
    
  • 使用actions函数

通过this直接使用

this.$store.dispatch('getData')

mapActions辅助函数使用

import {mapActions } from vuex
methods: {
  ...mapActions(['getData'])
}
<button @click="getData">获取数据</button>

#06-vuex的getters

目的:知道如何在vuex中定义计算属性getters
大致内容:

  • 定义getters数据(理解成vuex中的计算属性)
  • 使用getters数据
    • 通过this直接使用
    • computed中使用
    • mapGetters辅助函数使用

落地代码:

  • 定义getters数据

    state: {
    count: 10
    },
    // 基于state得到一个新数据
    getters: {
    cubeCount (state) {
      return Math.pow(state.count, 3)  
    }
    }
    

    通过this直接使用

    this.$store.getters.cubeCount
    
    <div>{{$store.getters.cubeCount}}</div>
    

    computed中使用

    computed: {
    cubeCount () {
      return this.$store.getters.cubeCount
    }  
    }
    
    <div>{{cubeCount}}</div>
    

    mapGetters辅助函数使用

    import {mapGetters } from vuex
    
    computed: {
    ...mapGetters(['cubeCount']) 
    }
    
    <div>{{cubeCount}}</div>
    

    07-vuex的modules

    目的:掌握vuex中分模块的写法,modules配置选项的使用。
    在new Vuex.Store({}) 的配置对象中数据函数越来越多不利于维护,vuex给我们提供了 modules 选项来拆分模块。
    大致内容:

  • 定义模块

  • 注册模块
  • 使用模块的:state,getters,mutations,actions 会有问题
  • 建议使用带命名空间的模块的:state,getters,mutations,actions

落地代码:

  • 定义模块, 注册模块 ```javascript import Vuex from ‘vuex’ import Vue from ‘vue’

Vue.use(Vuex)

// A模块 const moduleA = { // 开启命名空间:让你的state mutations getters actions 完全分割 namespaced: true, // 避免数据污染,模块中state建议写成函数 state () { return { count: 100 } }, getters: { cubeCount (state) { return Math.pow(state.count, 3) } }, mutations: { add (state) { state.count++ } }, actions: { getData (ctx) { setTimeout(() => { // 获取数据成功 ctx.commit(‘add’) }, 1000) } } // mutations getters actions } // B模块 const moduleB = { namespaced: true, // 避免数据污染,模块中state建议写成函数 state () { return { count: 10000 } }, mutations: { add (state) { state.count += 100 } } // mutations getters actions }

const store = new Vuex.Store({ // state mutations actions getters // 不建议直接这里定义 // 建议再 modules 配置选项定义模块 modules: { a: moduleA, b: moduleB } })

export default store


- 使用带**命名空间**的模块的:state,getters,mutations,actions
```javascript
<template>
  <div class="com-a">
    A组件 {{$store.state.a.count}} {{count}}
    <button @click="$store.commit('a/add')">累计1</button>
    <button @click="add">累计1</button>

    <button @click="$store.dispatch('a/getData')">发请求获取数据</button>
    <button @click="getData">发请求获取数据</button>
    <br>
    {{$store.getters['a/cubeCount']}} ---- {{cubeCount}}
  </div>
</template>
<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
  name: 'ComA',
  computed: {
    // 'a' 模块名称  ['count'] 模块中的state数据名称
    ...mapState('a', ['count']),
    ...mapGetters('a', ['cubeCount'])
  },
  methods: {
    // 定义了add函数调用 this.$store.commit('a/add')
    ...mapMutations('a', ['add']),
    // 定义了getData函数调用 this.$store.dispatch('a/getData')
    ...mapActions('a', ['getData'])
  }
}
</script>

需要带上模块名称 模块名称/函数名称 使用辅助函数 (模块名称,[函数名称,…])
小结一下:

  • 建议使用模块都带上命名控件,这样分割的更彻底,更好维护。

    08-vuex案例-准备代码

    目的:准备案例基础代码(克隆仓库)
    大致步骤:

  • 克隆仓库

  • 安装依赖
  • 启动案例
  • 了解结构

落地内容:

  • 克隆仓库

    git clone https://gitee.com/zhoushugang/vuex-cart-demo.git
    
  • 安装依赖

    cd vuex-cart-demo
    npm i
    
  • 启动案例

    npm run serve
    
  • 了解结构

image.png

08-vuex案例-准备接口

目的:准备后台接口(json-server)
大致步骤:

  • 安装工具
  • 初始数据
  • 启动接口
  • 测试接口

落地内容:

  • 安装工具

    npm i json-server -g
    
  • 初始化数据

新建一个目录 json-server 目录名称自定义,进入目录新建文件 db.json

{
  "cart": [
    {
      "id": 100001,
      "name": "低帮城市休闲户外鞋天然牛皮COOLMAX纤维",
      "price": 128,
      "count": 2,
      "thumb": "https://yanxuan-item.nosdn.127.net/3a56a913e687dc2279473e325ea770a9.jpg"
    },
    {
      "id": 100002,
      "name": "网易味央黑猪猪肘330g*1袋",
      "price": 39,
      "count": 10,
      "thumb": "https://yanxuan-item.nosdn.127.net/d0a56474a8443cf6abd5afc539aa2476.jpg"
    },
    {
      "id": 100003,
      "name": "KENROLL男女简洁多彩一片式室外拖",
      "price": 128,
      "count": 2,
      "thumb": "https://yanxuan-item.nosdn.127.net/eb1556fcc59e2fd98d9b0bc201dd4409.jpg"
    }
  ]
}
  • 启动接口

    json-server db.json
    
  • 测试接口 https://getman.cn/ 一个在线工具(和postman一样)

image.png

09-vuex案例-配置安装vuex

目的:安装并且配置vuex的store代码
具体步骤:

  • 安装 vuex
  • 新建vuex模块,创建store实例
  • main.js使用store实例

落地内容:

  1. 运行如下的命令安装 vuex:

    npm i vuex@3.6.2 -S
    
  2. 在 src 目录下新建 store 文件夹,并在 src/store 目录下新建 index.js 模块: ```javascript import Vue from ‘vue’ import Vuex from ‘vuex’

// 1. 把 Vuex 安装为 Vue 的插件 Vue.use(Vuex)

// 2. 创建 store 实例 const store = new Vuex.Store({ // 注册模块 modules: {}, })

// 3. 向外共享 store 实例 export default store


3. 在 main.js 入口文件中,导入并挂载 store 模块:
```javascript
import Vue from 'vue'
import App from './App.vue'
// 1. 导入 store 模块
import store from '@/store/index'

Vue.config.productionTip = false

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

09-vuex案例-新建cart模块

目的:创建配置cart的vuex数据模块
大致步骤:

  • 新建模块文件,基本配置
  • 在仓库注册模块

落地内容:

  1. 在 @/store/ 目录下新建 cart.js 购物车模块: ```javascript // 1. 定义购物车模块 const moduleCart = { // 1.1 开启命名空间 namespaced: true, // 1.2 数据 state: () => ({}) }

// 2. 向外共享购物车模块 export default moduleCart


2. 在 @/store/index.js 模块中,导入并注册购物车模块:
```javascript
import Vue from 'vue'
import Vuex from 'vuex'
// 1. 导入购物车模块
import moduleCart from './cart'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    // 2. 注册购物车模块
    cart: moduleCart,
  },
})

export default store

10-vuex案例-渲染列表

目的:使用vuex处理商品列表数据
大致步骤:

  • 安装下axios
  • vuex中state定义购物车商品列表数据
  • 定义修改商品列表数据的mutations函数
  • 定义获取商品列表数据的actions函数,获取后调用mutations函数进行修改
  • 在 app.vue 组件初始化调用actions获取数据,组件进行渲染

落地代码:

  • 安装axios

    npm i axios
    
  • 定义商品列表数据

    const moduleCart = {
    namespaced: true,
    state: () => ({
        list: []
    })
    }
    
  • 定义修改商品列表mutations函数

    mutataions: {
      updateList (state, payload) {
          state.list = payload
      }
    }
    
  • 定义获取商品列表actions函数

    actions: {
      getList (ctx) {
        axios.get('http://localhost:3000/cart').then(res=>{
            ctx.commit('updateList', res.data)
        })  
      }
    }
    
  • 在App组件获取数据,进行渲染 ```javascript import { mapState } from ‘vuex’

export default { name: “App”, created () { this.$store.dispatch(‘cart/getList’) }, computed: { …mapState(‘cart’,[‘list’]) },

```javascript
 <!-- 商品 Item 项组件 -->
    <es-goods
      v-for="item in list"
      :key="item.id"
      :id="item.id"
      :title="item.name"
      :thumb="item.thumb"
      :price="item.price"
      :count="item.count"
    ></es-goods>

11-vuex案例-计算总件数和总价

目的:渲染页面上的 件数总和,和总价格。
大致步骤:

  • 在vuex中使用getters计算件数和价格
  • 在App.vue 使用数据

落地代码:

  • 在vuex中使用getters计算件数和价格

    getters: {
      total (state) {
        return state.list.reduce((p,c)=>p+c.count,0)
      },
      amount (state) {
        return state.list.reduce((p,c)=>p+c.count*c.price,0)
      }
    },
    
  • 在App.vue 使用数据 ```javascript

```

#12-vuex案例-修改数量

目的:完成修改商品数量功能
大致步骤:
  • 定义修改数量的mutations函数
  • 定义修改数量的actions函数
  • Goods组件中修改数量的时候调用actions函数

落地代码:

  • 定义修改数量的mutations函数

    updateCount (state, payload) {
        const goods = state.list.find(item=>item.id === payload.id)
        goods.count = payload.count
      }
    
  • 定义修改数量的actions函数

    updateCount (ctx, payload) {
        axios.patch('http://localhost:3000/cart/'+payload.id,{
          count: payload.count
        }).then(res=>{
          ctx.commit('updateCount', payload)
        })
      }
    
  • Goods组件中修改数量的时候调用actions函数

    <!-- 按钮区域 -->
            <button class="btn btn-light" @click="onBtnClick(-1)">-</button>
            <span class="count">{{count}}</span>
            <button class="btn btn-light" @click="onBtnClick(1)">+</button>
    
    methods: {
      onBtnClick (step) {
        const newCount = this.count + step
        if (newCount < 1) return
        // 发送修改数量请求
        this.$store.dispatch('cart/updateCount',{id:this.id,count: newCount})
      }
    }