Vuex是做什么的?
管理什么状态
有什么状态需要在多个组件上共享呢
1.如果你做过大型开放,你一定遇到过多个状态,在多个界面之间的共享问题。
2.比如用户的登录状态、用户名称、头像、地理位置信息等等。
3.比如商品的收藏、购物车中的物品等等
对我来说 我认为Vuex就是一个大管家统一来帮助我们管理,看一下官方的图片
1.若你要修改state里面的状态,最好是要经过Mutations,因为这里能让Devtools监听到你的State变化,方便我们在使用Vuex能观察到哪个组件改变了该值以及操作
2.Actions这一步主要是有异步操作的时候(请求网络接口)
让我们来看看简单的例子使用
子组件的代码
<template>
<div>
<h2>我是子组件</h2>
<h2>{{$store.state.counter}}</h2> //值是直接可以通过$store.state拿到对应的值
</div>
</template>
<script>
export default{
name:'APP',
}
</script>
<style>
</style>
父组件的代码
<template>
<div id="app">
<h2>{{message}}</h2>
<h2>{{$store.state.counter}}</h2> //值是直接可以通过$store.state拿到对应的值
<button @click="addition">+</button>
<button @click="subtraction">-</button>
<h2>-----------------</h2>
<hello-vuex ></hello-vuex>
</div>
</template>
<script>
import HelloVuex from './components/HelloVuex.vue'
export default{
name:'APP',
data(){
return{
message:'我是APP组件',
}
},
methods:{
addition(){
this.$store.commit('increment') //通过commit 然后选择store下对应的mutations方法
},
subtraction(){
this.$store.commit('decrement')
},
},
components:{
HelloVuex
}
}
</script>
<style lang="less">
</style>
Vuex----------store中的index.js代码
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
//变量定义在state中
counter:520
},
mutations: {
//对应的方法要定义在mutations
increment(state){
state.counter++
},
decrement(state){
state.counter--
}
},
actions: {
},
getters:{
},
modules: {
}
})
结果如下图
Getter的使用
Getter基本使用–有时候我们需要从store获取到state一些变异后的状态,比如下面的Store中
例子一 比如要获取counter的平方
store下面的index.js的代码
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
counter:520
},
mutations: {
//方法
increment(state){
state.counter++
},
decrement(state){
state.counter--
}
},
actions: {
},
getters:{ //重点是这里
//比如要获取counter的平方
powerCounter(state){
return state.counter * state.counter
}
},
modules: {
}
})
<template>
<div id="app">
<h2>{{message}}</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="addition">+</button>
<button @click="subtraction">-</button>
<h2>-----App内容:getters的相关信息------------</h2>
<h2>getters:{{$store.getters.powerCounter}}</h2> //直接通过$store.getters获取到对应的值
<h2>-----------------</h2>
<hello-vuex ></hello-vuex>
</div>
</template>
例子二 获取学生年龄大于20的个数
store中的index.js代码 state增加了students getters增加了新的gettersmoreage获取年龄超过二十岁的方法
----------------------------
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
counter:520,
students:[
{
id:1,
name:'chen',
age:18
},
{
id:2,
name:'si',
age:28
},
{
id:3,
name:'na',
age:38
},
{
id:4,
name:'rich',
age:48
},
]
},
mutations: {
//方法
increment(state){
state.counter++
},
decrement(state){
state.counter--
}
},
actions: {
},
getters:{
//比如要获取counter的平方
powerCounter(state){
return state.counter * state.counter
},
//获取年龄超过20岁的
gettersmoreage(state){
return state.students.filter(v =>{
return v.age >=20
})
}
},
modules: {
}
})
父组件的代码 有两种方式可以实现可以拿到年龄超过20岁的,第一种是通过计算属性在单个页面下
(但是多个页面如果都想同时使用的话,就要复制一份代码就很累赘)
第二种方法就是通过vuex直接在getters定义处理好的方法,这边直接调用$store.getters.xxx()方法即可
---------------------
<template>
<div id="app">
<h2>{{message}}</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="addition">+</button>
<button @click="subtraction">-</button>
<h2>-----App内容:getters的相关信息------------</h2>
<h2>getters:{{$store.getters.powerCounter}}</h2>
<h3>getters:{{$store.getters.gettersmoreage}}</h3>
<h3>computed:{{moreage}}</h3>
<h2>-----------------</h2>
<hello-vuex ></hello-vuex>
</div>
</template>
<script>
import HelloVuex from './components/HelloVuex.vue'
export default{
name:'APP',
data(){
return{
message:'我是APP组件',
}
},
computed:{
moreage(){ //可以通过计算属性来过滤获取
return this.$store.state.students.filter(v =>{
return v.age >= 20
})
}
},
methods:{
addition(){
this.$store.commit('increment') //通过commit 然后选择对应的方法
},
subtraction(){
this.$store.commit('decrement')
},
},
components:{
HelloVuex
}
}
</script>
例子三 Getters作为参数和传递参数
结果如下图:
例子四 从外面传入参数进来 Getters接受参数并返回一个函数(这里描述不是很好,大家看看例子)
<template>
<div id="app">
<h2>-----App内容:getters的相关信息------------</h2>
//向getters里面的方法传递一个参数
<h3>getters传递参数返回函数:{{$store.getters.moreAgeStu(8)}}</h3>
<h3>getters传递参数返回函数:{{$store.getters.moreAgeStu(30)}}</h3>
</div>
</template>
state: {
counter:520,
students:[
{
id:1,
name:'chen',
age:18
},
{
id:2,
name:'si',
age:28
},
{
id:3,
name:'na',
age:38
},
{
id:4,
name:'rich',
age:48
},
]
},
getters:{
//获取别人传入参数年龄的方法 ---返回一个函数
moreAgeStu(state){
return function(enterage){ //接收传递进来的参数
return state.students.filter(v => v.age > enterage) //es6语法 简写
}
/*
这里也可以简写成
return enterage =>{
return state.students.filter(v => v.age > enterage)
}
但是阅读性太差了 不推荐大家这样写
*/
},
},
结果如图所示
Mutations的详细使用
Vuex的store状态的更新唯一方式:提交Mutations
例子一 Mutations传递参数(单个)
<h2>-----App内容:mutations的详细使用------------</h2>
<button @click="addCount(5)">+5</button>
<button @click="addCount(10)">+10</button>
methods:{
addition(){
this.$store.commit('increment') //通过commit 然后选择对应的方法
},
subtraction(){
this.$store.commit('decrement')
},
//加入上面的方法
addCount(count){
this.$store.commit('incrementCount',count) //传过去参数
}
},
在store的index.js下面中的mutations方法下面添加
mutations: {
//方法
increment(state){
state.counter++
},
decrement(state){
state.counter--
},
//详细使用
incrementCount(state,count){ //第二个接收传入进来的参数
state.counter += count //进行操作
},
},
例子二 Mutations传递参数 但是如果参数不是一个呢?(参数被称为是mutations的载荷(Payload))
比如我们有很多参数要传递
这个时候,我们通常会以对象的形式传递,也是Payload是一个对象
再从这个对象抽取出相关的信息
<h2>-----App内容:mutations的详细使用------------</h2>
<button @click="addStudent()">添加学生</button>
methods:{
addStudent(){
const stu = {
id:115,
name:'curry',
age:33,
}
this.$store.commit('maddStudent',stu)
},
},
mutations: {
//添加学生
maddStudent(state,stu){
state.students.push(stu)
}
},
结果如图所示
Mutations的提交风格
正常一般的提交风格
返回的数据如下
所以直接处理数据即可
特殊的提交风格
返回的数据如下
所以应该把第二个传进来的参数改为payload,payload是一个对象
小小的补充一下响应式原理
响应式原理:
vue2.0在初始化数据时,会使用Object.defineProperty这个函数重新定义data中所有的属性,当页面使用对应属性时,首先会进行依赖收集(收集当前组件中的watcher),当属性发生变化时会通知相关的依赖进行更新操作(发布订阅)
Vue3.x改用
Proxy
替代Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
这里Vuex的响应式例子 (Vue.set,Vue.delete )
<h2>-----App内容:info响应式原理------------</h2>
<h2>{{$store.state.info}}</h2>
methods:{
//响应式
updateinfo(){
this.$store.commit('updateinfo')
}
},
在store中的index中
mutations: {
//响应式
updateinfo(state){
state.info.name= 'sina' //页面跟着变化
//state.info['address'] = '洛杉矶' //注意!!!这个对象可以添加 但是做不到响应式 页面不会跟着改变
//pop push都是响应式的
//Vue.set是响应式的
//数组使用Vue.set时 例子 Vue.set(this.letters,0, 'bbbbb')中间参数是一个number
Vue.set(state.info,'address','洛杉矶') //对象响应式
delete state.info.age //这里也做到了响应式 因为是vue3.0通过proxy实现响应式
Vue.delete(state.info,'hh') //这里是vue2.0的删除响应式
}
},
结果如图所示
点击按钮页面变化为
Action的使用
当你发送异步请求的修改数据的时候 需要在action去请求—是为了让devtools能监测到变化
## store下的index.js的部分代码
state: {
counter:520,
students:[
{
id:1,
name:'chen',
age:18
},
{
id:2,
name:'si',
age:28
},
{
id:3,
name:'na',
age:38
},
{
id:4,
name:'rich',
age:48
},
],
info:{
name:'kobe',
age:42,
hh:11,
}
},
mutations: {
//响应式
updateinfo(state){
//不能直接在这里异步修改数据
state.info.name= 'sina' //页面跟着变化
Vue.set(state.info,'address','洛杉矶') //对象响应式
delete state.info.age //这里也做到了响应式 因为是vue3.0通过proxy实现响应式
Vue.delete(state.info,'hh') //这里是vue2.0的删除响应式
}
},
actions: {
// context:上下文
//异步修改state的属性 只能这样做
aUpdateInfo(context,payload){ //只能填写context方法 接收payload参数
setTimeout(()=>{ //调用上面的mutations下面的updateinfo方法
context.commit('updateinfo') (因为修改数据只能在mutations下修改)
console.log(payload)
},1000)
}
},
<h2>-----App内容:info响应式原理------------</h2>
<h2>{{$store.state.info}}</h2>
<button @click="updateinfo()">响应式改变</button>
----------------------------------------------------
methods:{
updateinfo(){
// this.$store.commit('updateinfo') //这是同步修改state属性
this.$store.dispatch('aUpdateInfo','我是payload') //这是异步请求修改state 需要用到dispatch拿action里面的方法,也可以传递参数
}
}
结果如图所示
教你们一个优雅的优化----------Promise
/*优雅的写法 Promise*/
actions: {
aUpdateInfo(context,payload){
return new Promise((resolve,reject) => { //优雅的优化
setTimeout(()=>{
context.commit('updateinfo')
console.log(payload)
resolve('hello 我是promise')
},1000)
})
}
}
------------外面调用的方法---------------------
updateinfo(){
/*优雅的写法*/
this.$store.dispatch('aUpdateInfo','我是携带的信息').then(res=>{
console.log('里面完成了提交') //回调告诉完成了
console.log(res) //还可以接收promise中resolve传递出来的参数
})
}
兄弟萌 够不够优雅 这里的优化就是到时候登录请求接口要用到的
结果截图如下
最后一个重点了哟--------modules的使用
//创建对象
const moduleA ={
state:{
name:'zhangsan'
},
mutations:{
updateAName(state,payload){
state.name = payload
}
},
// context:上下文 这个比较特殊 它只会调用自己模块的东西
actions:{
aUpdateName(context){
setTimeout(()=>{
context.commit('updateAName','wangwu')
},1000)
}
},
getters:{
fullname(state){
return state.name + '帅哥'
},
fullname2(state,getters){
return getters.fullname + 'sina'
},
//在模块里有第三个参数 rootState --可以拿到下面大模块的state
fullname3(state, getters, rootState){
return getters.fullname2 + rootState.counter
}
},
}
export default new Vuex.Store({
modules: {
a:moduleA
}
})
<h2>-----App内容:modules中的内容------------</h2>
<h2>moduleA的state:{{$store.state.a}}</h2>
<button @click="updateAname">修改模块A的名字</button>
<h2>moduleA的getter:{{$store.getters.fullname}}</h2>
<h2>moduleA的getter:{{$store.getters.fullname2}}</h2>
<h2>moduleA的getter:{{$store.getters.fullname3}}</h2>
<button @click="mokuaiyibu">模块异步修改名字</button>
<h2>-----App内容:modules中的内容------------</h2>
---------------------------------------------------------------------
methods{
/*模块的方法*/
updateAname(){
this.$store.commit('updateAName','lisi')
},
mokuaiyibu(){
this.$store.dispatch('aUpdateName')
}
}
结果跟上面基本差不多, module的用法基本跟上面差不多 主要要注意actions中的context,我们打印该数据看一下----------主要多了根的概念
最后添上所有的代码
目录结构
main.js
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')
index.js
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations.js'
import actions from './actions.js'
import getters from './getters.js'
import moduleA from './module/moduleA.js'
Vue.use(Vuex)
const state = {
counter:520,
students:[
{
id:1,
name:'chen',
age:18
},
{
id:2,
name:'si',
age:28
},
{
id:3,
name:'na',
age:38
},
{
id:4,
name:'rich',
age:48
},
],
info:{
name:'kobe',
age:42,
hh:11,
}
}
export default new Vuex.Store({
state,
mutations,
actions,
getters,
modules: {
a:moduleA
}
})
mutations.js
import {INCREMENTCOUNT} from './mutations-types.js'
import Vue from 'vue'
export default {
//方法
increment(state){
state.counter++
},
decrement(state){
state.counter--
},
//详细使用
/*
一般提交风格
incrementCount(state,count){ //第二个接收传入进来的参数
console.log(count) //payload是一个对象 接收特殊风格的提交
state.counter += count //进行操作
},
*/
//特殊提交风格
[INCREMENTCOUNT](state,payload){ //第二个接收传入进来的参数
console.log(payload.count) //payload是一个对象 接收特殊风格的提交
state.counter += payload.count //进行操作
},
//添加学生(添加对象)
maddStudent(state,stu){
state.students.push(stu)
},
//响应式
updateinfo(state){
state.info.name= 'sina' //页面跟着变化
//pop push都是响应式的
//state.info['address'] = '洛杉矶' //这个对象可以添加 但是做不到响应式 页面不会跟着改变
//Vue.set是响应式的
//数组使用Vue.set时 例子 Vue.set(this.letters,0, 'bbbbb')中间参数是一个number
Vue.set(state.info,'address','洛杉矶') //对象响应式
delete state.info.age //这里也做到了响应式 因为是vue3.0通过proxy实现响应式
Vue.delete(state.info,'hh') //这里是vue2.0的删除响应式
}
}
mutations-types.js
export const INCREMENTCOUNT = 'incrementCount'
actions.js
export default {
// context:上下文 就是这个store对象
//异步修改state的属性 只能这样做
// aUpdateInfo(context,payload){
// setTimeout(()=>{
// context.commit('updateinfo')
// console.log(payload.message)
// payload.success()
// },1000)
// }
/*优雅的写法 Promise*/
aUpdateInfo(context,payload){
return new Promise((resolve,reject) => {
setTimeout(()=>{
context.commit('updateinfo')
console.log(payload)
resolve('hello 我是promise')
},1000)
})
}
}
getters.js
export default {
//比如要获取counter的平方
powerCounter(state){
return state.counter * state.counter
},
//获取年龄超过20岁的
gettersmoreage(state){
return state.students.filter(v =>{
return v.age >=20
})
},
//getters作为传递参数
gettersmoreagelength(state,getters){
return getters.gettersmoreage.length
},
//获取别人传入参数年龄的方法 ---返回一个函数
moreAgeStu(state){
// return function(enterage){ //接收传递进来的参数
// return state.students.filter(v => v.age > enterage) //es6语法 简写
// }
return enterage =>{
return state.students.filter(v => v.age > enterage)
}
},
}
moduleA.js
export default{
state:{
name:'zhangsan'
},
mutations:{
updateAName(state,payload){
state.name = payload
}
},
// context:上下文 这个比较特殊 它只会调用自己模块的东西
actions:{
aUpdateName(context){
console.log(context)
setTimeout(()=>{
context.commit('updateAName','wangwu')
},1000)
}
},
getters:{
fullname(state){
return state.name + '帅哥'
},
fullname2(state,getters){
return getters.fullname + 'sina'
},
//在模块里有第三个参数 rootState --可以拿到下面大模块的state
fullname3(state, getters, rootState){
return getters.fullname2 + rootState.counter
}
},
}
父组件 APP.vue
<template>
<div id="app">
<h2>-----App内容:modules中的内容------------</h2>
<h2>moduleA的state:{{$store.state.a}}</h2>
<button @click="updateAname">修改模块A的名字</button>
<h2>moduleA的getter:{{$store.getters.fullname}}</h2>
<h2>moduleA的getter:{{$store.getters.fullname2}}</h2>
<h2>moduleA的getter:{{$store.getters.fullname3}}</h2>
<button @click="mokuaiyibu">模块异步修改名字</button>
<h2>-----App内容:modules中的内容------------</h2>
<h2>{{message}}</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="addition">+</button>
<button @click="subtraction">-</button>
<h2>-----App内容:info响应式原理------------</h2>
<h2>{{$store.state.info}}</h2>
<button @click="updateinfo()">响应式改变</button>
<h2>-----App内容:mutations的详细使用------------</h2>
<button @click="addCount(5)">+5</button>
<button @click="addCount(10)">+10</button>
<button @click="addStudent()">添加学生</button>
<h2>-----App内容:getters的相关信息------------</h2>
<h2>getters:{{$store.getters.powerCounter}}</h2>
<h3>getters:{{$store.getters.gettersmoreage}}</h3>
<h3>computed:{{moreage}}</h3>
<h3>getters作为参数:{{$store.getters.gettersmoreagelength}}</h3>
<h3>getters传递参数返回函数:{{$store.getters.moreAgeStu(8)}}</h3>
<h3>getters传递参数返回函数:{{$store.getters.moreAgeStu(30)}}</h3>
<h2>-----------------</h2>
<hello-vuex ></hello-vuex>
</div>
</template>
<script>
import HelloVuex from './components/HelloVuex.vue'
import {INCREMENTCOUNT} from './store/mutations-types.js'
export default{
name:'APP',
data(){
return{
message:'我是APP组件',
}
},
computed:{
moreage(){ //可以通过计算属性来过滤获取
return this.$store.state.students.filter(v =>{
return v.age >= 20
})
}
},
methods:{
addition(){
this.$store.commit('increment') //通过commit 然后选择对应的方法
},
subtraction(){
this.$store.commit('decrement')
},
addCount(count){
//正常的提交风格
// this.$store.commit('incrementCount',count) //传过去参数
//特殊的提交风格
// this.$store.commit({
// type:'incrementCount',
// count:count
// })
//特殊的提交风格以及使用常量
this.$store.commit({
type:INCREMENTCOUNT,
count:count
})
},
addStudent(){
const stu = {
id:115,
name:'curry',
age:33,
}
this.$store.commit('maddStudent',stu)
},
//响应式
updateinfo(){
// this.$store.commit('updateinfo') //这是同步修改state属性
// this.$store.dispatch('aUpdateInfo',{
// message: '我是携带的信息',
// success: ()=>{
// console.log('里面已经完成')
// }
// }) //这是异步请求修改state 需要用到dispatch 也可以传递参数(传递对象)
/*优雅的写法*/
this.$store.dispatch('aUpdateInfo','我是携带的信息').then(res=>{
console.log('里面完成了提交')
console.log(res)
})
},
/*模块的方法*/
updateAname(){
this.$store.commit('updateAName','lisi')
},
mokuaiyibu(){
this.$store.dispatch('aUpdateName')
}
},
components:{
HelloVuex
}
}
</script>
<style lang="less">
</style>
子组件HelloVuex.vue
<template>
<div>
<h2>我是子组件</h2>
<h2>{{$store.state.counter}}</h2>
<h2>getters:{{$store.getters.powerCounter}}</h2>
<h3>getters:{{$store.getters.gettersmoreage}}</h3>
</div>
</template>
<script>
export default{
name:'APP',
}
</script>
<style>
</style>
面试题
1.vuex是什么?怎么使用?哪种功能场景使用它?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。简单来说就是:应用遇到多个组件共享状态时,使用vuex。
场景:多个组件共享数据或者是跨组件传递数据时
vuex的流程
页面通过mapAction异步提交事件到action。action通过commit把对应参数同步提交到mutation,mutation会修改state中对应的值。最后通过getter把对应值跑出去,在页面的计算属性中,通过,mapGetter来动态获取state中的值
2.vuex有哪几种属性
有五种,分别是State , Getter , Mutation , Action , Module (就是mapAction)
-
state:vuex的基本数据,用来存储变量
-
geeter:从基本数据(state)派生的数据,相当于state的计算属性
-
mutation:提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数。
-
action:和mutation的功能大致相同,不同之处在于 ==》1. Action 提交的是 mutation,而不是直接变更状态。 2. Action 可以包含任意异步操作。
-
modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
3.Vue.js中ajax请求代码应该写在组件的methods中还是vuex的actions中?
一、如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。
二、如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用,并包装成promise返回,在调用处用async
await处理返回的数据。如果不要复用这个请求,那么直接写在vue文件里很方便
4.Vuex中actions和mutations的区别
Mutation 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation
非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数
(handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
Action Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。 .
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})