# 在 NativeScript-Vue 应用程序中使用 Vuex-现在使用 Cats!

·

在上一篇文章中,我讨论了如何在 NativeScript 应用程序中使用 Vuex。Vuex 是一个非常复杂的主题,在上一个演示中我只涵盖了其中的一小部分,因此本文将添加更多细节,并纠正不包括任何猫的明显错误。对不起-老实说,我不知道自己在想什么。

# 应用程式

在进入代码之前,让我们快速看一下应用程序。这是一个相当简单的单页应用程序,它模拟照顾猫。对于那些年龄大到足以记住的人,可以将其视为手机的 Tamagotchi。打开应用程序后,您的猫咪会很高兴。

快乐的猫

当然,随着时间的流逝,您的猫通常对生活的满意度会降低。

中性猫

最终,当您-再次-失望时,它陷入了绝望的深渊。

疯狂的猫

如您所见,该应用程序分为三个部分。最上面是一张图片,代表猫的一般状态(以及我的艺术天赋的缩影),以及更直接的数字状态(主要用于测试,在此类“真实”游戏中将被删除),以及一个可以让您喂猫的按钮。

当您喂猫时,它会慢慢变得更快乐,但同时它也会变得更加饥饿,因此变得不那么快乐。拥有猫的任何人都会立即将这种矛盾理解为(猫)生活的简单事实。因此,让我们看一下它是如何构建的。

# 定义 Cat 商店

我的应用程序分为两部分-用于显示猫并与之交互的主要组件,以及表示虚拟猫数据并允许进行交互的 Vuex 存储。这个特定的 Vuex 演示将展示以前的 Getters 和 Actions 缺少的两个功能。

吸气剂的工作原理与计算后的 Vuex 属性类似。它们允许您定义需要封装某种逻辑的数据。让我们从查看猫的数据开始:

state: {
    hungriness: 0
},

是的,就是这样。虚拟猫的整体由一个属性定义-它有多饿。但是,我们希望向应用程序展示猫的快乐程度,并且您可以想象,这是猫的饥饿程度的函数。吸气剂在一个getters块中定义:

getters: {
    happiness(state) {
        // the more hungry, the less happy
        return 100 - state.hungriness;
    }
},

吸气剂传递到商店的状态,并且在状态更改时将自动更新。在应用程序方面,可通过来解决store.getters.happiness。让我们看一个例子-图像和文本状态消息。

<Image :src="catImage" />
<Label textWrap="true" :text="status" />

这些都与计算属性有关:

computed: {
    catImage() {
        // get the status, which is a % from 0 to 100
        let status = this.$store.getters.happiness;
        if (status > 90) {
            return "~/images/cat_happy.png";
        } else if (status > 60) {
            return "~/images/cat_medium.png";
        } else return "~/images/cat_sad.png";
    },
    status() {
        return "status is " + this.$store.getters.happiness;
    }
}

尽管使用的吸气剂非常琐碎,但这里的好处是,随着 Cat 变得越来越复杂,它的幸福感背后的逻辑也可以更新。这是 Encapsulation 101,本身并不是什么新鲜的东西,但是再次可以看到 Vuex 如何真正使它易于使用。现在让我们看看 Vuex 的另一个功能-动作。

在上一篇文章中,我演示了 Mutations 是组件如何对商店进行更改的接口。突变必须是同步的,然后应该提出一个问题-如何进行异步更改。这是动作的发挥。

动作不会直接更改存储中的状态-而是也会使用突变。为了支持此操作,将一个context对象传递给他们以支持进行更改。动作也可以采用任意参数。我们的猫有两个定义的动作。

第一个是“心跳”动作,它仅模拟时间的流逝。这就是让猫随着时间的推移变得饥饿(和不快乐)的原因。

actions: {
    // more stuff here...
    heartbeat(context) {
        console.log('heartbeat started');
        setInterval(() => {
            console.log('heartbeat running');
            context.commit('hunger');
        }, HB_SPEED * 1000);
    }
}

setInterval用来初始化定时重复的猫的生活。HB_SPEED 是一个常量变量,定义了它应该多久运行一次,我在测试时进行了调整,以寻找一种“好感觉”来使猫感到多么烦恼。注意这一行:

context.commit('hunger');

