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

·

恭喜-您已决定开始构建本机移动应用程序,并希望使用 Vue.js 作为框架。大!现在您可能想知道-“基于 Web 的原始” Vue 开发的哪些方面会延续到 NativeScript 流程中?尽管不一定是一对一的翻译,但绝对可以继承的方面之一是(通常)需要 Vuex 之类的工具。

在本文中,我将演示如何将 Vuex 添加到 NativeScript-Vue 应用程序中,但更好的是,我将对 Vuex 进行基本介绍并描述为什么可以使用它。当我刚开始学习 Vue 时,这个“我何时需要它”特别令人困惑,因此,当您要在应用程序中使用它时,我将尽我所能使其清楚(就像有些泥泞!)。准备?让我们开始吧!

# Vuex 到底是什么?

Vuex 这样定义自己:

TIP

“ Vuex 是 Vue.js 应用程序的状态管理模式+库。它用作应用程序中所有组件的集中存储,其规则确保状态只能以可预测的方式进行更改。”

起初这可能没有意义,尤其是如果您在 NativeScript-Vue 之前使用 Vue 是在较简单的应用程序中,其中 Vue 用于向简单网页添加简单的交互性和其他更新。(有时,我认为 Vue 是现代开发人员的超级 jQuery 构建。)

但是,一旦开始构建稍微复杂的应用程序或具有多个组件的应用程序(这肯定是您在 NativeScript-Vue 中会遇到的问题),您就会发现在多个组件之间保持数据同步的问题。用户记录可能是整个应用程序中使用的东西,并且通过 Vuex 可以使每个组件(也许每个页面)一致地反映有关该用户的正确信息。

是的,使用 Vuex 需要做更多的工作,但是您会发现它提供的功能在 NativeScript-Vue 中确实发光。让我们从构建一个简单的示例开始,并展示 Vuex 如何发挥作用。

在进行第一个演示之前,我想指出,我将跳过 NativeScript。每当我学到一些东西时,我都会尽量简化我的测试用例。因此,对于第一个示例,我将仅使用 CodePen。下一个示例将其正确集成到真实的 NativeScript 移动应用程序中。

# 在 Vue 建造宇宙飞船!

让我们在 Vue 中建造一艘太空船!好吧,我试图将其保持在 2000 字以下,所以代替一艘太空飞船,如何控制一个非常有限的,具有两个系统的引擎-武器和武器的太空船呢?整个船具有一定的能量。我将创建一个由各种组件组成的面板,使您可以一次增加或减少一个系统的能量。因此,例如增加一个引擎的能量必须减少武器的能量。这是操作面板的屏幕截图。

控制面板

在此屏幕快照中,有一个主要的 Vue 应用程序和两个子组件,每个控件一个。这是布局代码。

<div id="app" v-cloak>
  <h2>Current Status</h2>
  <p>
    Engines are at {{engines}} and weapons are at {{weapons}}.
  </p>
  <engine-control></engine-control>
  <weapon-control></weapon-control>
</div>

不是很复杂吧?因此,让我们创建一个商店。

const shipStore = new Vuex.Store({
  state: {
    engines: 50,
    weapons: 50
  },
  mutations: {
    increaseEngines(state) {
      if (state.engines < 100) {
        state.engines++;
        state.weapons--;
      }
    },
    decreaseEngines(state) {
      if (state.engines > 0) {
        state.engines--;
        state.weapons++;
      }
    },
    increaseWeapons(state) {
      if (state.weapons < 100) {
        state.weapons++;
        state.engines--;
      }
    },
    decreaseWeapons(state) {
      if (state.weapons > 0) {
        state.weapons--;
        state.engines++;
      }
    }
  }
});

Vuex 商店可以包含许多不同的内容,但我将从两个主要方面开始简单介绍。该state值非常类似于dataVue 应用程序的一部分。它代表您的商店跟踪的信息。就我而言,这只是引擎和武器的能量计数。我最初将这两个值都设置为 50,以表示该船总共 100 个“单位”(无关紧要)。一个更高级的示例可以存储总值,然后以编程方式确定两个系统。

