Vue14-vuex

 


Vue14-vuex

1.vuex简介

  1. vuex是vue中一个集中式状态(数据)管理的vue插件,可以对vue应用中多个组件的共享状态进行集中式的管理(读/写);vuex也是一种组件间通信的方式,且适用于任意组件间通信。
  2. vuex的Github地址https://github.com/vuejs/vuex
  3. vuex使用场景:多个组件依赖于同一状态;来自不同组件的行为需要变更同一状态。
    image

2.求和案例-纯vue版

  1. App.vue。
<template>
    <div>
        <Count/>
    </div>
</template>

<script>
    import Count from "./components/Count";
    export default {
        name: "App",
        components: {Count}
    }
</script>

<style scoped>

</style>
  1. Count.vue。
<template>
    <div>
        <h3>当前和为:{{sum}}</h3>
        <!-- value="2"会被当做字符串处理,有两种解决方式。
            1 使用v-model.number强制转换为数字。
            2 使用:value,这样2就是js表达式,2就为数字。
         -->
        <select v-model.number="num">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
        <button @click="incrementOdd">当值为奇数时加</button>
        <button @click="incrementWait">等等加</button>
    </div>
</template>

<script>
    export default {
        name: "Count",
        data() {
            return {
                sum: 0,
                num: 1
            }
        },
        methods: {
            increment() {
                this.sum += this.num;
            },
            decrement() {
                this.sum -= this.num;
            },
            incrementOdd() {
                if (this.sum % 2) {
                    this.sum += this.num;
                }
            },
            incrementWait() {
                setTimeout(() => {
                    this.sum += this.num;
                }, 500);
            }
        }
    }
</script>

<style scoped>
    button {
        margin-left: 5px;
    }
</style>

3.vuex的按照和使用

  1. 安装vuex,npm i vuex@3。注:vue2中只能使用vuex的3版本,vue3中只能使用vuex的4版本,当前vue最新的是3版本,所以直接使用npm i vuex安装的是vuex的4版本,直接使用会报错,所有使用vuex@3来按照vuex的3版本。
  2. main.js中引入和使用vuex。注:后续引入和使用的代码会移动到store/index.js中。
import Vuex from 'vuex'
// 引入vuex后,new Vue时就会多出来一个store配置项。
Vue.use(Vuex);
  1. 准备store对象。
    1. src下创建store文件夹,在store中创建index.js。
    2. index.js中分别创建store中核心的三个概念,actions、mutations和state。
    3. index.js中创建store,store中包含actions、mutations和state。
    4. index.js中执行Vue.use(Vuex);,防止在main.js中执行报错。
    5. main.js中引入store,然后将其放入Vue配置对象中。

4.求和案例-vuex版

  1. store中的index.js。
// 该文件用于创建vuex中最为核心的store

/**
 * 在store/index.js中执行Vue.use(Vuex);的原因。
 *  1 在实例化store前必须前执行Vue.use(Vuex),
 *      否则报错[vuex] must call Vue.use(Vuex) before creating a store instance.
 *  2 在main.js中import store from './store'时就会实例化store对象,
 *	所以需要import store from './store'前执行Vue.use(Vuex); 
 *  但是vue脚手架在打包代码时,会将import汇总放在顶部,即import会先被执行,所以需要将
 *  Vue.use(Vuex);代码放在index.js中执行,防止报错。
 */

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex);

// 准备actions,用于响应组件中的动作
const actions = {
    /**
     * actions中的函数接受两个参数。
     *  context,store的上下文环境。
     *  value,$store.dispatch()时传递的参数。
     */
    jia(context, value) {
        // Actions中通过commit调用mutations中的JIA函数。
        context.commit('JIA', value);
    },
    jian(context, value) {
        context.commit('JIAN', value);
    },
    jiaOdd(context, value) {
        if (context.state.num % 2) {
            context.commit('JIA', value);
        }
    },
    jiaWait(context, value) {
        setTimeout(() => {
            context.commit('JIA', value);
        }, 500)
    },
};

