# NativeScript 应用程序中的客户端存储

·

在研究我想创建(并撰写!)的新 NativeScript-Vue 演示的大纲时,我发现了我尚未探索的 NativeScript 开发的另一个方面-客户端存储。我非常了解在 Web 应用程序中将数据存储在客户端上的各种方式,但是我还没有研究 NativeScript 支持什么。在本文中,我将讨论您可以做什么,显示一些示例,并对它们与标准 Web API 的关系进行比较。是的,将会有一个猫演示。

# 网络存储

尽管一段时间以来,浏览器一直在不断变化,但浏览器已经能够在客户端上存储数据了一段时间。从广义上讲,并且忽略了我们不会谈论的过时技术,网络存储归结为两种核心技术:

  • Web 存储(通常称为本地存储)是一种用于存储少量数据的简单“键/值”系统。当与大量数据一起使用时,它存在性能问题,但是我认为这是对技术的不当使用,而并不是规范本身的失败。它非常易于使用,非常适合诸如保存表格,购物车和用户首选项之类的事情。
  • IndexedDB 的是不是一个简单的 API,但对于大数据进行搜索和过滤的需求更加合适。大致上,您可以将其与 MongoDB 和其他 NoSQL 类型的解决方案进行比较。

两者都有令人难以置信的良好支持。据 CanIUse 称,对网络存储的支持在所有浏览器中的支持率接近 97%,而 IndexedDB 的支持率接近 96%。显然,这些技术都不能替代“真正的”企业级存储服务器,但是它们都通过允许浏览器在页面视图上保留和持久保存数据,在存储数据和提高性能方面都非常有用。

# 用 NativeScript 存储

因此,我已经清楚地表明,我是从 Web 开发人员的角度进入 NativeScript 的,当我计划下一个演示应用程序时,我什至没有考虑存储方面的问题,因为我只是假设这是基于我的观点而定的。网站开发经验。当我意识到必须真正弄清楚我要用什么时,我迅速停下了脚步。幸运的是,我发现了多种存储数据的有用方法,这些方法实际上与我现有的知识非常相似。

# 使用 ApplicationSettings 保持简单

NativeScript 应用程序最简单的存储选项是 ApplicationSettings。如果您熟悉本地存储,则可以轻松使用此功能。与本地存储类似,ApplicationSettings 用于通过键名存储简单值。该 API 支持:

  • 设定值(duh)
  • 获取价值(当然)
  • 检查值是否存在
  • 删除值
  • 删除所有内容
  • 如果需要较低级别的访问,还可以提供对 ApplicationSettings 包装的基础本机 API 的快速访问

ApplicationSettings 唯一真正的“复杂性”是存在用于处理布尔值,字符串和数字值的单独的 API。让我们来看一个实际的令人难以置信的简单示例。

以下应用程序将仅跟踪其打开次数。琐碎,但实际上可能是您想要跟踪的一种很酷的指标。这是初始模板。

<template>
    <Page class="page">
        <ActionBar title="Home" class="action-bar" />
        <ScrollView>
            <StackLayout class="home-panel">
                <!--Add your page content here-->
                <Label textWrap="true" :text="numberOfVisits" />
            </StackLayout>
        </ScrollView>
    </Page>
</template>
<script>
export default {
    data () {
        return {
            hits:0
        };
    },
    computed:{
        numberOfVisits() {
            return "You've been here " + this.hits + " times.";
        }
    }
}
</script>

我的观点只是Label绑定到我用来创建描述性消息的计算变量的元素。它 hits 现在利用的只是 0。现在让我们添加 ApplicationSettings 支持。首先,我们导入库:

const appSettings = require('tns-core-modules/application-settings');

接下来,我们可以在created安装组件时添加方法来执行代码:

created() {
    this.hits = appSettings.getNumber("hits");
    if(!this.hits) this.hits = 0;
    appSettings.setNumber("hits", ++this.hits);
}

我们首先从中加载值appSettings。注意事项getNumber。如我所说,ApplicationSettings支持加载布尔值,字符串和数字。所有的 API 都是一样的,只是方法名称在变化。因此,如果您想获取布尔值,则可以使用getBoolean并且显然getString用于字符串。

如果未定义该值,则将其设置为 0,这可能看起来很奇怪,因为我已在代码 data 块中将其默认设置为 0 ,但否则会有所undefined不同。最后,在将值递增一之后,我将其存储回去。

对此进行几次测试,每次保存代码并重新运行项目时,您都会看到数字递增。如果您使用的是 NativeScript Playground,它将是自动的。我们可以使代码更简单一些。

与本地存储不同,它 ApplicationSettings支持为尚不存在的值提供默认值。只需提供默认值作为第二个参数即可完成此操作:

this.hits = appSettings.getNumber('hits', 0);
appSettings.setNumber('hits', ++this.hits);

TIP

您可以在此处查看和创建此简单项目的副本。

让我们考虑另一个更真实的例子。想象一个应用程序,它允许用户自定义应用程序的配色方案。一种简单的方法是更改ActionBar顶部的颜色。为此,我们可以先将颜色绑定到一个值:

<ActionBar title="Home" class="action-bar" :color="color" />