Mutatations改变商店价值的方式。您的 Vue 应用程序可以直接访问状态值,但是通常您希望避免这种情况。这是一个很好的理由。我们不希望系统的总能量低于 0 或高于 100。突变可以解决这一问题。默认情况下,突变会传递当前状态值,但也可以传递其他参数。变异必须是同步的,但存储也通过来支持异步操作actions。再说一次-让我们保持简单。

回顾一下-我定义了商店的初始状态,以及外部应用程序处理该数据的方式。

const app = new Vue({
  el: '#app',
  store: shipStore,
  computed: {
    engines() {
      return this.$store.state.engines;
    },
    weapons() {
      return this.$store.state.weapons;
    }
  }
});

因此,这里有两件事要注意。商店将传递到 Vue 构造函数中。我不需要这样做,但是这样做可以通过来自动将其提供给子组件this.$store。现在请注意中的两种方法computed。两者都访问this.$store.state以返回每个组件的值。如果您还记得布局,这部分是这样工作的:

<h2>Current Status</h2>
<p>
    Engines are at {{engines}} and weapons are at {{weapons}}.
</p>

如果您不想直接访问状态,或者想要computed在商店内部进行类似操作,那么您很幸运。Vuex 商店支持与getters一样工作的属性computed。现在让我们看一下其中的一个组件(只是一个组件,因为它们实际上是相同的)。这是引擎组件。

Vue.component('engine-control', {
  template: `
  <form>
    <fieldset>
    <legend>Engine Controls</legend>
    <button @click.prevent="increaseEngine">Increase Engine</button>
    <button @click.prevent="decreaseEngine">Decrease Engine</button>
    </fieldset>
  </form>
`,
  methods: {
    increaseEngine() {
      this.$store.commit('increaseEngines');
    },
    decreaseEngine() {
      this.$store.commit('decreaseEngines');
    }
  }
});

这里的布局不太重要,但请看一下两种方法。在这两种情况下,我们都使用来访问商店突变this.$store.commit。请注意,我只是传递突变的名称。我也可以传递其他数据。例如,我可能要添加“全能量!” 将所有能量转移到系统的选项。

您可以在此 CodePen 上查看所有代码并运行完整的示例:

见笔 飞船商店由雷蒙德·卡姆登(@cfjedimaster)上 CodePen。
显然,这是一个琐碎的示例,并且存储可能会适得其反,但是随着应用程序变得越来越复杂(因为它很少会以其他方式出现),存储成为与船舶有关的数据“真相”的中心位置。我的 Vue 应用程序可以将更多的精力放在 UI 问题上(单击按钮可以这样做),而商店则集中在集中应用程序的“逻辑”上。

# NativeScript,Vue 和 Vuex

现在让我们看一下我们刚刚构建的应用程序的 NativeScript 版本。对于此演示,我将使用 NativeScript Playground,因此您可以使用 NativeScript Playground Preview 应用程序自己运行此示例。让我们看一下完成的结果,然后我们可以深入研究代码。这是初始页面。我正在使用TabView创建三个视图。

nativescript vue 应用程序-TabView

初始视图具有两个系统的基本状态。单击单个选项卡仅提供该系统的报告,并控制修改电源。

nativescript vue 应用程序-修改电源

那么我是如何构建的呢?我首先使用 Vue 模板创建了一个新的 Playground 应用。为了增加对 Vuex 的支持,我在资源管理器中使用了+号,并选择了 NPM 选项。

本机游乐场 vuex

然后,我在搜索字段中输入“ vuex”并选择了最新版本。

nativescript 游乐场 vuex 包

如果使用 CLI,则要添加对 Vuex 的支持时,将在创建项目时提示您。如果您决定以后增加对 Vuex 的支持,则需要npm install vuex --save在终端中使用。文档将对此进行更深入的讨论。如果您熟悉 Vue CLI,则知道它也支持添加插件,但是您不想在 NativeScript Vue 应用程序中使用该选项。现在让我们看一下代码。我做的第一件事是创建我的商店。与 CodePen 示例不同,该示例位于其自己的文件中。我创建了一个文件夹store,然后单击shipStore.js。这是内容。

