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()返回的是一个对象;
const obj1 = { x: 1, y: 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调用)