// 准备mutations,用于操作数据(state)
const mutations = {
    // mutations中的函数一般大写,用于和actions中的函数名区分。
    // mutations中的函数接受两个参数。
    //  state对象,state中的属性会加上get和set。
    //  value,通过commit()函数调用传递的参数。
    JIA(state, value) {
        state.sum += value;
    },
    JIAN(state, value) {
        state.sum -= value;
    }
};
// 准备state,用于存储数据
const state = {
    sum: 0
};

// 创建store
export default new Vuex.Store({
    actions: actions,
    mutations: mutations,
    state: state
});
  1. main.js。
import Vue from 'vue'
import App from './App'

// 引入store。index.js是脚手架默认识别js,所以可以直接写为from './store',
// 相当与from './store/index.js'
import store from './store'

new Vue({
    el: '#app',
    render: h => h(App),
    store: store,
    beforeCreate() {
        Vue.prototype.$bus = this;
    }
});
  1. App.vue和原来保持一样。
  2. Count.vue。
<template>
    <div>
        <h3>当前和为:{{$store.state.sum}}</h3>
        <select v-model.number="num">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
        <button @click="incrementOdd">当值为奇数是加</button>
        <button @click="incrementWait">等等加</button>
    </div>
</template>

<script>
    export default {
        name: "Count",
        data() {
            return {
                num: 1
            }
        },
        methods: {
            increment() {
                // 通过dispatch调用Actions中的jia函数,并且传递参数num。
                this.$store.dispatch('jia', this.num);
            },
            decrement() {
                // 当Actions中没有业务操作的时候,可以直接通过commit调用mutations来操作数据。
                this.$store.commit('JIAN', this.num);
            },
            incrementOdd() {
                this.$store.dispatch('jiaOdd', this.num);
            },
            incrementWait() {
                this.$store.dispatch('jiaWait', this.num);
            }
        }
    }
</script>

<style scoped>
    button {
        margin-left: 5px;
    }
</style>

5.求和案例-getters

  1. 当state中的数据需要经过加工后在使用时,可以在getters中加工。
  2. main.js和App.vue代码保持不变。
  3. 添加getters后的store/index.js。
... actions、mutations和state省略
const getters = {
    bigSum(state) {
        return state.sum * 10;
    }
};
// 创建store
const store = new Vuex.Store({
    actions: actions,
    mutations: mutations,
    state: state,
    getters: getters
});
  1. Count.vue中使用getters。
<template>
    <div>
        <h3>当前和10倍为:{{$store.getters.bigSum}}</h3>
    </div>
</template>

6.求和案例-mapState和mapGetters

  1. mapState用于将state中的数据映射为计算属性;mapGetters用于将getters中的数据映射为计算属性。
  2. store/index.js、main.js、App.vue代码保持不变。
  3. Count.vue。
<template>
    <div>
        <!-- 使用mapGetters简化getters后的写法。 -->
        <h3>当前和为:{{bigSum}}</h3>
        <!-- 每次插值语法中都需要写$store.state.xxx,可以使用计算属性,简化这种写法。 -->
        <!-- 使用mapState简化读取state数据的写法。 -->
        <h3>姓名 {{xingming}}</h3>
        <h3>学校 {{xuexiao}}</h3>
        <h3>年龄 {{age}}</h3>
    </div>
</template>

