版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
教学目标掌握Vuex的工作原理。掌握Vuex五个核心概念。掌握Vuex中mutation和action的定义与使用方法。熟悉多模块的应用场景。学会在Vue3.x中引入相关VuexAPI、使用store对象。1第9章-状态管理模式Vuex(4学时)29.1Vuex概述
1.状态管理模式Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的State状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex也集成到Vue的官方调试工具devtoolsextension,提供了诸如零配置的time-travel调试、状态快照导入导出等高级调试功能。
状态自管理应用通常包含state、view和actions三个部分,如图所示。它们作用分别如下:state(状态):驱动应用的数据源。数据源就是组件里面的data。view(视图):以声明方式将state映射到视图。{{count}}即为声明方式,数据就可以显示出来。actions(行为):响应在view上的用户输入导致的状态变化。actions其实就是多个函数。9.1.1Vuex定义【例9-1】简易投票的单向数据流应用实战。39.1.1Vuex定义
第16~22行定义template属性,使用反单引号定义div标记,该标记包含文本插值和一个按钮,用于触发递增行为来改变count的值,并显示在视图上。
第24行定义methods属性,其中定义vote()方法实现tickets增1。view初始读取data中的tickets,显示为0。通过事件触发调用actions里面的vote()方法,然后actions去更新state的状态数据。state更新之后,view的界面也会随之改变。42.Vuex数据流向及适用场景
在实际工程应用中经常会遇到多组件间共享状态,此时单向数据流的简洁性很容易被破坏。主要来源于两种应用场合:使用数据:多个视图依赖于同一状态。
在多层嵌套的组件中采用参数传递的方法将会非常繁琐,尤其是兄弟组件间的状态传递更显得无能为力。更新数据:来自不同视图的行为需要变更同一状态。9.1.1Vuex定义
经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致代码无法维护。
如果不打算开发大型单页应用,就没有必要使用Vuex。一个简单的store模式就足够满足需求了。但是,如果需要构建一个中大型单页应用,此时很可能会考虑如何更好地在组件外部管理状态,Vuex自然而然将会成为最好的选择。5
若有一个状态需要被多个实例共享,可以简单地通过维护一份数据来实现共享,这就是store模式。
【例9-2】store模式应用实战--共享状态。设计要求:设置两个组件分别共享store中state状态,同时管理自身的私有数据,通过全局声明一个store对象变量,封装一个state属性(定义为reactive()响应式对象)和setMessageAction(newValue)、clearMessageAction()等两个方法。再定义两个组件,分别为App1、App2,并给每个实例分别定义data、methods等属性。data属性中分别定义私有数据属性privateState和共享数据属性sharedState,并将sharedState属性的值设置为store.state。9.1.2简单状态管理-store模式69.1.2简单状态管理-store模式
代码中第27~40行定义1个对象变量store,并给其定义一个state属性(在其中定义message属性,作为状态数据)和setMessageAction(newValue)(功能为改变状态)、clearMessageAction()(功能为清空状态)等两个方法。
两个Vue实例中data属性中均定义了privateState(私有数据)、sharedState(共享数据)。7
store模式应用-单击“改变共享信息”按钮页面store模式应用-单击“清空享信息”按钮页面9.1.2简单状态管理-store模式注意:所有store中state的改变,都放置在store自身的action中去管理。这种集中式状态管理能够被更容易地理解哪种类型的mutation将会发生,以及它们是如何被触发。当出现错误时,现在也会有一个log记录bug之前发生了什么。此外,每个实例/组件仍然可以拥有和管理自己的私有状态(数据)。89.2Vuex基本使用
在项目中需要使用Vuex时,必须将vue.js和vuex.js引用到项目中。可以通过CDN或者script脚本引用(
/vuex@4.1.0/dist/vuex.global.js),然后在项目中安装vuex模块,并在项目主文件中导入vue和vuex,再显式地使用app.use(store)即可。具体的操作步骤如下:1.直接下载/CDN引用。HTML中使用script标签引入。<scriptsrc="vue.global.js"></script><scriptsrc="vuex.global.js"></script>CDN引用。<scriptsrc="/vuex@4.1.0/dist/vuex.global.js"></script>
2.项目目录下载安装模块。npminstallvuex--save-dev|-D
3.导入并显式地使用Vuex。//index.jsimportVuefrom'vue'importVuexfrom'vuex'Vue.use(Vuex)//Vue2.x中显式地通过
Vue.use()
来使用Vuex//在Vue3.x中使用方式import{createApp}from'vue'importAppfrom'./App.vue'importstorefrom'./store'Vue.createApp(App).use(store).mount('#app')99.2Vuex基本使用4.Vue3.x中的Vuex
在Vue3.x中使用Vuex的版本是v4.0.2以上,在src/store/index.js文件中需要导入createStoreAPI,然后定义state、mutations、actions、modules等核心属性。部分参考代码如下:import
{
createStore
}
from
'vuex'
export
default
createStore({
state:
{
user:
{name:
'李小明',sex:
'男'},
title:
'Vuex状态管理'
},
mutations:
{
setUserName(state,
value)
{
=
value
}
},
actions:
{},
modules:
{}
})
在Vue3.x中,组件内使用Vuex,setup函数中使用有所不同。部分参考代码如下:import
{
toRefs,reactive}
from
"vue";
import
{
useStore}
from
"vuex";
export
default
{
setup()
{
const
state
=
reactive({
name:
'',
title:''
})
const
store
=
useStore()
=
return
{
...toRefs(state)
...toRefs(store.state.user)
}
}
};
在Vue3.x中使用Vuex时需要显式导入useStore,然后在setup函数内通过useStore()来创建store(如第9行所示),并在其中定义相关方法去调用相关的mutations和actions,最后将store.state中的相关属性通过toRefs()转换为普通的响应式数据(如第13行所示),供模板使用。109.3Vuex核心概念
Vuex应用的核心就是store(仓库)。store就是一个容器,它包含着用户应用中大部分的状态(数据),如右图所示。但Vuex和单纯的全局对象是不同。
主要有两点区别:
Vuex的状态存储是响应式的。
当Vue实例/组件从store中读取状态的时候,若store中的状态发生变化,那么相应的实例/组件也会相应地得到高效更新。
用户不能直接改变store中的状态。
改变store中的状态的唯一途径就是显式地提交mutation。
这样使得可以方便地跟踪每一个状态的变化,从而可以通过一些工具帮助用户更好地了解自己的应用。119.3.1一个完整的store结构
一个完整的store包含state、getters、mutations、actions、modules等五大组成部分。精简的代码如下所示。//Vue3.x中定义方式import
{
createStore
}
from
'vuex'
export
default
createStore({
state:
{
//
存放状态
},
getters:
{
//
state的计算属性
},
mutations:
{
//
更改state中状态的逻辑,同步操作
}
actions:
{
//
提交mutation,异步操作
}
//
如果将store分成一个个的模块的话,则需要用到modules。
//
然后在每一个module中写state,
getters,
mutations,
actions等。
modules:
{
a:
moduleA,
b:
moduleB,
//
...
})
需要使用exportdefaultcreateStore({})将store导出,在main.js文件可以导入,并挂载到Vue根实例中,其它子组件即可以使用store中的state状态。129.3.2最简单的store
安装Vuex之后,就可以来创建一个store。创建过程比较简单,仅需要提供一个初始state对象和一些mutation。部分代码如下:import
{createStore}
from
'vuex'
exportdefaultcreateStore({
state:
{
count:
0
},
mutations:
{
increment
(state)
{
state.count++
}
}
})
接下来就可以通过store.state来获取状态对象,以及通过mit方法触发状态变更,并通过控制台输出状态数据。代码如下:mit('increment')//increment触发mutationconsole.log(store.state.count)//count值为1注意:通过提交mutation的方式,而非直接改变store.state.count,是因为想要更明确地追踪到状态的变化。这样可使用户的意图更加明显,在阅读代码的时候能更容易地解读应用内部的状态改变。由于store中的状态是响应式的,在组件中调用store中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的methods中提交mutation。139.3.2最简单的storeVuex是解决Vue组件和组件间相互通信而存在的。Vuex理解起来稍微复杂,可以通过以下五个核心概念来了解并学会使用。它们分别是:state:定义状态(变量),辅助函数mapState。getter:获取状态(变量的值),同时可以对状态进行处理,辅助函数mapGetters。mutation:修改状态(修改变量的值),辅助函数mapMutations。action:触发mutation函数,从而修改状态,支持异步,辅助函数mapActions。module:在状态很多时,把状态分开来管理。149.3.3Vuex中stateVuex使用单一状态树,即用一个对象包含了全部的应用层级状态,它作为一个“唯一数据源”而存在。
单一状态树让用户能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
存储在Vuex中的数据和Vue实例中的data遵循相同的规则,都是纯粹的对象(有零个或多个的key/value对)。
1.在Vue组件中通过computed计算属性获得Vuex状态
在Vue组件中如何展示状态呢?由于Vuex的状态存储是响应式的,从store实例中读取状态最简单的方法就是在计算属性computed中返回某个状态。
部分代码如下://
创建一个
Counter
组件
const
Counter
=
{
template:
`<div>{{
count
}}</div>`,
computed:
{
count
()
{
return
store.state.count
}
}
}
每当store.state.count变化的时候,都会重新求取计算属性,并且触发更新相关联的DOM。然而,这种模式导致组件依赖全局状态单例。在模块化的构建系统中,在每个需要使用state的组件中需要频繁地导入,并且在测试组件时需要模拟状态。15Vuex通过main.js文件中的createApp(App).use(store)为每一个子组件提供Store。部分代码如下:import
{
createApp
}
from
'vue'
import
App
from
'./App.vue'
import
store
from
'./store'
createApp(App).use(store).mount('#app')
单个文件组件中通过computed()获取状态数据。代码如下:<template><div>{{
count
}}</div></template>
<scriptsetup>import{computed}from'vue'import{useStore}from'vuex'conststore=useStore()
constcount=computed(()=>store.state.count
)</script>
【引入问题】
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。怎么办呢?【解决办法】Vuex通过使用mapState()辅助函数帮助生成计算属性,减少用户按键的次数。2.在Vue组件中通过mapState()辅助函数获得Vuex状态。mapState()函数返回的是一个对象,用来获取多个状态。mapState()函数可以接受对象{}或数组[]作为参数。{}为键值对形式,key:value,key为计算属性,value为函数,参数为store.state,返回需要的state;当映射的计算属性的名称与state的子节点名称相同时,可以给mapState传一个字符串数组。9.3.3Vuex中state169.3.3Vuex中state//
1.在单独构建的版本中辅助函数为
Vuex.mapState
computed:
mapState({
//
箭头函数可使代码更简练
count:
state
=>
state.count,
//
传字符串参数
'count'
等同于
`state
=>
state.count`
countAlias:
'count',
//
为了能够使用
`this`
获取局部状态,必须使用常规函数
countPlusLocalState
(state)
{
return
state.count
+
this.localCount
}
})
//
2.当映射的计算属性的名称与
state
的子节点名称相同时
computed:
mapState([
//
映射
this.count
为
store.state.count
'count'
//可以有多个state对象中属性(key),用逗号分隔])
【引入问题】如何将Vuex状态与局部计算属性混合使用呢?
【解决办法】展开运算符(...)将多个对象合并为一个,再传给computed属性。
3.对象展开运算符部分代码如下:computed:
{
localComputed
()
{
/*
...
*/
},
//
使用对象展开运算符将此对象混入到外部对象中
...mapState({
//
...
})
}
4.组件自有局部状态
使用Vuex并不意味着需要将所有的状态均放入Vuex。
如果有些状态严格属于单个组件,最好还是作为组件的局部状态。可以根据具体应用开发需要进行权衡和确定。179.3.3Vuex中state
【例9-3】Vuex核心概念实战--state的应用(项目vuex-state-1)
1.在当前目录下,通过vuecreate命令创建项目,选择babel+vuex+eslint+Vue3.x,完成后进入项目文件夹,启动本地开发服务。命令如下,执行结果如下图所示。vuecreatevuex-state-1cdvuex-state-1npmrunserve2.在浏览器中打开http://localhost:8080,查看页面。
3.看到下图这个界面说明项目启动成功,然后在项目的src目录下新建一个目录store,在该目录下新建一个index.js文件,使用createStore({})创建store,并使用exportdefault导出store。具体代码如下:import
{
createStore
}
from
'vuex'
export
default
createStore({
state:
{
bookname:
'Vue.js前端框架技术与实战',
price:
69.80,
press:
'清华大学出版社'
},
getters:
{},
mutations:
{},
actions:
{},
modules:
{}
})
18【例9-3】Vuex核心概念实战--state4.修改src/App.vue文件。文件包含template、script、style等3个部分,完成HelloWorld组件导入和使用。代码如下:<template>
<div>
<img
alt="logo"
src="/logo.png"
/>
<HelloWorld
msg="欢迎使用清华社图书"
/>
</div>
</template>
<script
setup>
import
HelloWorld
from
"./components/HelloWorld.vue";
</script>
<style>
#app
{text-align:
center;
color:
#2c3e50;
margin-top:
60px;}
</style>
5.修改components/HelloWorld.vue文件。代码如下:<template>
<div
class="hello">
<h3>{{
msg
}}</h3>
<p>图书名称:{{
bookname1
}}</p>
<p>定价:{{
price1
}}</p>
<p>出版社:{{
press1
}}</p>
<h3>使用mapState()</h3>
<p>图书名称:{{
storeState.bookname}}</p>
<p>定价:{{
storeState.price
}}</p>
<p>出版社:{{
storeState.press
}}</p>
<!--
1-1.使用toRefs()获取状态
-->
<!--
<p>图书名称:{{
bookname}}</p>
<p>定价:{{
price
}}</p>
<p>出版社:{{
press
}}</p>
-->
</div>
</template>
<script
setup>
//
import
{
toRefs
}
from
'vue'
1-2.导入toRefs()
import
{
computed,
defineProps
}
from
'vue'
import
{
useStore,
mapState
}
from
'vuex'
defineProps({
msg:
String
})
const
store
=
useStore()
19【例9-3】Vuex核心概念实战--state6.在src子文件夹下创建main.js。代码如下:import
{
createApp
}
from
'vue'
import
App
from
'./App.vue'
import
store
from
'./store'
createApp(App).use(store).mount('#app')7.切换到浏览器界面,并刷新页面,效果如下图所示。
//
单个获取store中的状态数据,使用计算函数
const
bookname1
=
computed(()
=>
store.state.bookname)
const
price1
=
computed(()
=>
store.state.price)
const
press1
=
computed(()
=>
store.state.press)
//
使用辅助函数一次性获取所有状态数据,使用mapState()
const
storeStateFns
=
mapState(['bookname',
'price',
'press'])
//返回函数对象console.log(storeStateFns)
const
storeState
=
{}
//
对storeStateFns进行Object.keys(storeStateFns)=[bookname,price,press]
Object.keys(storeStateFns).forEach(fnkey
=>
{
const
fn
=
storeStateFns[fnkey].bind({
$store:
store
})
storeState[fnkey]
=
computed(fn)
})
//
const
{
bookname,
price,
press
}
=
toRefs(store.state)
1-3.使用toRefs()
</script>
<style
scoped>
h3
{margin:
5px
auto;}
</style>
209.3.4Vuex中getter1.Vuex中getter的需求背景【引入问题】工程项目中有时候需要从store.state中派生出一些状态,例如对列表进行过滤并计数,可以使用计算属性来实现。实现的代码段如下:computed:{doneTodosCount(){//统计待办项目中已经完成的项目数returnthis.$store.state.todos.filter(todo
=>todo.done).length}}
如果有多个组件需要用到此属性,要么复制这个函数,要么抽取到一个共享函数,然后在多处导入它,但无论哪种方式使用起来均不是很理想。【解决办法】Vuex允许在store中定义getter(可以认为是store的计算属性)。getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。getter可以接受第为一个参数为state。部分代码如下:import{createStore}from'vuex'const
store
=
createStore({
state:
{
todos:
[
{
id:
1,
text:
'...',
done:
true
},
{
id:
2,
text:
'...',
done:
false
}
]
},
getters:
{
doneTodos:
state
=>
{
return
state.todos.filter(todo
=>
todo.done)
}
}
})
212.getter使用方法。常用的方法有通过属性、方法和mapGetters()辅助函数来访问。
通过属性访问。getter会暴露为store.getters对象,可通过属性的形式访问这些值:store.getters.doneTodos//返回已完成项目[{id:1,text:'...',done:true}]getter可以接受将其他getter作为第二个参数。代码如下:getters:
{
//
...
doneTodosCount:
(state,
getters)
=>
{
return
getters.doneTodos.length
}
}
//使用doneTodosCountstore.getters.doneTodosCount
//返回19.3.4Vuex中getter
在其它组件中可以很容易地使用它。代码如下:constdoneTodosCount=computed(()=>store.getters.doneTodosCount)
注意:通过属性访问时,getter作为Vue的响应式系统的一部分缓存在其中。通过方法访问。getters:
{
//
...
getTodoById:
(state)
=>
(id)
=>
{
return
state.todos.find(todo
=>
todo.id
===
id)
}
}
//使用方法
store.getters.getTodoById(2)
//
返回
{
id:
2,
text:
'...',
done:
false
}
229.3.4Vuex中getter在OptionsAPI编程中,通过mapGetters()辅助函数来访问。代码如下:import
{
mapGetters
}
from
'vuex'
export
default
{
//
...
computed:
{
//
使用对象展开运算符将
getter
混入
computed
对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
//
...
])
}
}
如果想将一个getter属性另取一个名字,使用对象形式来定义。代码如下:mapGetters({
//
把
`this.doneCount`
映射为
`this.$store.getters.doneTodosCount`
doneCount:
'doneTodosCount'
})
【例9-4】Vuex核心概念实战之二--getter的使用(项目vuex-getter-1)。步骤如下:1.在当前目录下,新建vuex-getter-1项目,依次执行下列指令,完成项目创建与配置工作。vuecreatevuex-getter-1cdvuex-getter-1npmrunserve
2.进入vuex-getter-1文件夹,删除components/HelloWorld.vue组件,然后依次修改App.vue、src/store/index.js等文件。各文件具体内容如下:23【例9-4】Vuex核心概念实战之二编辑main.js文件。import
{
createApp
}
from
'vue'
import
App
from
'./App.vue'
import
store
from
'./store'
createApp(App).use(store).mount('#app')
编辑index.js文件。import
{
createStore
}
from
'vuex'
export
default
createStore({
state:
{
bookname:
'Vue.js前端框架技术与实战',
price:
69.8,
press:
'清华大学出版社',
total:
0.0,
count:
2
},
getters:
{
getPrice
(state)
{
return
state.price
},
getCount
(state)
{
return
state.count
},
getTotal
(state,
getters)
{
return
getters.getPrice
*
getters.getCount
}
},
mutations:
{},
actions:
{},
modules:
{}
})
编辑App.vue文件。<template>
<div>
<img
alt="Vue
logo"
src="/vuebook.jpg"
/>
<h3>{{
store.state.bookname
}}</h3>
<p>图书定价:{{
getPrice
}}</p>
<p>图书数量:{{
getCount
}}</p>
<p>购买总价:{{
getTotal
}}</p>
</div>
</template>
<scriptsetup>import{toRefs}from'vue'import{useStore}from'vuex'conststore=useStore()const{getPrice,getCount,getTotal}=toRefs(store.getters)</script>
<style>#app{
text-align:center;
color:#2c3e50;
margin-top:60px;}</style>3.切换到浏览器界面,刷新页面,效果如图9-11所示249.3.5Vuex中mutation
更改Vuex的store中的状态的唯一方法是提交mutation(突变、变异、改变)。
Vuex中的mutation非常类似于事件:每个mutation都有一个字符串的事件类型(type)和一个回调函数(handler)-就是实际进行状态更改的地方,并且它会接受state作为第一个参数。import{createStore}from'vuex'exportdefaultcreateStore({
state:
{
count:
1
},
mutations:
{
increment
(state)
{
//increment为事件类型type,state为参数
state.count++
//
变更状态
}
}
})
用户不能直接调用一个mutationhandler。这个选项更像是事件注册:“当触发一个类型为increment的mutation时,调用此函数”。要唤醒一个mutationhandler,需要以相应的type调用mit()方法。代码如下:mit('increment')
提交载荷(payload)
可以向mit()传入额外的参数,即mutation的载荷。部分代码如下://
...
mutations:
{
increment
(state,
n)
{
state.count
+=
n
}
}
259.3.5Vuex中mutation
唤醒这样mutationhandler同样需要以相应的type调用mit()方法。mit(type,[payload])//[]表示可选参数mit('increment',
10)
载荷应该是一个对象,这样可以包含多个字段并且记录的mutation会更易读。部分代码如下://
...
mit(type,[payload])//[]可选mit('increment',
10)
在多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的mutation会更易读。部分代码如下:mutations:
{
increment
(state,
payload)
{
state.count
+=
payload.amount
//累加,payload是对象,amount是其一个属性
}
}
相应的唤醒方法如下://把payload和type分开提交mit('increment',
{
amount:
10
})
对象风格的提交方式
提交mutation的另一种方式是直接使用包含
type
属性的对象{}。//整个对象都作为载荷传给mutation函数mit({
type:
'increment',
amount:
10
})
修改state对象的方法
有时会动态修改state对象,如增加对象的属性,如何才能正确地实施呢?
可以修改state对象中的属性方法如下:Vue.set(obj,'newProp',value)//Vue.set()方法
state.obj={...state.obj,newProp:value}//以新对象替换老对象269.3.5Vuex中mutation
例如,state对象中的student对象原来有name、sex两个属性,现在需要增加age属性。正确的添加方法如下:import{createStore}from'vuex'exportdefaultcreateStore({
state:
{
student:
{
name:
'张兰英',
sex:
'女'
}
}
})
//以下给state添加一个age属性
mutations:
{
addAge
(state)
{
Vue.set(state.student,
'age',
20)
//这是第一种方法,新增属性需要使用引号
//
state.student
=
{
...state.student,
age:
18
}
//这是第二种方法
}
}
使用常量替代Mutation事件类型(选讲)
在工程项目中,可以使用常量替代mutation事件类型。通常将这些常量放在单独的文件中,可以让项目中所包含的mutation一目了然,方便项目合作者查看使用。具体代码如下:新建mutation-types.js文件//
mutation-types.js
export
const
SOME_MUTATION
=
'SOME_MUTATION'
27新建store.js文件//
store.js
import
{createStore}
from
'vuex'
import
{
SOME_MUTATION
}
from
'./mutation-types'
exportdefaultcreateStore({
state:
{
...
},
mutations:
{
//
使用
ES2015
风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION]
(state)
{
//
mutation
state
}
}
})
注意:函数名必须是带[]的类型常量(如[SOME_MUTATION])。建议多人合作的大项目最好用常量的形式来处理mutation。小项目不需要这样做。9.3.5Vuex中mutationmutation必须是同步函数
一条重要的原则就是要记住mutation必须是同步函数。为什么?要通过提交mutation的方式来改变状态数据,才能更明确地追踪到状态的变化。在组件中提交mutation
在OptionAPI编程模式下,组件中可以使用this.$mit(‘xxx')提交mutation,或者使用mapMutations辅助函数将组件中的methods映射为mit()调用(需要在根节点注入store)。具体实现的部分代码如下:28组件中script标记中导入import
{
mapMutations
}
from
'vuex'
export
default
{
//
...
methods:
{
...mapMutations([
'increment',
//
将
`this.increment()`
映射为
`this.$mit('increment')`
//
`mapMutations`
也支持载荷:
'incrementBy'
//
将
`this.incrementBy(amount)`
映射为
`this.$mit('incrementBy',
amount)`
]),
...mapMutations({
add:
'increment'
//
将
`this.add()`
映射为
`this.$mit('increment')`
})
}
}
9.3.5Vuex中mutation299.3.5Vuex中mutation
在CompositionAPI编程模式下,组件的<script
setup>内可以在functionfunName()内使用mit("mutationName",[payload])来提交mutation。具体实现的部分代码如下:<!--
采用Vue3.2
新增<script
setup>
-->
<script
setup>
import
{
useStore
}
from
'vuex';
const
store
=
useStore();
//状态foods、total、orderSumfunction
add(n)
{
mit("addOrderAmount",
n);
mit("addTotal");
mit("totalSum");
}
function
reduce(n)
{
mit("reduceOrderAmount",
n);
mit("reduceTotal");
mit("totalSum");
}
</script>
309.3.6Vuex中action
action类似于mutation,又不同在于mutation。具体有以下两点:action提交的是mutation,而不是直接变更状态。action可以包含任意异步操作。
actions对象里的方法需要使用store.dispatch调用。action函数接受一个与store实例具有相同方法和属性的context对象,因此可以调用mit提交一个mutation,或者通过context.state和context.getters来获取state和getters。
以下来注册一个简单的action。import{createStore}form'vuex'exportdefaultcreateStore({
state:
{
count:
0
},
mutations:
{
increment
(state)
{
state.count++
}
},
actions:
{
increment
(context)
{
mit('increment')
}
}
})
在项目实践中,也可以使用ES2015的参数解构来简化代码(特别是需要多次调用commit时候)。简化格式如下:
31actions:
{
increment
({commit
})
{
//{commit}相当于{commit:mit}
commit('increment')
//由原来mit简化为commit
}
}
分发ActionAction通过store.dispatch方法触发Mutations。代码如下:store.dispatch('increment')
由于mutation必须同步执行,但action就没有这个约束。可以在action内部执行异步操作。部分代码如下:actions:
{
incrementAsync
({
commit
})
{
setTimeout(()
=>
{
//=>表示箭头函数
commit('increment')
},
1000)
//1000毫秒后执行
}
}
9.3.6Vuex中action329.3.6Vuex中actionActions支持同样的载荷方式和对象方式进行分发://
以载荷形式分发
store.dispatch('incrementAsync',
{
amount:
10
})
//
以对象形式分发
store.dispatch({
type:
'incrementAsync',
amount:
10
})
在组件中分发Action
组件中使用this.$store.dispatch(‘xxx’)分发action,或者使用mapActions辅助函数将组件的methods映射为store.dispatch()调用(需要先在根节点注入store)。
在OptionsAPI编程模式下,Vue2.6.x中,组件中使用this.$store.dispatch(‘xxx')分发action,或者使用mapActions辅助函数将组件的methods映射为store.dispatch()调用(需要先在根节点注入store)。import
{
mapActions
}
from
'vuex'
export
default
{
//
...
methods:
{
...mapActions([
'increment',
//
将
`this.increment()`
映射为
`this.$store.dispatch('increment')`
//
`mapActions`
也支持载荷:
'incrementBy'
//
将
`this.incrementBy(amount)`
映射为
`this.$store.dispatch('incrementBy',
amount)`
]),
...mapActions({
add:
'increment'
//
将
`this.add()`
映射为
`this.$store.dispatch('increment')`
})
}
}
339.3.6Vuex中action(带载荷)
在CompositionAPI编程模式下,组件的<scriptsetup>标记中可以在functionfunName()内使用dispatch("mutationName",[payload])来提交mutation。具体实现的部分代码如下:<script
setup>
import
{useStore}
from
'vuex'
const
{state,commit,dispatch}
=
useStore()
const
add
=
()
=>
{
commit('increment')
}
const
asyncIncrement
=
()
=>
{
dispatch('incrementAsync',3)
}
</script>
Vuex核心概念实战之三--muatations和actions的使用(项目vuex-mutation-action-1)。使用store的mutations和actions完成周薪调节功能。代码如下,页面效果如图9-12~图9-14所示。具体实现的步骤如下:1.在当前目录下,新建vuex-3-mutation-action项目,依次执行下列指令,完成项目创建与配置工作。vuecreatevuex-mutation-action-1cdvuex-mutation-action-1npmrunserve2.修改App.vue、src/store/index.js等文件,在components子文件夹下新建addWeeklyPay.vue、reduceWeek.vue两个组件,删除HelloWorld.vue组件。。349.3.6Vuex中action修改src/store/index.js文件。内容如下://
vuex-mutation-action-1
index.js
import
{
createStore
}
from
'vuex'
export
default
createStore({
state:
{
name:
'张成长',
weeklyPay:
5000,
week:
6
},
mutations:
{
add
(state)
{
state.weeklyPay
=
state.weeklyPay
+
100
},
addNum
(state,
num)
{
//
带第二个参数num(幅度)
state.weeklyPay
=
state.weeklyPay
+
num
},
reduce
(state)
{
state.weeklyPay
=
state.weeklyPay
-
100
},
reduceNum
(state,
num)
{
//
带第二个参数num(幅度)
state.weeklyPay
=
state.weeklyPay
-
num
}
},
修改App.vue文件。内容如下:<!--
vuex-mutation-action-1
App.vue
-->
<template>
<div
id="app">
<h2>使用mutation调增周薪</h2>
<add></add>
<hr
/>
<h2>使用action调减周薪</h2>
<reduce></reduce>
</div>
</template>
<script
setup>
import
add
from
'./components/addWeeklyPay.vue'
import
reduce
from
'./components/reduceWeeklyPay.vue'
</script>
<style
scoped>
#app
{
margin-top:
10px;
padding:
10px;
border:
1px
dashed
#112233;}
</style>
359.3.6Vuex中action编辑main.js文件import
{
createApp
}
from
'vue'
import
App
from
'./App.vue'
import
store
from
'./store'
createApp(App).use(store).mount('#app')
创建addWeeklyPay.vue组件<!--
addWeeklyPay.vue
-->
<template>
<div>
<p
v-once>
姓名:{{
$
}},第{{
$store.state.week
}}周,周薪:{{
$store.state.weeklyPay
}}元
</p>
<p>姓名:{{
name
}},第{{
week
}}周,周薪:{{
weeklyPay
}}元</p>
<button
@click="addWeeklyPay">增薪(100元)</button>
<button
@click="addWeeklyPayNum">增薪(Num元)</button>
</div>
</template>
actions:
{
addWeeklyPay
(context)
{
mit('add')
},
reduce
(context)
{
mit('reduce')
//
同步减少
},
reduceNum
(context,
num)
{
mit('reduceNum',
num)
//
同步减少
},
reduceAsync
(context)
{
setTimeout(()
=>
{
//
异步
mit('reduce')
},
1000)
},
reduceNumAsync
(context,
step)
{
//
异步带参数
setTimeout(()
=>
{
mit('reduceNum',
step)
},
1000)
}
}
})
36<script
setup>
import
{
useStore
}
from
'vuex'
import
{
toRefs
}
from
'vue'
const
{
state,
commit
}
=
useStore()
//解构赋值const
addWeeklyPay
=
()
=>
{
commit('add')
}
const
addWeeklyPayNum
=
()
=>
{
commit('addNum',
150)
}
const
{
week,
name,
weeklyPay
}
=
toRefs(state)
//解构赋值</script>
<style
scoped="scoped">
button
{
border-radius:
4px
4px;
border:
1px
solid
#1f1202;
height:
28px;
background-color:
#F1F2F3;
}
</style>
创建reduceWeek.vue组件文件。内容如下:<!--
reduceWeek.vue
-->
<template>
<div>
<p
v-once>
姓名:{{
$
}},第{{
$store.state.week
}}周,
周薪:{{
$store.state.weeklyPay}}元
</p>
<p>姓名:{{
name
}},第{{
week
}}周,周薪:{{
weeklyPay
}}元</p>
<button
@click="reduceWeeklyPay">降薪(100元)</button>
<button
@click="reduceWeeklyPayNum">降薪(Num元)</button>
<button
@click="reduceAsync">异步Actions降薪(100元)</button>
<button
@click="reduceNumAsync(300)">异步Actions降薪(Num元)</button>
</div>
</template>
9.3.6Vuex中action37<style
scoped>
button
{
border:
1px
solid
#774477;
border-radius:
4px
4px;
height:
28px;
background-color:
#ebebeb;
}
</style>
3.刷新浏览器页面,效果如图9-12所示。两个子组件中定义的按钮样式是略有差异。然后依次单击各个组件中的各个按钮,页面效果如图9-13所示。9.3.6Vuex中action<script
setup>
import
{
useStore
}
from
'vuex'
import
{
toRefs
}
from
'vue'
const
{
state,
dispatch
}
=
useStore()
const
{
name,
week,
weeklyPay
}
=
toRefs(state)
const
reduceWeeklyPay
=
()
=>
{
dispatch('reduce')
}
const
reduceWeeklyPayNum
=
()
=>
{
dispatch('reduceNum',
300)
}
const
reduceAsync
=
()
=>
{
dispatch('reduceAsync')
}
const
reduceNumAsync
=
(num)
=>
{
dispatch('reduceNumAsync',
num)
}
</script>
389.3.7Vuex中module
由于使用单一状态树,应用的所有状态会集中在一个比较大的对象中。在工程应用变得非常复杂时,store对象就有可能变得相当臃肿。为了解决以上问题,Vuex允许将store分割成模块(module)。每个模块拥有自己的state、getter、mutation、action,甚至是嵌套子模块--从上至下进行同样方式的分割。多模块定义方法。以下来定义两个模块,并注册到Vuex中store中。部分代码参考如下:const
module1
=
{
state:
{
...
},
mutations:
{
...
},
actions:
{
...
},
getters:
{
...
}
}
const
module2
=
{
state:
{
...
},
mutations:
{
...
},
actions:
{
...
}
}
exportdefaultcreateStore({
modules:
{
m1:
module1,
m2:
module2
}
})
store.state.m1//调用module1的状态store.state.m2
//调用module2的状态2.模块的局部状态及使用
对于模块内部
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 苏教版四年级语文下册期末复习要点精讲
- 部编版二年级下册绝句教学设计论文题目
- 一次函数图像的奇偶性与函数性质
- 北师大版尺规作图学习方法与心得
- 三年级上册苏教版口算题库
- 人教版高中物理解题技巧
- 五年级下册数学计算题精讲与解析
- 探索包身工的意义高中语文导学案
- 北师大版四年级语文同步练习题
- 一年二日的坚持与积累
- 部编版高中语文选择性必修上册教学计划(含教学进度表)
- 混凝土强度回弹检测方案完整版
- 学习雷锋好榜样-小学生学雷锋课件
- 体能训练概论(NSCA)
- 电影专业术语大全(附中英文对照)
- RQS沥青混合料拌合站远程监控系统解决方案
- 第一节无所不在的信息
- 全省水稻新品种(组合)抗稻瘟病鉴定方案
- 关于五年级道德与法制上册作业设计(4篇)
- 图书漂流活动方案计划
- 刘玲玲练习题答案合订
评论
0/150
提交评论