Vuex 基础(一)

Vuex 是一个专门为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件状态。

也就是将 Vue.js 程序中各个页面公用的数据和获取、改变这些数据的方法抽离出来,方便各个页面调用,以及页面之间的数据传输。

下面通过一个例子来讲解Vuex

我们首先创建一个Vue工程。

我们假设有两个富豪榜,这两个富豪榜分属两个界面,通过组件注册,在一个主界面。

一、使用 props 属性传递参数

页面结构如下:

Vuex 基础(一)_第1张图片

我们在父页面 App.vuedata 属性中加入人员列表,并引入两个子页面,通过 v-bind 指令将 data 传递给子页面。

<template>
  <div id="app">
    <!-- 将 data 元素传递给子页面  -->
    <salary-list-one v-bind:sendedSalaryList="salaryList"></salary-list-one>
    <salary-list-two v-bind:sendedSalaryList="salaryList"></salary-list-two>
  </div>
</template>
<script>
  import salarylist1 from './components/salarylist1';
  import salarylist2 from './components/salarylist2';
  export default {
    name: "app",
    data() {
      return {
        salaryList: [{
            name: "马云",
            salaryAmount: 1000
          },
          {
            name: "马化腾",
            salaryAmount: 900
          },
          {
            name: "李彦宏",
            salaryAmount: 800
          }
        ]
      };
    },
    components: {
      'salary-list-one': salarylist1,
      'salary-list-two': salarylist2
    }
  };
</script>
<style scoped>
</style>

子页面通过 props 属性接收父页面传递过来的参数,并展示出来。

salarylist1.vue 代码如下

<template>
    <div id="salary-list-fisrt">
        <h2>财富榜</h2>
        <ol>
            // 通过 v-for 命令将其展示出来

            <li v-for="salary in sendedSalaryList">
                {{salary.name}}的工资是:{{salary.salaryAmount}}
            </li>
        </ol>
    </div>
</template>
<script>
    export default {
        name: "salary-listfirst",
        // 通过 props 属性接收父页面传来的参数

        props: {
            sendedSalaryList: {
                type: Array,
                required: true
            }
        }

    }
</script>
<style>

</style>

salarylist2.vue 代码和上面的代码基本相似

运行结果如下:

Vuex 基础(一)_第2张图片

二、使用 Vuex 简化数据传递

虽然页面之间可以使用 props, $emit 等传递参数,但是这种参数的传递会面临以下问题

问题一 :多个视图依赖于同一状态。

问题二 :来自不同视图的行为需要变更同一状态。

对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。

对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?

在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!

另外,通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,我们的代码将会变得更结构化且易维护

如果之前没有安装 vuex,使用下面的命令安装

npm install vuex --save

在项目根目录下新建 store/store.js 文件,这个文件中保存的就是这个应用中需要共享的内容, 而且整个应用中只有一个。

main.js 文件中引入 vuex

import Vue from 'vue'
import App from './App.vue'
import {store} from './store/store'


Vue.config.productionTip = false

new Vue({
  store:store,
  render: h => h(App),
}).$mount('#app')

1. State

state 类似于 Vue 实例中的 data 属性,不同的是 state 是共享的。

现在将刚才 App.vue 中的 data 保存在store.js 中,将 App.vue 中保存的数据移除。

现在,两个财富榜都没有了数据,如图:

Vuex 基础(一)_第3张图片

现在改为从 store 请求数据

// store.js
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export const store = new Vuex.Store({
    state: {
        storeSalaryList: [{
                name: "马云",
                salaryAmount: 1000
            },
            {
                name: "马化腾",
                salaryAmount: 900
            },
            {
                name: "李彦宏",
                salaryAmount: 800
            }
        ]
    }
});

在 财富榜2 中引入获取 store

<template>
    <!--salarylist2.vue -->
    <div id="salary-list-fisrt">
        <h2>财富榜2</h2>
        <ol>
            <li v-for="salary in salaryList ">
                {{salary.name}}的工资是:{{salary.salaryAmount}}
            </li>
        </ol>
    </div>
