目录
3.3 Mutation--变更Store中的数据--commit调用
3.4 Action--处理异步任务--dispatch调用
3.5 Getter--加工处理store中的数据,不改变原store数据
一、Vuex概述
1.1 组件之间共享数据的方式
父向子传值:v-bind属性绑定
子向父传值:v-on 事件绑定
兄弟组件之间共享数据:EventBus $on接收数据的那个组件 $emit发送数据的那个组件
1.2 Vuex是什么
Vuex是实现组件全局状态(数据)管理的一种机制,可以方便地实现组件之间数据的共享。
1.3 使用Vuex统一管理状态的好处
(1)能够在vuex中集中管理共享的数据,易于开发和后期维护
(2)能够高效地实现组件的数据共享,提高开发效率
(3)存储在vuex中的数据都是响应式的,能够实时保持数据与页面的同步
一般情况下,只有组件之间共享的数据,才有必要存储到vuex中;对于组件中的私有数据,依旧存储在组件自身的data中即可。
二、VUex的基本使用
1. 安装vuex依赖包
npm install vuex -S
2.导入vuex包(store/index.js)
import Vuex from 'vuex'
Vue.use(Vuex)
3.创建store对象(store/index.js)
const store = new Vuex.Store({
//state中存放的就是全局共享的数据
state: { count: 0 }
})
4.引入并将store对象挂载到vue实例中(main.js)
import store from './store'
new Vue({
router,
store
render: h => h(App)
}).$mount('#app')
步骤一:可视化创建项目
(1)cmd窗口输入vue ui,可视化创建vue项目
(2)选择项目目录,及包管理器,点击下一步
(3)选择手动配置
(4)选择配置,打开Babel/Vuex/Lint Format/使用配置文件四项
(5)选择eslint为标准模式
(6)不保存该预设。至此,项目创建完成。
vscode打开项目,发现src目录下新增store/index.js文件,且在main.js中挂载
//store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
}
})
//main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
store,
render: h => h(App)
}).$mount('#app')
三、Vuex的核心概念
3.1 核心概念概述
Vuex的主要核心概念如下:
- state
- Mutation
- Action
- Getter
3.2 State--存储公共数据源
State提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储
export default new Vuex.Store({
state: {
count: 0
}
})
组件访问State中的数据的两种方式:
方式一:this.$store.state.全局数据名称
方式二:
- 从vuex中按需引入mapState函数
import { mapState } from 'vuex'
- 将全局数据,映射为当前组件的computed计算属性
computed: { ...mapState(['count']) }
3.2.1 计数器案例演示访问State中的数据的两种方式
(1)Store/index.js下state中声明公共数据变量:count
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 创建store数据源,提供唯一的公共数据
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
},
actions: {
},
modules: {
}
})
(2)创建两个子组件:Addition.vue和Subtraction.vue
// Addition.vue
<template>
<div>
<h3>count值为:</h3>
<button>+1</button>
</div>
</template>
<script>
export default {
data () {
return {
}
}
}
</script>
<style>
</style>
// Subtraction.vue
<template>
<div>
<h3>count值为:</h3>
<button>-1</button>
</div>
</template>
<script>
export default {
data () {
return {
}
}
}
</script>
<style>
</style>
(3)App.vue中引入两个子组件:
<template>
<div>
<my-addition></my-addition>
<p>-----------</p>
<my-subtraction></my-subtraction>
</div>
</template>
<script>
import Addition from '@/components/Addition.vue'
import Subtraction from '@/components/Subtraction.vue'
export default {
components: {
'my-addition': Addition,
'my-subtraction': Subtraction
}
}
</script>
<style>
</style>
页面如下:
若想获取全局变量count。
方式一:
方式二:
3.3 Mutation--变更Store中的数据--commit调用
如上计数器案例,若想实现点击+按钮自增1的操作,我们的初步思路是button定义click事件,在click事件中执行this.$store.state.count++操作,该方法可以实现count值自增,但不可取!!!缺陷在于若count值变更,不好找出是哪个组件操作的结果。
3.3.1 Mutations的基本用法
Mutation用于变更Store中的数据
- 只能通过Mutation变更Store数据,不可以直接操作Store中的数据
- 通过这种方式虽然操作起来复杂,但是可以集中监控所有数据的变化。
如上,用Mutation实现点击+按钮count值加1的效果:
(1)store/index.js中的mutations下定义add函数
(2)Addition.vue中调用mutations中的add函数
3.3.2 Mutations传递参数
Mutations触发时支持传递参数,如下:
3.3.3 触发Mutations函数的第二种方式
this.$store.commit()是触发mutations的第一种方式,触发的第二种方式是:
// 从vuex中按需引入mapMutations函数
import { mapMutations } from 'vuex'
通过导入的mapMutation,将需要的mutation函数,映射为当前组件的methods方法:
methods: {
...mapMutations(['substract', 'add'])
}
直接调用方法即可:
// Subtraction.vue
<template>
<div>
<h3>count值为:{{ count }}</h3>
<button @click="substract">-1</button>
</div>
</template>
<script>
// 从vuex中按需引入mapMutations函数
import { mapState, mapMutations } from 'vuex'
export default {
data () {
return {
}
},
computed: {
...mapState(['count'])
},
methods: {
...mapMutations(['substract'])
}
}
</script>
<style>
</style>
3.4 Action--处理异步任务--dispatch调用
如上计数器案例,若想实现点击+1后1秒,count值加1。初始想法是在method中定义setTimeOut函数,并在函数内部执行count++的操作。发现页面count值在1秒后增加,但vue调试工具中查看,发现count值未改变。采用Action解决该问题。
3.4.1 Action的基本使用
Action用来处理异步任务。如果通过异步操作变更数据,必须通过Action,而不能使用Mutation,但是在Action中还是要通过触发Mutation的方式间接变更数据。
如上述计数器案例,实现点击+按钮3秒后,count值加1
(1)修改store/index.js中的Action
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 创建store数据源,提供唯一的公共数据
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
add (state) {
state.count++
},
},
actions: {
addAsync (context) {
setTimeout(() => {
context.commit('add')
}, 3000)
}
},
modules: {
}
})
(2)在Addition.vue中调用action中的addAsync方法
// Addition.vue
<template>
<div>
<h3>count值为:{{ $store.state.count }}</h3>
<button @click=handler1>+1 async</button>
</div>
</template>
<script>
export default {
data () {
return {
}
},
methods: {
handler1 () {
this.$store.dispatch('addAsync')
}
}
}
</script>
<style>
</style>
注:在actions中,不能直接修改state中的数据,必须通过context.commit()触发某个mutation才可以
3.4.2 Action传递参数
触发actions异步任务时携带参数:
3.4.3 Action调用的两种方式
方式一:this.$store.dispatch()
方式二:
- 从vuex中导入mapActions函数
import { mapActions } from 'vuex'
- 通过导入的mapActions函数,将需要的actions函数,映射为当前组件的methods方法
methods: {
...mapActions(['addAsync'])
}
- 方法调用:
<button @click=addAsync()>+1 async</button>
3.5 Getter--加工处理store中的数据,不改变原store数据
- Getter用于对Store中的数据进行加工处理形成新的数据。类似vue的计算属性
- Store中的数据发生变化,Getter中的数据也会跟着变化
(1)Getter的定义
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 创建store数据源,提供唯一的公共数据
export default new Vuex.Store({
state: {
count: 0
},
getters: {
// state为入参
showNum: state => {
return '当前最新的数量为【' + state.count + '】'
}
}
})
(2)getters的使用
方式一:this.$store.getters.名称
<template>
<div>
<h3>{{$store.getters.showNum}}</h3>
<button @click=handler>+1</button>
</div>
</template>
方式二:
<template>
<div>
<h3>{{ showNum }}</h3>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['showNum'])
},
}
</script>
四、基于Vuex的案例-Todos
如上,样例包含如下功能:
- 输入任务,点击添加事项,事项添加至列表
- 点击未完成,显示的是所有未完成的事项
- 点击已完成,显示的是所有已完成的事项
- 点击某条事项后的删除,可以删除该事项
- 点击清除已完成,删除所有已完成的事项
- 左下角显示当前未完成的事项条数
4.1 初始化项目
- 创建新项目vuex-demo
- 安装vuex依赖包: npm install vuex axios ant-design-vue -S
- 实现Todos基本布局(基于已有样式模板替换原main.js和App.vue文件)
main.js代码如下:
import Vue from 'vue'
import App from './App.vue'
// 1、导入ant-design-vue 组件库
import Antd from 'ant-design-vue'
// 2、导入组件库的样式表
import 'ant-design-vue/dist/antd.css'
Vue.config.productionTip = false
// 3、安装组件库
Vue.use(Antd)
new Vue({
render: h => h(App)
}).$mount('#app')
App.vue代码如下:
<template>
<div id="app">
<a-input placeholder="请输入任务" class="my_ipt" />
<a-button type="primary">添加事项</a-button>
<a-list bordered :dataSource="list" class="dt_list">
<a-list-item slot="renderItem" slot-scope="item">
<!-- 复选框 -->
<a-checkbox>{{ item.info }}</a-checkbox>
<!-- 删除链接 -->
<a slot="actions">删除</a>
</a-list-item>
<!-- footer区域 -->
<div class="footer" slot="footer">
<span>0条剩余</span>
<a-button-group>
<a-button type="primary">全部</a-button>
<a-button>未完成</a-button>
<a-button>已完成</a-button>
</a-button-group>
<a>清除已完成</a>
</div>
</a-list>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
list: [
{
id: 0,
info: "Racing car sprays burning fuel into crowd.",
done: false,
},
{
id: 1,
info: " Japanese princess to wed commoner.",
done: false,
},
{
id: 2,
info: "Australian walks 100km after outback crash.",
done: false,
},
{
id: 3,
info: "Man charged over missing wedding girl.",
done: false,
},
{
id: 4,
info: "Los Angeles battles huge wildfires.",
done: false,
},
],
};
},
};
</script>
<style scoped>
#app {
padding: 10px;
}
.my_ipt {
width: 500px;
margin-right: 10px;
}
.dt_list {
width: 500px;
margin-top: 10px;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
4.2 全部事项展示
(1)在public目录下新建list.json文件,并将App.vue中的list数组剪切后复制到该文件中,如下为修改后的list.json。App.vue中data中的list设置为空数组:list: []
[
{
"id": 0,
"info": "Racing car sprays burning fuel into crowd.",
"done": false
},
{
"id": 1,
"info": " Japanese princess to wed commoner.",
"done": false
},
{
"id": 2,
"info": "Australian walks 100km after outback crash.",
"done": false
},
{
"id": 3,
"info": "Man charged over missing wedding girl.",
"done": false
},
{
"id": 4,
"info": "Los Angeles battles huge wildfires.",
"done": false
}
]
(2)store/index.js:引入并安装vuex插件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
}
})
(3)main.js中挂载store对象
import Vue from 'vue'
import App from './App.vue'
import store from './store'
// 1、导入ant-design-vue 组件库
import Antd from 'ant-design-vue'
// 2、导入组件库的样式表
import 'ant-design-vue/dist/antd.css'
Vue.config.productionTip = false
// 3、安装组件库
Vue.use(Antd)
new Vue({
store,
render: h => h(App)
}).$mount('#app')
(4)修改store/index.js。
- state中声明list数组
- actions中发起异步axios请求,获取list数据。
- 将要获取的数据赋值给state中的list,因为actions中不能操作list,所以赋值操作需要由mutations完成。
--------store/index.js----------
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
list: []
},
mutations: {
initList (state, list) {
state.list = list
}
},
actions: {
getList (context) {
axios.get('/list.json').then(({ data }) => {
context.commit('initList', data)
})
}
},
modules: {
}
})
(5)App.vue中获取并展示list数组的值。
- 删除原data中的list数组
- 定义create函数,并调用actions中的getList函数
- 将store/index.js中的state中的list展示到页面中
<template>
<div id="app">
<a-input placeholder="请输入任务" class="my_ipt" />
<a-button type="primary">添加事项</a-button>
<a-list bordered :dataSource="list" class="dt_list">
<a-list-item slot="renderItem" slot-scope="item">
<!-- 复选框 -->
<a-checkbox>{{ item.info }}</a-checkbox>
<!-- 删除链接 -->
<a slot="actions">删除</a>
</a-list-item>
<!-- footer区域 -->
<div class="footer" slot="footer">
<span>0条剩余</span>
<a-button-group>
<a-button type="primary">全部</a-button>
<a-button>未完成</a-button>
<a-button>已完成</a-button>
</a-button-group>
<a>清除已完成</a>
</div>
</a-list>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'app',
computed: {
...mapState(['list'])
},
created () {
this.$store.dispatch('getList')
},
data () {
return {
}
}
}
</script>