<script>
    import {mapState, mapGetters} from 'vuex'
    export default {
        ... name data methods属性省略。
        computed: {
            // 1 使用计算属性访问state中数据的访问。
            // 可以看到计算属性访问state中的数据后,return this.$store.state.xxx都是相同的代码,
            //      所以可以使用mapState来生成这段代码。
            xingming() {
                return this.$store.state.name;
            },
            // 2 借助mapState生成计算属性,从state中读取数据,对象写法。
            // 使用mapState生成xuexiao和age的计算属性,xuexiao和age去访问state中school和age属性。
            //      生成的计算属性的代码和上面的xingming计算属性作用相同。
            // ...mapState({}),将...mapState({})生成的对象展开,然后放到computed中。
            ...mapState({xuexiao: 'school'}),

            // 如果计算属性名和state中的属性重名,不能使用属性的简写,
            //    	因为简写会将school当做变量来处理,而这里的school是一个字符串。
            // ...mapState({school, age})

            // 3 借助mapState生成计算属性,从state中读取数据,数组写法。
            //      计算属性名和state中的属性重名时可以采用数组写法。
            ...mapState(['age']),

            // 4 借助mapGetters生成getters中的计算属性,对象写法。
            ...mapGetters({bigSum: 'bigSum'}),

            // 5 借助mapGetters生成getters中的计算属性,数组写法。
            ...mapGetters(['bigSum']),
        },
</script>

7.求和案例-mapActions和mapMutations

  1. mapActions生成和actions对话的方法;mapMutations生成和mutations对话的方法。

  2. mapActions和mapMutations在使用时需要注意:如果需要传递参数,就需要在模板中绑定事件时将参数传递好,否则传递的参数就是事件对象。

  3. Count.vue。

<template>
    <div>
        <!-- 当使用mapMutation和mapAction生成代码后,方法的调用需要传递参数。 -->
        <button @click="decrement(num)">-</button>
        <button @click="JIAN(num)">-</button>
        <button @click="incrementOdd(num)">当值为奇数是加</button>
        <button @click="jiaWait(num)">等等加</button>
    </div>
</template>

<script>
    import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
    export default {
        ... 省略name data computed
        methods: {
            increment() {
                // 通过dispatch调用Actions中的jia函数,并且传递参数num。
                this.$store.dispatch('jia', this.num);
            },

            // 1 使用mapMutations生成对应的方法,方法中会调用commit来联系mutations,对象式写法。
            //      使用mapMutations生成代码,生成代码需要传递一个对象,
            //      对象的key为方法名,对象的value为mutations中的方法名。
            // 默认生成的代码为:
            /*
            decrement(value) {
                this.$store.commit('jia', value);
            },
             */
            // 2 默认生成decrement方法使用@click="decrement"调用,vue中事件触发会将事件作为方法的参数传递,即value是事件event。
            //      所以调用mapMutations生成的方法需要指定参数,如@click="decrement(num)"
            ...mapMutations({decrement: 'JIAN'}),

            // 3 使用mapMutations生成对应的方法,方法中会调用commit来联系mutations,数组写法。
            //      数组写法需要注意,数组中的值为mutations中定义函数的函数名;
            //      当生成的方法名和mutations中的函数名相同时使用数组写法。
            ...mapMutations(['JIAN']),

            // 4 使用mapActions生成的方法会调用dispatch来联系actions,对象写法。
            ...mapActions({incrementOdd: 'jiaOdd'}),
            // 5 使用mapActions生成的方法会调用dispatch来联系actions,数组写法。
            ...mapActions(['jiaWait']),
        }
    }
</script>

8.求和案例-vuex模块化和命名空间

  1. 模块化。求和案例出现两个组件:Count求和组件和Person人员组件。模块化会将服务与Count组件的数据定义到store/count.js中;将服务与Person组件的数据定义到person.js中。然后在store/index.js中统一引入。
  2. 模块化让代码更好的维护,让多种数据分类更加明确。
  3. count.js。
export default {
    namespaced: true,
    // 省略actions mutations getters中的内容。
    actions: {},
    mutations: {},
    getters: {},
};
  1. person.js。
import axios from 'axios'
import {nanoid} from 'nanoid'
// 导出
export default {
    // 使用mapXxx引入模块,需要在模块中定义名称空间。
    namespaced: true,
    actions: {
        addPersonWang(state, value) {
            if (value.name.indexOf('王') === 0) {
                state.commit('ADD_PERSON', value);
            } else {
                alert('添加的人必须姓王!');
            }
        },
        addPersonServer(state) {
            // http://api.uixsj.cn/hitokoto/get?type=social获取励志语言。
            axios.get(`http://api.uixsj.cn/hitokoto/get?type=social`).then(
                response => {
                    state.commit('ADD_PERSON', {id: nanoid(), name: response.data});
                },
                error => {
                    alert(error.message);
                }
            )
        }
    },
    mutations: {
        ADD_PERSON(state, value) {
            console.log('Mutations中的ADD_PERSON被调用了');
            state.personList.unshift(value);
        }
    },
    state: {
        personList: [
            {id: '001', name: '张三'}
        ]
    },
    getters: {
        // 返回第一个人的姓名
        firstPersonName(state) {
            return state.personList[0].name;
        }
    },
};
  1. index.js。
import Vuex from 'vuex'
import Vue from 'vue'

// 分别引入count.js/person.js
import countOptions from './count'
import personOptions from './person'

Vue.use(Vuex);

export default new Vuex.Store({
    // 将countOptions和personOptions作为模块传入。
    modules: {
        a: countOptions,
        b: personOptions,
    }
});
  1. Person.vue中使用模块化,模块化后原生写法。
<template>
    <div>
        <h3>第一个人的名字:{{firstPersonName}}</h3>
        <input type="text" placeholder="请输入姓名" v-model="name">
        <button @click="add">提交</button>
        <button @click="addPersonWang">提交</button>
        <button @click="addPersonServer">添加一个人,名字随机</button>
        <ul>
            <li v-for="person in personList" :key="person.id">{{person.name}}</li>
        </ul>
        <h3 style="color: red">上面组件和为 {{sum}}</h3>
    </div>
</template>

<script>
    import {nanoid} from 'nanoid'

    export default {
        methods: {
            add() {
                const personObj = {id: nanoid(), name: this.name};
                // 如果使用store原生的commit写法,模块后,调用模块中的方法,需要使用b/ADD_PERSON
                this.$store.commit('b/ADD_PERSON', personObj);
            },
            addPersonWang() {
                const personObj = {id: nanoid(), name: this.name};
                // 如果使用store原生的dispatch写法,模块后,调用模块中的方法,需要使用b/ADD_PERSON
                this.$store.dispatch('b/addPersonWang', personObj);
            },
            addPersonServer() {
                return this.$store.dispatch('b/addPersonServer')
            }
        },
        computed: {
            personList() {
                return this.$store.state.b.personList;
            },
            sum() {
                return this.$store.state.a.sum;
            },
            firstPersonName() {
                // 如果使用store原生调用getters写法,
                // 模块化后需要使用 this.$store.getters['b/firstPersonName']
                return this.$store.getters['b/firstPersonName']
            },
        }
    }
</script>
  1. Count.vue中使用模块化,模块化后mapXxx写法。
<template>
    <div>
        <h3>当前和为:{{a.sum}}</h3>
        <h3>当前和为:{{bigSum}}</h3>
        <h3>姓名 {{a.name}}</h3>
        <button @click="jia(num)">+</button>
        <h3 style="color: red">下方组件人数为 {{personList.length}}</h3>
    </div>
</template>

<script>
    import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
    export default {
        methods: {
            ...mapMutations('a', ['JIAN']),

            ...mapActions('a', ['jiaWait', 'jiaOdd', 'jia']),
        },
        computed: {
            // 1 对象方式引入a模块,a中每个属性或方法的调用都需要使用a.xxx。
            ...mapState({a: 'a'}),
            ...mapState('b', ['personList']),

            // 2 通过namespaced名称空间引入,需要在组件中加namespaced: true。
            // 3 通过namespaced名称空间引入后,调用方式可以直接写为xxx,而不是a.xxx。
            ...mapGetters('a', ['bigSum']),
        }
    }
</script>
posted @   行稳致远方  阅读(35)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示