一、Vuex单界面到多界面状态管理切换:
①安装配置vuex:
step1.下载vuex
npm install vuex --save
step2.因为所有文件导入都堆积在main.js里太过繁杂,所以新建一个store文件夹 里面存放index.js:
import Vue from 'vue'
import Vuex from 'vuex'
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
state: {
counter: 1000
},
mutations: {
},
actions: {
},
modules: {
}
})
// 3.导出store对象
export default store
3.将store挂载到main.js4.使用方法:$store.state
通过这个方式,能直接使用state里的数据。
<template>
<div>
<h2>{{$store.state.counter}}</h2>
</div>
</template>
<script>
export default {
name: 'HelloVueX' ,
data () {
return {
}
}
}
</script>
<style scope>
</style>
二、Vuex-devtools和mutations
①devtools插件
在Chrome安装devtools插件打开项目之后:
就可以看见vue项目的基本结构:
可以看到和实时监控state的数据
<h2>{{$store.state.counter}}</h2>
<button @click="$store.state.counter++">+</button>
<button @click="$store.state.counter--">-</button>
可以发现,我们直接通过$store.state.counter++来处理数据是不会对state里的counter有影响的。
②mutations:
所以要想修改state的数据必须通过mutations:
step1.在mutation里写方法:
const store = new Vuex.Store({
state: {
counter: 1000
},
mutations: {
// 方法
increment() {
this.state.counter++
},
decrement() {
this.state.counter--
}
},
actions: {
},
modules: {
}
})
step2.在组件里写方法提交:
methods: {
addition() {
this.$store.commit('increment')
},
subtraction() {
this.$store.commit('decrement')
}
},
<template>
<div id="app">
<h2>App里的内容————————————————————————————————</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="addition">+</button>
<button @click="subtraction">-</button>
<hello-vuex/>
</div>
</template>
<script>
import HelloVuex from './components/HelloVuex'
export default {
name: 'App',
data() {
return {
massage: '我是App里的massage',
}
},
methods: {
addition() {
this.$store.commit('increment')
},
subtraction() {
this.$store.commit('decrement')
}
},
components: {
HelloVuex
}
}
</script>
三、Vuex核心概念:
Vuex有几个比较核心的概念:
- State
- Getters
- Mutation
- Action
- Module
①Vuex-state单一状态树:
(单一数据源)
一个项目只有一个store
- 如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难。
- 所以Vuex也使用了单一 状态树来管理应用层级的全部状态。
- 单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。
②vuex-getters的使用:
其实可以当成组件共享的计算属性
例:写一个对象数组,找出里面age>20的对象
普通写法:
state: {
counter: 1000,
students: [
{id:110, name: 'he' , age: 18},
{id:111, name: 'li' , age: 20},
{id:112, name: 'ha' , age: 21},
{id:113, name: 'ss' , age: 19},
]
}
computed: {
moreThan20() {
return this.$store.state.students.filter(s => s.age > 20)
}
}
<h2>{{moreThan20}}</h2>
但这种写法只能在需要的每个组件里都写上这个计算属性
如果写在getters里面 每个组件都可以直接使用:
getters:
const store = new Vuex.Store({
state: {
students: [
{id:110, name: 'he' , age: 18},
{id:111, name: 'li' , age: 20},
{id:112, name: 'ha' , age: 21},
{id:113, name: 'ss' , age: 19},
]
},
getters: {
//这里要传参
moreThan20(state) {
return state.students.filter(s => s.age > 20)
}
}
})
使用方法:
<h2>{{$store.getters.moreThan20}}</h2>
另外,在getters的方法里也可以将getters作为参数,调用getters里的其他方法:
moreThan20(state) {
return state.students.filter(s => s.age > 20)
},
moreThan20Length(state, getters) {
return getters.moreThan20.length
}
如何传入一个参数呢?moreThan20Length(state, age)
不能这样写,这样写第二个参数还是会被当成getters。
正确方法:
moreAgeStu(state) {
return function(age) {
return state.students.filter(s => s.age > age)
}
//箭头函数写法:
return age => {
return state.students.filter(s => s.age >age)
}
moreAgeStu(8)
③Mutation状态更新:
state里的数据改变只能通过Mutation!
有参数传递的例子
参数被称为mutation的payload(载荷)
index.js中:
mutations: {
// 方法
incrementCount(state,count) {
state.counter += count
}
}
组件中:
methods: {
addCount(count){
this.$store.commit('incrementCount',count)
}
}
<button @click="addCount(5)">+5</button>
mutation的提交风格:
之前的commit只是一种普通的提交风格
特殊的提交风格:
Vue还提供了另外-种风格,它是一个包含type属性的对象
this.$store.commit({
type: 'changeCount',
count
})
Mutation中的处理方式是将整个commit的对象作为payload使用,所以代码没有改变,依然如下:
changeCount(state,payload){
state.count = payload.count
}
④Vuex的响应式原理:
Vuex的store中的state是响应式的,当state中的数据发生改变时, Vue组件会自动更新.
这就要求我们必须遵守一些Vuex对应的规则:
提前在store中初始化好所需的属性.
当给state中的对象添加新属性时,使用下面的方式:
➢方式一: 使用Vue.set(obj, ‘newProp’, 123)
➢方式二:用新对象给旧对象重新赋值
//info是一个对象
//如果想往这个对象里面添加新的属性并做到响应式 只能用vue.set
vue.set(state.info, 'address' , '洛杉矶')
//同样的如果想删除这个属性:
vue.delete(state.info, 'address')
⑤vue-mutations的类型常量:
可以在store里加一个js文件:
然后通过这样的方式定义常量:
export const INCREMENT = 'increment'
别的文件中导入后即可使用:
导入:
import{
INCREMENT
} from './mutations-type'
使用:
mutations: {
// 方法
[INCREMENT]() {
this.state.counter++
}
}
methods: {
addition() {
this.$store.commit(INCREMENT)
}
}
⑥vuex-actions的使用:
mutation里的所有代码都不能有异步操作:
如果有异步操作,就用actions来替代Mutations
但是actions任然不能直接修改state里的数据,如果有一个异步操作需要修改state里的数据,那么正确的方法应该是:
index.js:
state: {
info:{
name: 'he',
age: 18
}
}
mutations: {
updateInfo(state) {
state.info.name = 'hels'
}
}
actions: {
// 默认属性context 上下文
aUpdateInfo(context) {
setTimeout(() => {
context.commit('updateInfo')
}, 1000);
}
}
组件中:
methods: {
updataInfo() {
this.$store.dispatch('aUpdateInfo')
}
}
dispatch:
context后面也可以接一个传递参数payload:
aUpdateInfo(context,payload) {
setTimeout(() => {
context.commit('updateInfo');
console.log(payload);
}, 1000);
}
this.$store.dispatch('aUpdateInfo','我是携带的信息')
但是通常携带的信息不只一个的时候怎么办呢:可以将payload作为一个对象:
updataInfo() {
this.$store.dispatch('aUpdateInfo',{
massage: '我是携带的信息',
success: () => {
console.log('里面已经完成')
}
})
}
aUpdateInfo(context,payload) {
setTimeout(() => {
context.commit('updateInfo');
console.log(payload.massage)
payload.success()
}, 1000);
}
更简洁优雅的写法:Promise:
aUpdateInfo(context,payload) {
return new Promise((resolve,reject) => {
setTimeout(() => {
context.commit('updateInfo');
console.log(payload.massage)
payload.success()
resolve('1111')
}, 1000);
})
}
this.$store
.dispatch('aUpdateInfo','我是携带的信息')
.then(res => {
console.log('里面完成了提交')
console.log(res)
})
⑦vuex-modules的使用:
Vue使用单-状态树,那么也意味着很多状态都会交给Vuex来管理.
当应用变得非常复杂时,store对象就有可能变得相当臃肿,为了解决这个问题,Vuex允许我们将store分割成模块(Module),而每个模块拥有自己的state. mutations、actions, getters等。
1.modules的state:
const moduleA = {
state: {
name: 'jing'
},
mutations: {},
actions: {},
getters: {},
}
const store = new Vuex.Store({
state: {},
mutations: {},
actions: {},
getters: {},
modules: {
a: moduleA
}
})
<h2 style="color:#FF8484">{{$store.state.a.name}}</h2>
这里只能用
$store.state.a.name
而不是.modules.a
因为在Devtools里可以看到 a会自动变成state的一个对象:
2.modules的mutation:
const moduleA = {
state: {
name: 'jing'
},
mutations: {
updateName(state,payload) {
state.name = payload
}
},
actions: {},
getters: {},
}
const store = new Vuex.Store({
state: {
name: 'jing'
},
mutations: {},
actions: {},
getters: {},
modules: {
a: moduleA
}
})
methods: {
updateName() {
//这里还是commit
this.$store.commit('updateName','yang')
}
}
<h2 style="color:#FF8484">{{$store.state.a.name}}</h2>
<button @click="updateName">修改名字</button>
3.modules的getters:
无论是定义在store里的getters还是定义在modules里的getters 调用的方法都相同:
const moduleA = {
state: {
name: 'jing'
},
mutations: {},
getters: {
fullname(state) {
return state.name + 'hahaha'
}
},
actions: {}
}
<h2>{{$store.getters.fullname}}</h2>
我们都知道getters里的方法可以将本身getters作为参数来调用里面的其他函数:
getters: {
fullname(state) {
return state.name + 'hahaha'
},
fullname2(state,getters) {
return getters.fullname + '33'
}
}
而且,在modules中的getters还可以将store里的state作为参数:
getters: {
fullname(state) {
return state.name + 'hahaha'
},
fullname2(state,getters) {
return getters.fullname + '33'
},
fullname3(state,getters,rootState) {
return getters.fullname2 + rootState.counter
}
}
const store = new Vuex.Store({
state: {
counter: 1000
}
})
4.modules的actions:
跟store的actions没什么差别:
写在modules里的actions commit时是commit的modules的mutation
但是如果想要commit store的mutation的话:
context.rootGetters
⑧vuex-store文件夹的目录组织:
1.state的抽离:
const state = {
counter: 1000,
students: [
{id:110, name: 'he' , age: 18},
{id:111, name: 'li' , age: 20},
{id:112, name: 'ha' , age: 21},
{id:113, name: 'ss' , age: 19},
],
info:{
name: 'he',
age: 18
}
}
const store = new Vuex.Store({
state,
}
2.mutations的抽离:
新建一个mutations.js:
import{
INCREMENT
} from './mutations-type'
export default {
// 方法
[INCREMENT]() {
this.state.counter++
},
decrement() {
this.state.counter--
},
incrementCount(state,count) {
state.counter += count
},
addStudent(state,stu) {
state.students.push(stu)
},
updateInfo(state) {
state.info.name = 'hels'
}
}
然后:
import mutations from './mutations';
const store = new Vuex.Store({
state,
mutations,
}
3.actions的抽离:
和mutation一样可以抽离成一个函数:
export default {
// 默认属性context 上下文
aUpdateInfo(context,payload) {
setTimeout(() => {
context.commit('updateInfo');
console.log(payload);
}, 1000);
},
aUpdateInfo(context,payload) {
return new Promise((resolve,reject) => {
setTimeout(() => {
context.commit('updateInfo');
console.log(payload.massage)
payload.success()
resolve('1111')
}, 1000);
})
}
}
import actions from './actions'
const store = new Vuex.Store({
state,
mutations,
actions,
}
4.getters的抽离:
同理:
export default{
powerCounter() {
return this.state.counter * this.state.counter
},
moreThan20(state) {
//这里要传参
return state.students.filter(s => s.age > 20)
},
moreThan20Length(state, getters) {
return getters.moreThan20.length
},
moreAgeStu(state) {
// return function(age) {
// return state.students.filter(s => s.age > age)
// }
return age => { return state.students.filter(s => s.age >age) }
}
}
import getters from './getters';
const store = new Vuex.Store({
state,
mutations,
actions,
getters,
}
5.modules的抽离:
新建一个modules文件夹:
在下面创建一个moduleA文件:
moduleA.js:
export default {
state: {
name: 'jing'
},
mutations: {
updateName(state,payload) {
state.name = payload
}
},
getters: {
fullname(state) {
return state.name + 'hahaha'
},
fullname2(state,getters) {
return getters.fullname + '33'
},
fullname3(state,getters,rootState) {
return getters.fullname2 + rootState.counter
}
},
actions: {
aupdateName(context) {
setTimeout(() => {
context.commit('updateName','hels33')
}, 1000);
}
}
}
import moduleA from './modules/moduleA';
const store = new Vuex.Store({
state,
mutations,
actions,
getters,
modules: {
a: moduleA
}
})