笔记总结来源:视频学习
组件之间共享数据的方式
- 父向子传值:v-bind 属性绑定。
- 子向父传值:v-on 事件绑定。
- 兄弟组件之间共享数据:EventBus ($on 接收数据的那个组件 ; $emit 发送数据的那个组件)。
上面这三种方式只适用于小范围的。而Vuex适合大范围数据频繁共享。
一、Vuex概述
Vuex 是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享。
1.1 使用Vuex统一管理状态的好处:
- 能够在Vuex中集中管理共享的数据,易于开发和后期维护;
- 能够高效地实现组件之间的数据共享,提高开发效率;
- 存储在Vuex中的数据都是响应式的,能够实时保持数据与页面的同步。
二、Vuex的基本使用
2.1 安装依赖包
npm i vuex -S
2.2 在store文件夹中index.js导入Vuex包,
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//创建store对象
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})
2.3 在main.js将store对象挂载到vue实例中
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
三、Vuex的核心概念
3.1 State
定义:State提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储。eg: 定义一个count值。
export default new Vuex.Store({
state: {
count:0,
}
})
3.1.1 组件访问State中数据的第一种方式:
在 template 中:{{ $store.state.count }}
在 js 中: this.$store.state.count
3.1.2 组件访问State中数据的第二种方式:
<template>
<div>
<!-- 3、通过插值表达式显示计算属性的值 -->
<h3>当前最新的count值为:{{ count}}</h3>
<button>-1</button>
</div>
</template>
<script>
import { mapState } from 'vuex' // 1、从vuex中按需导入 mapState 函数
export default {
data(){return {}},
computed:{
...mapState( ['count'] ) // 2、通过展开运算符,mapState函数将当前组件需要的全局数据映射为当前组件的computed计算属性
}
}
</script>
3.2 Mutation
定义: 用于变更Store中的数据。
- 直接在组件中操作Store中的数据会不规范,代码量大的时候很难维护,找不到是哪个组件修改了Store的值,因此,建议只通过mutation 变更Store数据。
- 只有mutations中定义的函数,才有权利修改 state 中的数据。
- 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化。
- 不能在mutations的函数中执行异步代码,mutations中的函数处理的是同步任务。
3.2.1 变更state对象第一种方式——commit()
组件中this.$store.commit();commit的作用 ,就是调用 某个mutation 函数。
当组件触发mutations时传递参数:
3.2.2 变更state对象第二种方式——mapMutations()
3.3 Action
Action用于处理异步任务。
- 如果通过异步操作变更数据,必须通过Action,而不能使用Mutation。
- 在Action中不能直接更改state中的数据,要通过触发Mutations的方式间接变更数据,。
3.3.1 触发Action第一种方式——dispatch()
state: {
count:0,
},
mutations: {
add(state){
// 变更状态
state.count += 3
},
},
actions: {
addAsync(context){
setTimeout(() => {
// commit()里的方法只能是mutations中的函数
context.commit('add')
},3000)
}
},
<template>
<div>
<h3>当前最新的count值为:{{$store.state.count}}</h3>
<button @click="handler2">延迟3后执行+1</button>
</div>
</template>
<script>
export default {
data(){
return {
}
},
methods:{
handler2(){
// 触发Action,不携带参数时
this.$store.dispatch('addAsync')
}
}
}
</script>
3.3.2 触发Action第二种方式——mapActions()
3.4 Getter
定义:Getter用于对Store中的数据进行加工处理形成新的数据,不会修改Store中的原数据,类似起到了包装作用。
- Getter可以对Store中已有的数据加工处理之后形成新的数据,类似Vue的计算属性。
- Store中数据发生变化,Getter的数据也会跟着变化。
export default new Vuex.Store({
state: {
count:0,
},
getters: {
showNum:state => {
return '当前最新的数量是【'+state.count+'】'
}
}
})
3.4.1 使用getters的第一种方式
在 template 中:{{ $store.getters.showNum}}
在 js 中: this.$store.getters.showNum
3.4.1 使用getters的第二种方式
<template>
<div>
<!-- 3、通过插值表达式显示计算属性的值 -->
<h3>getters修饰过的:{{ showNum}}</h3>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed:{
...mapGetters ( ['showNum'] )
}
}
</script>
os:以上举例完整代码如下:
1、store文件夹里的index.js
export default new Vuex.Store({
state: {
count:0,
},
mutations: {
add(state){
// 变更状态
state.count ++
},
addN(state,step){
// 变更状态
state.count += step
},
subFun(state){
// 变更状态
state.count --
},
subNFun(state,step){
// 变更状态
state.count -= step
}
},
actions: {
addAsync(context){
setTimeout(() => {
// commit()里的方法只能是mutations中的函数
context.commit('add')
},1000)
},
addNAsync(context,step){
setTimeout(() => {
// commit()里的方法只能是mutations中的函数
context.commit('addN',step)
},3000)
},
subAsync(context){
setTimeout(() => {
// commit()里的方法只能是mutations中的函数
context.commit('subFun')
},1000)
},
subNAsync(context,step){
setTimeout(() => {
// commit()里的方法只能是mutations中的函数
context.commit('subNFun',step)
},3000)
}
},
getters: {
showNum:state => {
return '当前最新的数量是【'+state.count+'】'
}
},
modules: {
}
})
2、执行加法组件的代码如下:
<template>
<div>
<h3>当前最新的count值为:{{$store.state.count}}</h3>
<h3>getters修饰过的:{{$store.getters.showNum}}</h3>
<button @click="handler1">+1</button>
<button @click="handler2">延迟1后执行+1</button>
<button @click="handler3">延迟3后执行+3</button>
</div>
</template>
<script>
export default {
data(){
return {
}
},
methods:{
handler1(){
// 触发mutations
this.$store.commit('add')
},
handler2(){
// 触发Action
this.$store.dispatch('addAsync')
},
handler3(){
// 触发Action
this.$store.dispatch('addNAsync',3)
}
}
}
</script>
<style>
</style>
3、执行减法的代码如下:
<template>
<div>
<!-- 3、通过插值表达式显示计算属性的值 -->
<h3>当前最新的count值为:{{count}}</h3>
<h3>getters修饰过的:{{showNum}}</h3>
<button @click="handler2">-1</button>
<button @click="handler3">-N</button>
<button @click="handler4">异步-1</button>
<button @click="handler5">异步-N</button>
</div>
</template>
<script>
import {mapGetters,mapActions,mapMutations,mapState} from 'vuex' // 1、从vuex中按需导入 mapState 函数
export default {
data(){
return {
}
},
computed:{
...mapGetters(['showNum']),
...mapState(['count']) // 2、通过展开运算符,mapState函数将当前组件需要的全局数据映射为当前组件的computed计算属性
},
methods:{
...mapMutations(['subFun','subNFun']), //触发mutations的第二种方式
...mapActions(['subAsync','subNAsync']), //触发Actions的第二种方式
handler2(){
this.subFun()
},
handler3(){
this.subNFun(3)
},
handler4(){
this.subAsync()
},
handler5(){
this.subNAsync(3)
}
}
}
</script>
四、基于Vuex的案例——TODOS
涉及到的技术栈:vue2 、Ant Design vue 、axios 、vuex
4.1 安装依赖
npm i axios -S
npm i ant-design-vue@1.7.8 -S
注意:
如果目前使用 npm i --save ant-design-vue 命令安装的 Ant Design vue 版本为最新版本,适用于 Vue3.x 项目。如果我们 要使用 Vue2.x 来开发项目时,就必须安装指定 Ant Design vue 的版本号, 否则会报错 。如果您是 Vue2.x 环境下,必须使用 npm i ant-design-vue@1.7.8。
4.2 配置安装好的依赖
在main.js中配置
// 1、导入 ant-design-vue 组件库
import Antd from 'ant-design-vue';
// 2、导入组件库的样式表
import 'ant-design-vue/dist/antd.css';
Vue.config.productionTip = false
// 安装组件库
Vue.use(Antd);
4.3 最基本的UI结构
<template>
<div class="box">
<div>
<a-input placeholder="请输入任务" class="my_ipt" />
<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>
<a-button type="primary">添加事项</a-button>
</div>
</template>
<script>
export default {
name: "HomeView",
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>
.box {
display: flex;
justify-content: center;
margin-top: 50px;
}
.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.4 功能实现
<template>
<div class="box">
<div>
<a-input
:value="inputValue"
@change="handleInputChange"
placeholder="请输入任务"
class="my_ipt"
/>
<a-list bordered :dataSource="infolist" class="dt_list">
<a-list-item slot="renderItem" slot-scope="item">
<!-- 复选框 -->
<a-checkbox :checked="item.done" @change="cbOperateFun($event,item.id)">{{
item.info
}}</a-checkbox>
<!-- 删除链接 -->
<a slot="actions" @click="removeItemById(item.id)">删除</a>
</a-list-item>
<!-- footer区域 -->
<div class="footer" slot="footer">
<span>{{unDOneLength}}条剩余</span>
<a-button-group>
<a-button :type="viewKey == 'all' ? 'primary':'default'" @click="changeList('all')">全部</a-button>
<a-button :type="viewKey == 'undone' ? 'primary':'default'" @click="changeList('undone')">未完成</a-button>
<a-button :type="viewKey == 'done' ? 'primary':'default'" @click="changeList('done')">已完成</a-button>
</a-button-group>
<a @click="cleanFun">清除已完成</a>
</div>
</a-list>
</div>
<a-button type="primary" @click="addItemToList">添加事项</a-button>
</div>
</template>
<script>
import {mapState,mapGetters} from 'vuex'
export default {
name: "HomeView",
data() {
return {
};
},
computed:{
...mapState(['inputValue','viewKey']),
...mapGetters(['unDOneLength','infolist'])
},
created(){
this.$store.dispatch("getList")
},
methods:{
// 监听文本框内容变化
handleInputChange(e){
// console.log('ee',e.target.value);
this.$store.commit('setInputValue',e.target.value)
},
// 向列表中新增item项
addItemToList(){
if(this.inputValue.trim().length <= 0){
return this.$message.warning('文本框内容不能为空!')
}
this.$store.commit('addItem')
},
//向列表中删除item项
removeItemById(id){
this.$store.commit('delItem',id)
},
// 复选框操作
cbOperateFun(e,id){
// 通过e.target可以得到最新的选中状态
// console.log('e',e.target,id);
const param = {
id:id,
status:e.target.checked
}
this.$store.commit('changeStatus',param)
},
//清除已完成的任务
cleanFun(){
this.$store.commit('cleanDone')
},
// 修改页面上展示列表的数据
changeList(key){
this.$store.commit('changeViewKey',key)
}
}
};
</script>
<style scoped>
.box {
display: flex;
justify-content: center;
margin-top: 50px;
}
.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>
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
list:[], //所有的任务列表
inputValue:'',
nextId:5 , //下一个ID
viewKey:'all'
},
mutations: {
initList(state,list_){
state.list = list_
},
// 为store中的inputValue赋值
setInputValue(state,val){
state.inputValue = val
},
// 添加列表项
addItem(state){
const obj = {
id:state.nextId,
info:state.inputValue.trim(),
done:false
}
state.list.unshift(obj);
state.nextId++;
state.inputValue = ''
},
// 删除列表项
delItem(state,item_id){
let index = state.list.findIndex(item => item.id == item_id)
// 根据索引,删除对应的元素
if(index !== -1){
state.list.splice(index,1);
}
state.inputValue = ''
},
// 修改列表项的选中状态
changeStatus(state,param){
const index = state.list.findIndex(item => item.id == param.id)
if(index !== -1){
state.list[index].done = param.status
}
},
// 清除已完成的任务列表
cleanDone(state){
state.list = state.list.filter(item => item.done === false)
},
// 修改视图的关键字
changeViewKey(state,key){
state.viewKey = key
}
},
actions: {
getList(context){
axios.get('/list.json').then(({data}) => {
console.log(data);
context.commit('initList',data)
})
},
},
getters: {
// 统计未完成的任务条数
unDOneLength:state => {
return state.list.filter(item => item.done === false).length
},
//
infolist:state => {
if(state.viewKey == 'all'){
return state.list
}
if(state.viewKey == 'undone'){
return state.list.filter(item => !item.done)
}
if(state.viewKey == 'done'){
return state.list.filter(item => item.done)
}
return state.list
}
}
})