</template>
<script>
    export default {
        name: "salary-list-first",
        computed: {
            salaryList() {
                // 通过 computed 属性,获取到 store.js 中的数据,并返回

                return this.$store.state.storeSalaryList;
            }
        }
    }
</script>
<style>

</style>

在 App.vue 中去掉 v-bind 指令

<template>
  <div id="app">
    <salary-list-one ></salary-list-one>
    <salary-list-two ></salary-list-two>
  </div>
</template>
<script>
import salarylist1 from  './components/salarylist1';
import salarylist2 from './components/salarylist2';
export default {
  name: "app",
  data() {
    return {

    };
  },
  components:{
    'salary-list-one':salarylist1,
    'salary-list-two':salarylist2
  }
};
</script>
<style scoped>
</style>

运行结果如下:

Vuex 基础(一)_第4张图片

如果在 salarylist1.vue 中也加入上面的 store 引用,就可以连个财富榜就可以全部展示。

2.getter

在一些情况下,当获取到 state 的数据之后,我们希望进行一些加工处理再进行展示。

现在,我们将上面每个人的工资翻倍,再增加 **美元($)**符号。

修改 salarylist1.vuesalarylist2.vue 代码,在 computed 属性中增加,doubleSalary 属性

<template>
    <div id="salary-list-fisrt">
        <h2>财富榜</h2>
        <ol>
            <li v-for="salary in doubleSalary">
                {{salary.name}}的工资是:{{salary.salaryAmount}}
            </li>
        </ol>
    </div>
</template>
<script>
    export default {
        computed: {
            salaryList() {
                return this.$store.state.storeSalaryList;
            },
            // doubleSalary 属性对每个人 state 的数据加工后进行返回
            doubleSalary() {
                var afterDoubleSalary = 
                     this.$store.state.storeSalaryList.map(salary => {
                    return {
                        name: salary.name,
                        salaryAmount: salary.salaryAmount * 2 + " " + "$"

                    };
                });
                return afterDoubleSalary;
            }
        },

    }
</script>
<style>

</style>

运行结果如下

Vuex 基础(一)_第5张图片

以上的代码虽然对 state 属性进行了加工之后返回,但是有一定的局限性,如果有100个页面都要这样处理,是不是就要将 doubleSalary 属性写100遍?这样显然是不合理的,为了解决这个问题,可以 doubleSalary 属性写进 store.js 作为一个 getter 方法。

store.js 中增加一个getter 方法

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

Vue.use(Vuex);

export const store = new Vuex.Store({
    state: {
        storeSalaryList: [{
                name: "马云",
                salaryAmount: 1000
            },
            {
                name: "马化腾",
                salaryAmount: 900
            },
            {
                name: "李彦宏",
                salaryAmount: 800
            }
        ]
    },
    getters: {
        // 增加一个 getter 方法,用于返回加工后的 工资
        doubleSalaryGetter: (state) => {
            var afterDoubleSalary = 
                state.storeSalaryList.map(salary => {
                return {
                    name: salary.name,
                    salaryAmount: salary.salaryAmount * 2 + " " + "$"

                };
            });
            return afterDoubleSalary;
        }
    }
});

修改 salarylist1.vuesalarylist2.vue 代码,在 computed 属性中增加,doubleSalaryByGetter 属性,在这个属性中,调用 store.js 中的 getter 返回结果。

<template>
    <div id="salary-list-fisrt">
        <h2>财富榜</h2>
        <ol>
            <li v-for="salary in doubleSalaryByGetter">
                {{salary.name}}的工资是:{{salary.salaryAmount}}
            </li>
        </ol>
    </div>
</template>
<script>
    export default {
        computed: {
            salaryList() {
                return this.$store.state.storeSalaryList;
            },
            // 在这里调用 store.js 中的方法。
            doubleSalaryByGetter() {
                return this.$store.getters.doubleSalaryGetter;
            }
        },

    }