接下来,我们可以添加一个简单的字段,以允许用户编辑该值:

<Label text="Enter RGB Color" />
<TextField v-model="color" hint="Try #00ff00" />

最后,这是处理读取当前值并在更改后对其进行更新的代码。

export default {
  data() {
    return {
      color: '#ff0000'
    };
  },
  created() {
    this.color = appSettings.getString('color', '#ff0000');
  },
  watch: {
    color(val) {
      appSettings.setString('color', val);
    }
  }
};

现在,在加载应用程序后,将从本地设置中读取颜色,并且用户可以随意更改颜色。

TIP

你可以在这里玩这个项目。

要了解有关 ApplicationSettings 的更多信息,请务必阅读文档并记住,像所有客户端数据一样,绝对不应将其用于敏感数据!

在继续之前,一定要知道有一个添加了 LocalStorage 和 SessionStorage API 的 NativeScript 插件:nativescript-localstorage。如果您只是想使用已经熟悉的 API,那么此插件可能会很方便。另一个原因是试图合并另一个需要 API 的库。

# 使用 CouchDB 使其变得复杂

好的,“复杂”不一定是最好的术语,但是对于更复杂的存储需求,Osei Fortune 的 CouchDB 插件是一个很好的解决方案。这个插件使存储各种临时数据变得容易,同时还提供了检索,更新,删除甚至搜索和排序数据的功能。不仅如此,您还可以在 NativeScript 应用程序和远程 CouchDB 服务器之间建立同步。

首先,您需要通过将插件添加到您的应用程序中tns plugin add nativescript-couchbase-plugin。该插件无法在 Playground 上使用,因此您需要在本地进行测试。

接下来,导入代码并创建一个数据库对象:

import { Couchbase } from 'nativescript-couchbase-plugin';
const database = new Couchbase('my-database');

在上面的代码中,my-database是 CouchDB 数据库的名称,并且可以是对您的应用程序有意义的任何名称。让我们看一个简单的示例,该示例简单地处理随机但持久的数据。

应用程序的屏幕截图

在上面的屏幕截图中,您可以看到猫的列表。列表下方是添加新猫的按钮。在一个更好的应用程序中,我将使用一种表格让您输入有关猫的详细信息,但为了简单起见,我使用了一些随机性来命名(和年龄)猫。现在让我们看一下代码。

<template>
    <Page class="page">
        <ActionBar class="action-bar">
            <Label class="action-bar-title" text="Home"></Label>
        </ActionBar>
        <StackLayout>
            <ListView for="cat in cats">
                <v-template>
                    <Label :text="cat.name"></Label>
                </v-template>
            </ListView>
            <Button @tap="addCat" text="Add Cat"></Button>
        </StackLayout>
    </Page>
</template>
<script>
import { Couchbase } from 'nativescript-couchbase-plugin';
const database = new Couchbase('cat-db');
// function to make some mock data
function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomName() {
    var initialParts = ["Fluffy","Scruffy","King","Queen","Emperor","Lord","Hairy","Smelly","Most Exalted Knight","Crazy","Silly","Dumb","Brave","Sir","Fatty"];
    var lastParts = ["Sam","Smoe","Elvira","Jacob","Lynn","Fufflepants the III","Squarehead","Redshirt","Titan","Kitten Zombie","Dumpster Fire","Butterfly Wings","Unicorn Rider"];
    return initialParts[getRandomInt(0, initialParts.length - 1)] + ' ' + lastParts[getRandomInt(0, lastParts.length - 1)];
}
function getCats() {
    return database.query({
        select: [], // Leave empty to query for all
        order: [{ property: 'name', direction: 'asc' }]
    });
}
export default {
    data() {
        return {
            cats:[]
        }
    },
    created() {
        this.cats = getCats();
    },
    methods: {
        addCat() {
            let documentId = database.createDocument({
                "name": randomName(),
                "age": getRandomInt(1,10)
            });
            this.cats = getCats();
        }
    }
};
</script>

UI 处理遍历显示名称的 cat 猫数据。我的猫有名字和年龄,但显然您不必显示所有数据。底部的按钮负责执行我的逻辑以添加一只新猫。

在应用程序逻辑内部,您可以看到我在哪里打开 CouchDB 数据库。我有一个实用程序函数,getCats它利用了query数据库对象的方法。我在这里没有进行任何过滤,只是进行排序,但是您可以按照自己的意愿对数据进行“切片和切块”。

最后,addCat利用createDocumentAPI添加 cat。名称和年龄都是随机值,但是正如我上面所说,可以基于表单字段。

基本上就是这样,但是一定要查看插件文档以获取更多示例,包括我上面提到的同步以及如何使用事务。

# 其他选择?

除了上面列出的内容外,还有其他选择。一种可能是使用 SQLite,如果您更加熟悉基于 SQL 的数据库,则该选项可能比 CouchDB 更可取。

您也可以使用设备上的文件系统。我可能会避免将其用于“抽象数据”,而是将其用于存储图片和音频文件。

和往常一样,我很想看看你做了什么。在下面为我提供您的示例以及对您有用(或无效)的评论!