Vuex
【一】了解Vuex
【1】想象一个场景
如果你的项目里有很多页面(组件/视图),页面之间存在多级的嵌套关系,此时,这些页面假如都需要共享一个状态的时候,此时就会产生以下两个问题:
- 多个视图依赖同一个状态
- 来自不同视图的行为需要变更同一个状态
【2】解决方案
对于第一个问题,假如是多级嵌套关系,你可以使用父子组件传参进行解决,虽有些麻烦,但好在可以解决;对于兄弟组件或者关系更复杂组件之间,就很难办了,虽然可以通过各种各样的办法解决,可实在很不优雅,而且等项目做大了,代码就会变成屎山,实在令人心烦。
对于第二个问题,你可以通过父子组件直接引用,或者通过事件来变更或者同步状态的多份拷贝,这种模式很脆弱,往往使得代码难以维护,而且同样会让代码变成屎山。
【3】换一种思路
把各个组件都需要依赖的同一个状态抽取出来,在全局使用单例模式进行管理
在这种模式下,任何组件都可以直接访问到这个状态,当状态发生改变时,所有组件都发生更新
【4】vuex的诞生
这就是 Vuex 背后的基本思想,借鉴了 Flux、Redux。与其他模式不同的是,Vuex 是专门为 Vue 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。
【5】什么时候该用vuex
这个问题因人而异,如果你不需要开发大型的单页应用,此时你完全没有必要使用vuex,比如你的页面就两三个,使用vuex后增加的文件比你现在的页面还要多,那就没这个必要了。
假如你的项目达到了中大型应用的规模,此时您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
【二】安装Vuex
进入项目,在命令行输入安装指令
npm install vuex --save
然后配置 vuex,使其工作起来:在src路径下创建store文件夹,然后创建index.js文件,文件内容如下:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
// 定义一个name,以供全局使用
name: '张三',
// 定义一个number,以供全局使用
number: 0,
// 定义一个list,以供全局使用
list: [
{ id: 1, name: '111' },
{ id: 2, name: '222' },
{ id: 3, name: '333' },
],
},
});
export default store;
修改main.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 定义一个name,以供全局使用
name: 'hqq'
},
})
最后修改APP.vue
export default {
name: 'HomeView',
data() {
return {}
},
methods: {},
created() {
console.log('全局', this.$store.state.name)
},
}
</script>
此时,启动项目,打开控制台就会发现输出了在store里面定义的name值
- 官方建议我们以上操作this.$store.state.XXX最好放在计算属性中,当然,我也建议你这么使用,这样可以让你的代码看起来更优雅一些,就像这样:
export default {
name: 'HomeView',
data() {
return {}
},
methods: {},
computed: {
getName() {
return this.$store.state.name
}
},
created() {
console.log('计算属性', this.getName)
},
}
</script>
此时就可以得到在控制台得到一样的效果
【三】修饰器Getter
【1】场景
getter时一个读取操作的修饰利器,
假设一个场景,
- 你已经将store的name全部都展示到页面上了,
- 此时产品经理说把所有的name前面都加上hello
- 这是如果没有别的办法就只能在每个页面上使用this.$store.state.name获取到值之后,进行遍历,前面追加"hello"。
这样做很不好,原因如下
- 代码冗余
- 如果下次有其他需求,要把hello改成hi,又要进行很大的修改
【2】使用
所以这时候getter就登场了,它可以从源头解决问题,它可以把state里的数据放到一个方法里面,得到返回值,如果有修改需求,只需要修改这个方法的返回值即可
首先,在store对象中增加getters属性
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 定义一个name,以供全局使用
name: 'hqq'
},
getters: {
getMessage(state) {
// 获取修饰后的name,第一个参数为必要参数,得到的就是state对象
return `大帅哥${state.name}`
}
}
})
在组件中使用
export default {
name: 'HomeView',
methods: {},
created() {
console.log(this.$store.getters.getMessage)
},
}
【四】修改值Mutation
说到修改,大多数人会想到直接得到值然后=赋值修改
this.$store.state.XXX = XXX;
这样做确实可以达到效果,但是不好,vuex开放了专门的接口提供给我们修改
修改store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 定义一个name,以供全局使用
name: 'hqq'
},
getters: {
getMessage(state) {
// 获取修饰后的name,第一个参数为必要参数
return `大帅哥${state.name}`
}
},
// 改值用的
mutations: {
// 第一个参数是state对象,第二个参数放要修改的值
setName(state, name) {
state.name = name
}
},
})
修改APP.vue
export default {
name: 'HomeView',
created() {
this.$store.commit('setName', 'green')
},
}
【五】异步操作Actions
Actions存在的意义是假设你在修改state的时候有异步操作,vuex作者不希望你将异步操作放在Mutations中,所以就给你设置了一个区域,让你放异步操作,这就是Actions
修改store/index.js
const store = new Vuex.Store({
state: {
name: '张三',
number: 0,
},
mutations: {
setNumberIsWhat(state, payload) {
state.number = payload.number;
},
},
actions: {
// 增加actions属性
setNum(content) {
// 增加setNum方法,默认第一个参数是content,其值是复制的一份store
return new Promise(resolve => {
// 我们模拟一个异步操作,1秒后修改number为888
setTimeout(() => {
content.commit('setNumberIsWhat', { number: 888 });
resolve();
}, 1000);
});
},
},
});
修改App.vue
async mounted() {
console.log(`旧值:${this.$store.state.number}`);
await this.$store.dispatch('setNum');
console.log(`新值:${this.$store.state.number}`);
},