Bootstrap

Vuex —— Day1

vuex概述

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

vuex的应用场景:

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

如果没有vuex,当我们遇到一些通用数据的场景,那原本的数据传递就会是:子传父、父传子;中间会经历大量的组件通信

但有了vuex之后,通用数据直接存到仓库里,将来任何组件都可以直接拿来用

优势:

  • 共同维护一份数据,数据集中化管理
  • 响应式变化 —— 任何一个组件对数据进行了修改,一旦vuex里的数据变化了,所有用到这个数据的地方都会立刻响应式更新

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

目标:实现三个组件共享同一份数据 —— 任一组件都可修改数据;三个组件的数据同步

创建项目:vue create vuex-demo

其余

创建三个组件:(components文件夹下)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插件,初始化一个空仓库

步骤:

  • 安装vuex

yarn add vuex@3

  • 新建vuex模块文件

store/index.js 专门用于存放vuex

  • 创建仓库

//vuex也是vue的插件, 需要use一下, 进行插件的安装初始化

Vue.use(Vuex)

//创建仓库

new Vuex.Store()

  • main.js导入挂载

挂载到Vue实例上

验证仓库是否建成: this.$store

vue的this.$ 常见的用法_vue.js_深度学习研究员-GitCode 开源社区

Vue.js 中,this.$ 用于访问 Vue 实例的一些内置属性、方法或者插件提供的功能

一旦仓库建完后,所有组件都能访问到这个仓库


state状态

  • 提供数据

所有共享的数据都要统一放到Store中的State中存储;在state对象中可以添加我们想要共享的数据

//创建仓库
const store = new Vuex.Store({
    state:{//所有组件共享的数据;区别于data —— 组件自己的数据
        count:101
    }
})
  • 使用数据

通过store直接访问

获取仓库 —— this.$store / import 导入 store

使用数据:

模板中:{{$store.state.xxx}}

组件逻辑中:this.$store.state.xxx

JS模块中:store.state.xxx

效果展示:

完整代码: 

App.vue

<template>
  <div id="app">
    <h1>根组件 - {{ $store.state.title }} - {{  $store.state.count }}</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.count)
  },
  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

//入口文件;第一个执行的文件;main.js基于App.vue创建结构渲染index.html
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')

components/Son1.vue

<template>
  <div class="box">
    <h2>Son1 子组件</h2>
    从vuex中获取的值: <label>{{ $store.state.count }}</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>

components/Son2.vue

<template>
  <div class="box">
    <h2>Son2 子组件</h2>
    从vuex中获取的值:<label>{{ $store.state.count }}</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>

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

通过辅助函数

前情:

老在模板里{{$store.state.count}}这么引用太麻烦,考虑使用计算属性:

{{ count }}

computed: { 

        count() {

                return this.$store.state.count

        }

},

到底什么是计算属性,为什么要用计算属性?

计算属性是写在computed对象中的属性,本质上是一个方法,不过使用时仍然当属性使用

计算属性的特点是,响应式更新,当依赖的属性发生变化时,计算属性会自动重新计算并更新其值。这意味着我们不需要手动去监听属性的变化,也不需要手动去更新计算属性的值,Vue 会自动帮我们完成这些操作。

为什么不用插值表达式?

插值表达式虽然可以填入简单运算,但是当表达式变得复杂,或运算结果需要复用多次的时候,使代码的可读性变差。

简化:vuex为我们提供了mapState 辅助函数,用于帮助我们生成计算属性 

步骤:

  • 先导入mapState 辅助函数

import {mapState} from 'vuex'

  • 数组方式引入state

mapState(['count'])

mapState()可以传入对象或者数组;并且返回的是一个对象

传入数组:mapState(['name'])

数组中的每个字符串都会被映射为组件的方法

传入对象:(可以对store中的数据进行重命名)

mapState({

        rename: state => state.name

})

  • 展开运算符映射 

computed:{

      ...mapState(['count'])

}

1 computed是一个对象,computed里面用来定义计算属性

2 ...展开运算符:js 展开运算符“...“的常见用法_js ...展开-CSDN博客

在这里属于对象展开运算符,mapState()返回的是一个对象;

  1. const obj1 = { x: 1, y: 2 };

  2. const obj2 = { ...obj1, z: 3 }; // { x: 1, y: 2, z: 3 }

...mapState(['count']) 等价于 count() {  return this.$store.state.count },...                                 

computed:{ ...mapState(['count']) ,... }  等价于

computed:{ count() {  return this.$store.state.count },... }

App.vue 

Son1.vue

Son2.vue


mutations

作用:用于修改state数据

vuex遵循单向数据流,组件中不能直接修改仓库的数据

这里注意,如果写成this.$store.state.count++是不会报错的,想让它报错(严格遵循单向数据流的形式可以通过 strict:true 开启严格模式

需要让组件把修改要求提交到仓库中,让组件通过提交mutations的方式,让仓库进行修改

步骤:

  • 仓库里(store/index.js)定义mutations对象,对象里存放修改state的方法(即仓库提供方法)

  • 组件中提交调用mutations(组件中调用)

效果展示:

流程:


mutations传参

提交调用mutations函数时是可以传参的

this.$store.commit('xxx', 参数)

需求1:希望 +1、+5、+10可以共用一个方法;而不是写成addOne、addFive、addTen

需求2.改标题

提供方法

提交调用mutations,并传参 

效果展示:

注意一个问题 

mutation参数有且只能有一个,如果需要多个参数,需要包装成一个对象


mutations练习

需求:实现输入框的值和仓库状态形成双向联动

这里不可以使用v-model;因为vuex遵循单向数据流,组件中不能直接修改仓库的数据

但v-model用在输入框上,就是value属性和input事件的合写

<input :value="msg2" @input="msg2 = $event.target.value" type="text">

v-bind:value="msg2"数据变;视图跟着变

v-on:input="msg2 = $event.target.value" 对输入框进行监听;实现视图变,数据也跟着变

措施:

  • 输入框进行内容渲染:value + 监听输入获取内容@input

  • 仓库封装mutation处理函数(mutation传参)
  • 组件调用传参(commit调用)


 

;