</script>
<style>
</style>

Vuex 基础(一)_第6张图片

如果我们 还有一个需求:计算工资总额,可以在 getter 新增加一个 totalSalary 方法

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

Vue.use(Vuex);

export const store = new Vuex.Store({
    state: {
        storeSalaryList: [{
                name: "马云",
                salaryAmount: 1000
            },
            {
                name: "马化腾",
                salaryAmount: 900
            },
            {
                name: "李彦宏",
                salaryAmount: 800
            }
        ]
    },
    getters: {
        doubleSalaryGetter: (state) => {
            var afterDoubleSalary = state.storeSalaryList.map(salary => {
                return {
                    name: salary.name,
                    salaryAmount: salary.salaryAmount * 2 + " " + "$"

                };
            });
            return afterDoubleSalary;
        },
        // 在这里增加一个 计算工资总额的方法

        totalSalary: (state) => {
            var sum = 0
            state.storeSalaryList.forEach(element => {
                sum += element.salaryAmount;
            });
            return sum*2;
        }
    }
});

修改 salarylist1.vue 代码,在 computed 属性中增加,totalSalaryByGetter 属性,在这个属性中,调用 store.js 中的 getter 返回结果。

<template>
    <div id="salary-list-fisrt">
        <h2>财富榜</h2>
        <ol>
            <li v-for="salary in doubleSalaryByGetter">
                {{salary.name}}的工资是:{{salary.salaryAmount}}
            </li>
        </ol>
        <span>工资总额是: {{totalSalaryByGetter}}</span>
    </div>
</template>
<script>
    export default {
        computed: {
            salaryList() {
                return this.$store.state.storeSalaryList;
            },
            doubleSalaryByGetter() {
                return this.$store.getters.doubleSalaryGetter;
            },
            // 增加获取工资总额的方法
            totalSalaryByGetter() {
                return this.$store.getters.totalSalary;
            }
        },

    }
</script>
<style>
</style>

Vuex 基础(一)_第7张图片

我们通过在当前页面调用 store.jsgetter 方法,可以返回想要的结果。但是发现,在每个页面中,都要写调用写一个方法来调用 getter 也不是很方便。

Vuex 为我们提供了一个 mapGetters 辅助函数,可以用来获取getter

<template>
    <div id="salary-list-fisrt">
        <h2>财富榜2</h2>
        <!--
        <ol>
            <li v-for="salary in doubleSalaryGetter ">
                {{salary.name}}的工资是:{{salary.salaryAmount}}
            </li>
        </ol>
        <span>工资总额是:{{totalSalary}}</span>
        -->
        <ol>
            <li v-for="salary in myDoubleSalaryGetter ">
                {{salary.name}}的工资是:{{salary.salaryAmount}}
            </li>
        </ol>
        <span>工资总额是:{{myTotalSalary}}</span>
    </div>
</template>
<script>
    import {
        mapGetters
    } from 'vuex';
    export default {
        name: "salary-list-two",

        computed: {
            salaryList() {
                return this.$store.state.storeSalaryList;
            },
            doubleSalaryByGetter() {
                return this.$store.getters.doubleSalaryGetter;
            },
            /** 
            ...mapGetters(
                // 注意这里是个数组
                [
                    'doubleSalaryGetter','totalSalary'
                ]
            )*/

            // ...mapGetters的另一种写法,通过这种方法可以给 getter 起别名
            ...mapGetters(
                // 注意这里是一个对象
                {
                    myDoubleSalaryGetter: 'doubleSalaryGetter',
                    myTotalSalary: 'totalSalary'

                })
        }
    }
</script>
<style>
</style>

运行结果如下:

Vuex 基础(一)_第8张图片
欢迎关注我的微信公众号 coding先锋
个人博客 https://www.iambruceliu.top

你可能感兴趣的:(前端)