10vuex状态管理

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' },
        ]
    },
});
//导出 store 对象
export default store;

配置main.js:

import Vue from 'vue';
import App from './App';
import router from './router';
import store from './store'; // 引入我们前面导出的store对象

Vue.config.productionTip = false;

new Vue({
    el: '#app',
    router,
    store, // 把store对象添加到vue实例上
    components: { App },
    template: '<App/>'
});

配置App.vue:

<template>
    <div></div>
</template>

<script>
    export default {
        mounted() {
            // 使用this.$store.state.XXX可以直接访问到仓库中的状态
            console.log(this.$store.state.name); 
        }
    }
</script>

在控制台查看

avatar

官方建议我们以上操作this.$store.state.XXX最好放在计算属性中,当然,我也建议你这么使用,这样可以让你的代码看起来更优雅一些,就像这样:

export default {
    mounted() {
        console.log(this.getName); 
    },
    computed:{
        getName() {
            return this.$store.state.name;
        }
    },
}

是不是每次都写this.$store.state.XXX让你感到厌烦,你实在不想写这个东西怎么办,当然有解决方案,就像下面这样:

<script>
import { mapState } from 'vuex'; // 从vuex中导入mapState
export default {
    mounted() {
        console.log(this.name);
    },
    computed: {
        ...mapState(['name']), // 经过解构后,自动就添加到了计算属性中,此时就可以直接像访问计算属性一样访问它
    },
}
</script>

了解修饰器 getter

当你看到这里的时候,证明你上一步已经完美的创建好一个vue项目,并且将vuex安装了进去!
好!接下来,我们介绍一个读取操作的 “修饰利器” ---Getter

🤨 设想一个场景,你已经将store中的name展示到页面上了,而且是很多页面都展示了,此时产品经理过来找事儿

产品经理:所有的name前面都要加上“hello”!

😡我:为什么?

产品经理:我提需求还需要为什么吗?

我:好,我加!

这时候,你第一想到的是怎么加呢,emm...在每个页面上,使用this.$store.state.name获取到值之后,进行遍历,前面追加"hello"即可
🤦🏻‍♂️ 错!这样很不好,原因如下:

第一,假如你在A、B、C三个页面都用到了name,那么你要在这A、B、C三个页面都修改一遍,多个页面你就要加很多遍这个方法,造成代码冗余,很不好;
第二,假如下次产品经理让你把 “hello” 改成 “fuck” 的时候,你又得把三个页面都改一遍,这时候你只能抽自己的脸了...

👏🏻 吸取上面的教训,你会有一个新的思路:我们可以直接在store中对name进行一些操作或者加工,从源头解决问题!那么具体应该怎么写呢?这时候,本次将要介绍的这个Getter利器闪亮登场!

🤡 怎么用呢?不废话,show code!
首先,在store对象中增加getters属性

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
    state: {
        name: '张三',
        number: 0,
        list: [
            { id: 1, name: '111' },
            { id: 2, name: '222' },
            { id: 3, name: '333' },
        ]
    },
    // 在store对象中增加getters属性
    getters: {
        getMessage(state) { // 获取修饰后的name,第一个参数state为必要参数,必须写在形参上
            return `hello${state.name}`;
        }
    },
});

export default store;

在组件中使用:

export default {
    mounted() {
        // 注意不是$store.state了,而是$store.getters
        console.log(this.$store.state.name);
        console.log(this.$store.getters.getMessage);
    }
}

看下控制台:
avatar

🤖 官方建议:
是不是每次都写this.$store.getters.XXX让你感到厌烦,你实在不想写这个东西怎么办,当然有解决方案,官方建议我们可以使用mapGetters去解构到计算属性中,就像使用mapState一样,就可以直接使用this调用了,就像下面这样:

<script>
import { mapState, mapGetters } from 'vuex';
export default {
    mounted() {
        console.log(this.name);
        console.log(this.getMessage);
    },
    computed: {
        ...mapState(['name']),
        ...mapGetters(['getMessage']),
    },
}
</script>

🤗 OK,当你看到这里,你已经成功的把Getter用起来了,你也能明白在什么时候应该用到getters,你可以通过计算属性访问(缓存),也可以通过方法访问(不缓存),你甚至可以在getters的方法里面再调用getters方法,当然你也实现了像state那样,使用mapGetters解构到计算属性中,这样你就可以很方便的使用getters啦!
😎 读取值的操作我们有 “原生读(state)” 和 “修饰读(getters)”,接下来就要介绍怎么修改值了!