这就是操作通过突变请求更改的方式。在这种情况下,它只会使猫更加饥饿:

mutations: {
    // more stuff here...
    hunger(state) {
        state.hungriness++;
        if (state.hungriness > 100) state.hungriness = 100;
    }
}

使用以下mounted事件从主 Vue 组件触发心跳调用:

mounted() {
    this.$store.dispatch("heartbeat");
},

动作的名称将作为第一个参数传递给商店,以后会随便添加任何其他参数。现在让我们看看另一个动作-喂猫。

feed(context) {
    console.log('feed started');
    setTimeout(() => {
        context.commit('feed');
    }, FEED_SPEED * 1000);
},

feed操作仅setTimeout在对名为 feed 的变异进行提交调用之前包装了一个。为什么setTimeout呢?只是为了模拟您喂猫时猫不会立即满足的情况。如果这看起来很武断和刻薄,那您可能是猫的新手。该feed突变也有点乱:

feed(state) {
    let satisfaction = getRandomInt(1, 10);
    console.log('statisfaction was set to ' + satisfaction);
    state.hungriness -= satisfaction;
    if (state.hungriness < 0) state.hungriness = 0;
},

在这种情况下,我认为即使您每次给猫喂同样的东西,也不一定意味着猫每次都回应相同。还要注意一点逻辑,以确保饥饿感永远不会低于 0。

基本上就是这样,让我们 ​​ 花点时间看一下整个商店:

import Vue from 'nativescript-vue';
import Vuex from '../vuex';
Vue.use(Vuex);
//number of seconds for updating
const HB_SPEED = 2;
// when you feed the cat, it takes a while for it to be satisfied from it. cuz cats
const FEED_SPEED = 8;
const catStore = new Vuex.Store({
  state: {
    hungriness: 0
  },
  getters: {
    happiness(state) {
      // the more hungry, the less happy
      return 100 - state.hungriness;
    }
  },
  mutations: {
    feed(state) {
      let satisfaction = getRandomInt(1, 10);
      console.log('statisfaction was set to ' + satisfaction);
      state.hungriness -= satisfaction;
      if (state.hungriness < 0) state.hungriness = 0;
    },
    hunger(state) {
      state.hungriness++;
      if (state.hungriness > 100) state.hungriness = 100;
    }
  },
  actions: {
    feed(context) {
      console.log('feed started');
      setTimeout(() => {
        context.commit('feed');
      }, FEED_SPEED * 1000);
    },
    heartbeat(context) {
      console.log('heartbeat started');
      setInterval(() => {
        console.log('heartbeat running');
        context.commit('hunger');
      }, HB_SPEED * 1000);
    }
  }
});
// Credit: https://stackoverflow.com/a/1527820/52160
function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
module.exports = catStore;

从本质上讲,商店代表了猫的整体,并定义了集成,这两种事物都会改变状态,并会读取状态,以便任何组件都可以使用它。我们的应用程序只有一个主要组件,但可以轻松扩展到更多组件。这是主要组成部分:

<template>
    <Page class="page">
        <ActionBar title="Cat Simulator 95 XP Ultimate Edition" class="action-bar" />
        <ScrollView>
            <StackLayout class="home-panel">
                <Image :src="catImage" />
                <Label textWrap="true" :text="status" />
                <Button text="Feed the Cat" @tap="feedCat" />
            </StackLayout>
        </ScrollView>
    </Page>
</template>
<script>
    export default {
        data() {
            return {};
        },
        mounted() {
            this.$store.dispatch("heartbeat");
        },
        methods: {
            feedCat() {
                this.$store.dispatch("feed");
            }
        },
        computed: {
            catImage() {
                // get the status, which is a % from 0 to 100
                let status = this.$store.getters.happiness;
                if (status > 90) {
                    return "~/images/cat_happy.png";
                } else if (status > 60) {
                    return "~/images/cat_medium.png";
                } else return "~/images/cat_sad.png";
            },
            status() {
                return "status is " + this.$store.getters.happiness;
            }
        }
    };
</script>
<style scoped>
    .home-panel {
        vertical-align: center;
        font-size: 20;
        margin: 15;
    }
</style>

如果您想亲自尝试一下,则整个代码库都位于 NativeScript Playground 上。尽力使猫开心。您会失败,但还是要开心!