import Vue from 'nativescript-vue';
import Vuex from '../vuex';
Vue.use(Vuex);
const shipStore = new Vuex.Store({
  state: {
    engines: 50,
    weapons: 50
  },
  mutations: {
    increaseEngines(state) {
      if (state.engines < 100) {
        state.engines++;
        state.weapons--;
      }
    },
    decreaseEngines(state) {
      if (state.engines > 0) {
        state.engines--;
        state.weapons++;
      }
    },
    increaseWeapons(state) {
      if (state.weapons < 100) {
        state.weapons++;
        state.engines--;
      }
    },
    decreaseWeapons(state) {
      if (state.weapons > 0) {
        state.weapons--;
        state.engines++;
      }
    }
  }
});
module.exports = shipStore;

在大多数情况下,这并没有太大区别,但是请注意,我已经导入了 Vuex 并指定 Vue 将使用它。坦白讲,在我找到这个很好的例子之前,我根本不愿这么做。

我的下一个修改是对我的核心app.js文件进行包括和注册商店的操作:

import Vue from 'nativescript-vue';
import Vuex from './vuex';
Vue.use(Vuex);
import SpaceShip from './components/SpaceShip';
import shipStore from './store/shipstore';
// Uncommment the following to see NativeScript-Vue output logs
// Vue.config.silent = false;
new Vue({
  render: h => h('frame', [h(SpaceShip)]),
  store: shipStore
}).$start();

同样,这与先前的示例非常相似。通过将商店传递给我的 Vue 组件,我的根应用程序和所有子组件将拥有它。现在让我们看一下该根组件。

<template>
    <Page class="page">
        <ActionBar title="Spaceship Info" class="action-bar" />
        <TabView>
            <TabViewItem title="Main Report">
                <Label :text="status" />
            </TabViewItem>
            <TabViewItem title="Engines">
                <Engines />
            </TabViewItem>
            <TabViewItem title="Weapons">
                <Weapons />
            </TabViewItem>
        </TabView>
    </Page>
</template>
<script>
    import Engines from "./Engines";
    import Weapons from "./Weapons";
    export default {
        data() {
            return {};
        },
        components: {
            Engines, Weapons
        },
        computed: {
            status() {
                return `Engines are at ${this.engines} and weapons are at ${this.weapons}.`;
            },
            engines() {
                return this.$store.state.engines;
            },
            weapons() {
                return this.$store.state.weapons;
            }
        }
    };
</script>

在此示例中,主要区别在于用于生成我的布局的标签。div我使用的是 NativeScript 布局和 UI 组件,而不是其他 HTML 标签。但是存储值的使用是完全相同的。现在让我们来看一下 Engine 组件。

<template>
    <StackLayout>
        <Label :text="status" />
        <Button text="Increase Engines" @tap="increaseEngine" />
        <Button text="Decrease Engines" @tap="decreaseEngine" />
    </StackLayout>
</template>
<script>
    export default {
        data() {
            return {};
        },
        computed: {
            status() {
                return `Engines currently at ${this.$store.state.engines}.`;
            }
        },
        methods: {
            increaseEngine() {
                this.$store.commit("increaseEngines");
            },
            decreaseEngine() {
                this.$store.commit("decreaseEngines");
            }
        }
    };
</script>

和以前一样,这里唯一真正的变化是布局。我对商店的使用完全相同,这太棒了。作为熟悉 Vue 并熟悉 NativeScript 的人,这应该会让您感到更加安全。我确实向该组件添加了一些文本,因为 NativeScript 版本使用选项卡来一次显示一个视图。

TIP

您可以在此处找到完整的源代码。

但是等等-还有更多(是的,很多…)
这仅仅是使用 Vuex 的开始,但希望您会开始发现采用这种方法所获得的一些优势。在下一篇文章中,我将演示 Vuex 的更多功能,并构建更接近于实际应用程序的东西。