Web前端之vuex基础
什么是Vuex
Vuex是一个专门为Vue.js应用程序开发的一个状态管理模式,它采用了集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
什么是状态管理模式
new Vue({ // state data () { return { count: 0 } }, // view template: ` <div>{{ count }}</div> `, // actions methods: { increment () { this.count } }})
状态自管理应用包含以下几个部分:
state,驱动应用的数据源
view,以声明方式将 state 映射到视图
actions,响应在 view 上的用户输入导致的状态变化
单向数据流:
当遇到多个组件共享时,单向数据流的简洁性很容易被破坏
多个视图依赖于同一状态
来自不同视图的行为需要变更同一状态
使用Vuex管理数据的好处:
能够在 vuex 中集中管理共享的数据,便于开发和后期进行维护
能够高效的实现组件之间的数据共享,提高开发效率
存储在 vuex 中的数据是响应式的,当数据发生改变时,页面中的数据也会同步更新
Vuex使用
安装或引入
可以通过CDN节点引入(不推荐)
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js"></script>
也可以使用npm命令进行安装(推荐)
npm install vuex -S
当然,如果在用Vue CLI 脚手架进行创建项目时,如果引用了Vuex咋不在需要上面的操作了
使用
使用时需要在新建js文件中引用,当然我们还需要在main.js中引入使用
当我们使用Vue CLI 脚手架创建项目后
我们在src目录下的views下面新建文件夹Store
在此文件夹内创建index.js文件
在index中编写如下编码
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: { }, mutations: { }})export default store
然后在main.js中引入调用并且创建实例
父传子
count 变量定义在父组件 App.vue 中
两个子组件中都一个一个 prop,名为 count
父组件通过 :count 为子组件中的 prop count 传值
子组件单击按钮时,通过 $emit,向外广播,告诉父组件应该为 count 变量 1或者 -1
Add.vue
<template> <div id="app"> <my-add :count="count" @handle-add="add"></my-add> <my-sub :count="count" @handle-sub="del" ></my-sub> </div></template><script>import c1 from './views/children'import c2 from './views/children1'export default { data() { return { count: 0 } }, components: { 'my-add': c1, 'my-sub': c2 }, methods: { add() { this.count }, del() { this.count-- } }}</script>
children.vue
<template> <div class="main"> <h3>变量 count 的值为:{{ count }}</h3> <button @click="add"> 1</button> </div></template><script>export default { props: ['count'], methods: { add() { this.$emit('add') } }}</script>
children1.vue
<template> <div class="main"> <h3>变量 count 的值为:{{ count }}</h3> <button @click="sub">-1</button> </div></template><script>export default { props: ['count'], methods: { sub() { this.$emit('del') } }}</script>
加加减减的写法
index.js文件
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: { count: 0 }, mutations: { add(state) { state.count }, del(state) { state.count-- } }})export default store
修改组件
<template> <div class="main"> <h3>变量 count 的值为:{{ $store.state.count }}</h3> <button @click="add"> 1</button> <button @click="del">-1</button> </div></template><script>export default { methods: { add() { this.$store.commit('add') }, del() { this.$store.commit('del') } }}</script>
效果:
核心
states
单一状态树
State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储
vuex 中的 state 相当于组件中的 data
State中的数据与组件 data 中的数据一样,也是响应式的
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: { name: '张三', age: 20, sex: '男' }})export default store
组件使用state中的值
<template> <div class="home">{{ $store.state }}</div></template><script>export default {}</script>
计算属性
上面原样使用不方便使用所以使用计算属性来便于使用
<template> <div class="home"> <p>{{ name }}</p> <p>{{ age }}</p> <p>{{ sex }}</p> </div></template><script>export default { computed: { name() { return this.$store.state.name }, age() { return this.$store.state.age }, sex() { return this.$store.state.sex } }}</script>
但上面那样使用代码量太多
我们可以使用辅助函数:mapState
<template> <div class="home"> <p>{{ name }}</p> <p>{{ age }}</p> <p>{{ sex }}</p> </div></template><script>// 在vuex中引出mapState辅助函数import { mapState } from 'vuex'export default { computed: mapState({ name: state => state.name, age: state => state.age, sex: state => state.sex })}</script>
computed 的值是一个对象,所以 mapState 函数的返回值一定是个对象
name,age,sex都是计算属性的名称
使用箭头函数时,可以省略 this.$store,而是直接使用 state
再次简洁一步
<template> <div class="home"> <p>{{ name }}</p> <p>{{ age }}</p> <p>{{ sex }}</p> </div></template><script>// 在vuex中引出mapState辅助函数import { mapState } from 'vuex'export default { computed: mapState({ name: 'name', age: 'age', sex: 'sex' })}</script>
最简洁方法:
映射的计算属性的名称与 state 的子节点名称相同时,我们可以给 mapState 传一个字符串数组
<template> <div class="home"> <p>{{ name }}</p> <p>{{ age }}</p> <p>{{ sex }}</p> </div></template><script>// 在vuex中引出mapState辅助函数import { mapState } from 'vuex'export default { computed: mapState(['name', 'age', 'sex'])}</script>
这时候我们的计算属性就不能叫 name、age、sex了,而是与 state 中的属性同名
mapState 中不是一个对象了,而是一个数组,数组的元素名称就是你想要使用的 state 中的属性名称
展开运算符
使用辅助函数后,mapState 函数的返回值是一个对象
<template> <div class="home"> <p>{{ name }}</p> <p>{{ age }}</p> <p>{{ sex }}</p> </div></template><script>// 在vuex中引出mapState辅助函数import { mapState } from 'vuex'export default { computed: mapState(['name', 'age', 'sex']), created() { const res = mapState(['name', 'age', 'sex']) console.log(res) }}</script>
输出结果
所以利用展开运算符可以更改上面的编码
<template> <div class="home"> <p>{{ name }}</p> <p>{{ age }}</p> <p>{{ sex }}</p> </div></template><script>// 在vuex中引出mapState辅助函数import { mapState } from 'vuex'export default { computed: { ...mapState(['name', 'age', 'sex']) }}</script>
但是我感觉用在这里,那简直的画蛇添足、多此一举了
不过展开运算符用在其他地方还是很有必要的
Getter数据获取器
在Store文件夹下index.js添加
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: { teacher: [ { name: '张三', age: 20, sex: '男' }, { name: '李四', age: 25, sex: '女' } ] }, getters: { // 定义gettter,统计所有完成的事项 doneTodos: state => { return state.teacher.filter(t => t.sex) } }})export default store
组件中
<template> <div class="home"> {{ $store.getters.doneTodos }} </div></template><script>export default {}</script>
也可使用 mapGetters 和 mapState 辅助函数
<template> <div class="home"> <p>{{ doneTodos }}</p> <p>{{ teacher }}</p> </div></template><script>// 在vuex中引出mapState辅助函数import { mapState, mapGetters } from 'vuex'export default { computed: { ...mapState(['teacher']), ...mapGetters(['doneTodos']) }}</script>
Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)
这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
任何时候想添加新值,且不能直接添加,只有通过mutation才可
Store 下的 index.js
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: { teacher: [ { name: '张三', age: 20, sex: '男' }, { name: '李四', age: 25, sex: '女' } ] }, getters: { // 定义gettter,统计所有完成的事项 doneTodos: state => { return state.teacher.filter(t => t.sex) } }, mutations: { add(state) { state.teacher.push({ name: '王五', age: 19, sex: '女' }) } }})export default store
组件
<template> <div class="home"> <p>{{ doneTodos }}</p> <p>{{ teacher }}</p> <button @click="add">添加</button> </div></template><script>// 在vuex中引出mapState辅助函数import { mapState, mapGetters } from 'vuex'export default { computed: { ...mapState(['teacher']), ...mapGetters(['doneTodos']) }, methods: { add() { this.$store.commit('add') } }}</script>
提交载荷
传入额外的参数,即 mutation 的 载荷(payload)
index.js
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: { teacher: [ { name: '张三', age: 20, sex: '男' }, { name: '李四', age: 25, sex: '女' } ] }, getters: { // 定义gettter,统计所有完成的事项 doneTodos: state => { return state.teacher.filter(t => t.sex) } }, mutations: { add(state, teacher) { state.teacher.push(teacher) } }})export default store
组件中使用
<template> <div class="home"> <p>{{ doneTodos }}</p> <p>{{ teacher }}</p> <button @click="add">添加</button> </div></template><script>// 在vuex中引出mapState辅助函数import { mapState, mapGetters } from 'vuex'export default { computed: { ...mapState(['teacher']), ...mapGetters(['doneTodos']) }, methods: { add() { this.$store.commit('add', { name: '王五', age: 19, sex: '女' }) } }}</script>
大多数情况下,载荷应该是一个对象,可以包含多个字段并且记录的 mutation 会更易读Mutation 必须是同步函数:不要在其中执行ajax 等异步操作
mapMutations辅助函数减少代码
组件中作如下修改
<template> <div class="home"> <p>{{ doneTodos }}</p> <p>{{ teacher }}</p> <button @click="addI">添加</button> </div></template><script>// 在vuex中引出辅助函数import { mapState, mapGetters, mapMutations } from 'vuex'export default { computed: { ...mapState(['teacher']), ...mapGetters(['doneTodos']) }, methods: { ...mapMutations(['add']), addI() { this.add({ name: '王五', age: 19, sex: '女' }) } }}</script>
Action
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态
Action 可以包含任意异步操作
js文件中
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: { teacher: [ { name: '张三', age: 20, sex: '男' }, { name: '李四', age: 25, sex: '女' } ] }, getters: { // 定义gettter,统计所有完成的事项 doneTodos: state => { return state.teacher.filter(t => t.sex) } }, mutations: { add(state, teacher) { state.teacher.push(teacher) } }, actions: { addS(state, teacher) { setTimeout(() => { state.commit('add', teacher) }, 2000) } }})export default store
组件中
<template> <div class="home"> <p>{{ doneTodos }}</p> <p>{{ teacher }}</p> <button @click="addI">添加</button> </div></template><script>// 在vuex中引出辅助函数import { mapState, mapGetters, mapMutations } from 'vuex'export default { computed: { ...mapState(['teacher']), ...mapGetters(['doneTodos']) }, methods: { ...mapMutations(['add']), addI() { // this.add({ name: '王五', age: 19, sex: '女' }) this.$store.dispatch('addS', { name: '王五', age: 19, sex: '女' }) } }}</script>
mapActions 辅助函数映射使用
组件中
<template> <div class="home"> <p>{{ doneTodos }}</p> <p>{{ teacher }}</p> <button @click="addI">添加</button> </div></template><script>// 在vuex中引出辅助函数import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'export default { computed: { ...mapState(['teacher']), ...mapGetters(['doneTodos']) }, methods: { ...mapMutations(['add']), ...mapActions(['addS']), addI() { // this.add({ name: '王五', age: 19, sex: '女' }) this.addS({ name: '王五', age: 19, sex: '女' }) } }}</script> methods: { ...mapMutations(['add']), addI() { // this.add({ name: '王五', age: 19, sex: '女' }) this.$store.dispatch('addS', { name: '王五', age: 19, sex: '女' }) } }}</script>
mapActions 辅助函数映射使用
组件中
<template> <div class="home"> <p>{{ doneTodos }}</p> <p>{{ teacher }}</p> <button @click="addI">添加</button> </div></template><script>// 在vuex中引出辅助函数import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'export default { computed: { ...mapState(['teacher']), ...mapGetters(['doneTodos']) }, methods: { ...mapMutations(['add']), ...mapActions(['addS']), addI() { // this.add({ name: '王五', age: 19, sex: '女' }) this.addS({ name: '王五', age: 19, sex: '女' }) } }}</script>