Mutation

🤗 OK!首先恭喜你看到了这里,至此,我们已经成功访问到了store里面的值,接下来我来介绍一下怎么修改state里面的值。

说到修改值,有的同学就会想到这样写:

// 错误示范
this.$store.state.XXX = XXX;

🤪 首先,这里我先明确的说明:这是错误的写法!这是错误的写法!这是错误的写法!
为什么上面是错误的写法?因为这个store仓库比较奇怪,你可以随便拿,但是你不能随便改
😱 这里说一条重要原则:Mutations里面的函数必须是同步操作,不能包含异步操作!
修改store/index.js:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
    state: {
        name: '张三',
        number: 0,
    },
    mutations: { // 增加nutations属性
        setNumber(state) {  // 增加一个mutations的方法,方法的作用是让num从0变成5,state是必须默认参数
            state.number = 5;
        }
    },
});

export default store;

修改App.vue:

<script>
export default {
    mounted() {
        console.log(`旧值:${this.$store.state.number}`);
        this.$store.commit('setNumber');
        console.log(`新值:${this.$store.state.number}`);
    },
}
</script>

控制台查看:

avatar

🤡 以上是简单实现mutations的方法,是没有传参的,如果我们想传不固定的参数怎么办?接下来教你解决
修改index.js:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
    state: {
        name: '张三',
        number: 0,
    },
    mutations: {
        setNumber(state) {
            state.number = 5;
        },
        setNumberIsWhat(state, number) { // 增加一个带参数的mutations方法
            state.number = number;
        },
    },
});

export default store;

修改App.vue:

<script>
export default {
    mounted() {
        console.log(`旧值:${this.$store.state.number}`);
        this.$store.commit('setNumberIsWhat', 666);
        console.log(`新值:${this.$store.state.number}`);
    },
}
</script>

查看控制台:

avatar

官方建议传递一个对象进去,这样看起来更美观,对象的名字你可以随意命名,但我们一般命名为payload,代码如下:

mutations: {
    setNumber(state) {
        state.number = 5;
    },
    setNumberIsWhat(state, payload) { // 增加一个带参数的mutations方法,并且官方建议payload为一个对象
        state.number = payload.number;
    }
}

修改App.vue:

<script>
export default {
    mounted() {
        console.log(`旧值:${this.$store.state.number}`);
        this.$store.commit('setNumberIsWhat', { number: 666 });  // 调用的时候也需要传递一个对象
        console.log(`新值:${this.$store.state.number}`);
    },
}
</script>

此时可以得到和之前一样的效果,并且代码更加美观!
🤖 官方建议:就像最开始的mapStatemapGetters一样,我们在组件中可以使用mapMutations以代替this.$store.commit('XXX'),是不是很方便呢?

<script>
import { mapMutations } from 'vuex';
export default {
    mounted() {
        this.setNumberIsWhat({ number: 999 });
    },
    methods: {   // 注意,mapMutations是解构到methods里面的,而不是计算属性了
        ...mapMutations(['setNumberIsWhat']),
    },
}
</script>

了解异步操作:Actions

Actions存在的意义是假设你在修改state的时候有异步操作,vuex作者不希望你将异步操作放在Mutations中,所以就给你设置了一个区域,让你放异步操作,这就是Actions

配置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}`);
},

可以采用mapActions的方式,把相关的actions解构到methods中,用this直接调用:

<script>
import { mapActions } from 'vuex';
export default {
    methods: {
        ...mapActions(['setNum']),   // 就像这样,解构到methods中
    },
    async mounted() {
        await this.setNum({ number: 123 });  // 直接这样调用即可
    },
}
</script>

🤓 看了例子,是不是明白了,action就是去提交mutation的,什么异步操作都在action中消化了,最后再去提交mutation的。

🤠 OK,看到这里,你应该明白action在vuex的位置了吧,什么时候该用action,什么时候不用它,你肯定有了自己的判断,最主要的判断条件就是我要做的操作是不是异步,这也是action存在的本质。当然,你不要将action和mutation混为一谈,action其实就是mutation的上一级,在action这里处理完异步的一些操作后,后面的修改state就交给mutation去做了。

posted @ 2021-11-08 22:50  天亮說晚安  阅读(36)  评论(0)    收藏  举报