我最近写了一个关于在使用 Vue.js JavaScript 框架的 NativeScript 应用程序中使用 Vuex 的教程。在这个名为《使用 Vuex 在 Vue.js NativeScript 应用程序中进行键值本地存储》的教程中,我们看到了几个使用 NativeScript 的应用程序设置模块进行键值存储的示例。
键值存储在某些场景下非常有用,但通常你会发现自己需要一些可以像 SQLite 一样进行查询的东西。
我们将利用 NativeScript + Vue.js 和 Vuex 进行状态管理,将我们之前的教程提升到一个新的水平,在使用 Android 和 iOS 构建的应用程序中使用原生 SQLite。
如果你还没有看过我的 上一篇文章,这不是成功阅读本文的必要条件,但在学习 Vuex 和 NativeScript 中的存储时,它还是很有价值的。
为了使事情简单易懂,我们将创建一个使用 NativeScript 和 Vue.js 的新项目。为此,请从命令行执行以下操作
vue init nativescript-vue/vue-cli-template vuex-project
cd vuex-project
npm install
npm run watch:ios
上述命令将使用 NativeScript 模板初始化一个新的 Vue.js 项目,安装立即的项目依赖项,然后在 iOS 上模拟它。但是,在使用 Vue CLI 创建的过程中,会问你一些问题。启用 Vuex 非常重要,但其他所有内容都可以保留为默认值。
创建 vuex-project 项目后,我们还没有完全完成。我们需要安装一个原生插件,它将为 Android 和 iOS 提供 SQLite 支持。
从命令行执行以下操作
npm install nativescript-sqlite --save
上述命令将为 NativeScript 安装 SQLite 插件。
此时,我们可以开始向我们非常简单的应用程序添加逻辑。
如果你以前从未使用过 Vuex,它是一种从单个位置管理应用程序状态的方法。在使用生成器创建项目时,我觉得它在 Vuex 方面过于复杂了。因此,在添加我们自己的 Vuex 逻辑之前,我们将对项目进行一些修改。
在项目的 src/store/index.js 文件中,包含以下 JavaScript 代码
import Vue from 'nativescript-vue';
import Vuex from 'vuex';
const Sqlite = require("nativescript-sqlite");
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
database: null,
data: []
},
mutations: { },
actions: { }
});
Vue.prototype.$store = store;
module.exports = store;
那么发生了什么变化?我没有在 src/store/index.js 文件中引用模块,而是直接在文件中包含了模块的内容。
现在让我们弄清楚我们想用 Vuex 实现什么。SQLite 插件是异步的,因此我们希望在我们的状态中维护一个打开的实例。我们还希望随时保持数据库数据加载并准备就绪,这可以在状态的 data
属性中发生。这些命名约定不是特定的,只要它们存在于 state
属性中即可。
任何时候我们想要更改状态变量,都需要在 mutation 中进行。但是,mutation 必须是同步的,因此我们也必须使用 action,action 可以是异步的。
那么我们的 mutation 可能是什么样子?请看下面的代码
mutations: {
init(state, data) {
state.database = data.database;
},
load(state, data) {
state.data = [];
for(var i = 0; i < data.data.length; i++) {
state.data.push({
firstname: data.data[i][0],
lastname: data.data[i][1]
});
}
},
save(state, data) {
state.data.push({
firstname: data.data.firstname,
lastname: data.data.lastname
});
},
}
我们在上面的代码中定义了三个不同的 mutation。我们有一个初始化操作,它将存储我们打开的数据库的状态。当我们希望加载数据库数据时,我们可以遍历查询结果并将其存储为对象数组。直接使用 SQLite 数据的原始形式并不那么令人愉快,但使用 JSON 非常容易。最后,我们有一种将数据保存到状态中的方法。
请记住,mutation 仅用于更改 state
变量。与数据库的实际交互将发生在 action 中
actions: {
init(context) {
(new Sqlite("my.db")).then(db => {
db.execSQL("CREATE TABLE IF NOT EXISTS people (id INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT)").then(id => {
context.commit("init", { database: db });
}, error => {
console.log("CREATE TABLE ERROR", error);
});
}, error => {
console.log("OPEN DB ERROR", error);
});
},
insert(context, data) {
context.state.database.execSQL("INSERT INTO people (firstname, lastname) VALUES (?, ?)", [data.firstname, data.lastname]).then(id => {
context.commit("save", { data: data });
}, error => {
console.log("INSERT ERROR", error);
});
},
query(context) {
context.state.database.all("SELECT firstname, lastname FROM people", []).then(result => {
context.commit("load", { data: result });
}, error => {
console.log("SELECT ERROR", error);
});
}
}
在 action 部分,我们有三个不同的 action,每个 action 最终都会调用我们的 mutation 之一。init
action 将打开一个数据库并执行创建表的查询。创建表后,打开的数据库将通过 commit
方法传递给 mutation。插入信息时,数据将传递给 insert
action 并执行查询。context.state.database
变量是我们 state
变量部分中打开的数据库。传递的数据用作我们查询的参数,当查询成功完成后,它将被提交到我们的 mutation,数据将保存在状态中。类似地,我们可以查询数据库中的数据。查询结果将被提交到我们的 mutation,以便它可以从组件中访问。
很多繁重的工作实际上是 SQL 语句。请记住,actions
用于异步代码,而 mutations
用于同步代码。
还有一件事必须做。
我们希望确保在应用程序打开时初始化我们的数据库。为此,请将以下行添加到 src/store/index.js 文件的底部
store.dispatch("init");
请注意,我们使用 dispatch
而不是 commit
来调用我们的 action。使用 Vuex 之后,我们可以在任何组件中使用它。
由于这是一个简单的应用程序,我们有两种简单的方法来创建我们的组件。我们可以继续使用项目的 src/components/Counter.vue 文件,或者我们可以将其重命名为更有意义的名称。由于我们存储的是人员数据,因此我已经将其重命名为 src/components/Person.vue。如果你决定重命名文件,请确保在 src/main.js 文件中更新它。
打开 src/components/Person.vue 文件并包含以下内容
<template></template>
<script>
export default {
data() {
return {
input: {
firstname: "",
lastname: ""
}
}
},
methods: {
save() {
this.$store.dispatch("insert", this.input);
},
load() {
this.$store.dispatch("query");
},
clear() {
this.input.firstname = "";
this.input.lastname = "";
}
}
};
</script>
我故意清空了 <template>
块,以便我们可以专注于 <script>
块。在 <script>
块中,我们初始化了一些变量,这些变量将绑定到我们的 HTML 表单。这些方法将绑定到一组按钮。当我们调用 save
方法时,我们将表单中的数据发送到 Vuex action 以存储到数据库中。同样,当我们调用 load
方法时,我们将调用获取数据的相应 action。最后,clear
函数将通过清除变量来清空我们的表单。这里没有什么太复杂的东西,因为 Vuex 为我们的 SQLite 数据库做了所有繁重的工作。
现在让我们看一下 <template>
块
<template>
<Page class="page">
<ActionBar class="action-bar" title="Person"></ActionBar>
<GridLayout rows="auto, *" columns="*">
<StackLayout class="form" row="0" col="0">
<StackLayout class="input-field">
<Label text="First Name" class="label font-weight-bold m-b-5" />
<TextField class="input" v-model="input.firstname" />
<StackLayout class="hr-light"></StackLayout>
</StackLayout>
<StackLayout class="input-field">
<Label text="Last Name" class="label font-weight-bold m-b-5" />
<TextField class="input" v-model="input.lastname" />
<StackLayout class="hr-light"></StackLayout>
</StackLayout>
<GridLayout rows="auto, auto" columns="*, *">
<Button text="Save" @tap="save" class="btn btn-primary" row="0" col="0" />
<Button text="Load" @tap="load" class="btn btn-primary" row="0" col="1" />
<Button text="Clear" @tap="clear" class="btn btn-primary" row="1" col="0" colSpan="2" />
</GridLayout>
</StackLayout>
<ListView for="person in $store.state.data" class="list-group" row="1" col="0">
<v-template>
<StackLayout class="list-group-item">
<Label v-bind:text="person.firstname + ' ' + person.lastname" />
</StackLayout>
</v-template>
</ListView>
</GridLayout>
</Page>
</template>
我们页面的模板将是一个网格。网格的第一行将是我们的表单,第二行将是数据库中数据的列表。让我们先看看上面部分
<StackLayout class="form" row="0" col="0">
<StackLayout class="input-field">
<Label text="First Name" class="label font-weight-bold m-b-5" />
<TextField class="input" v-model="input.firstname" />
<StackLayout class="hr-light"></StackLayout>
</StackLayout>
<StackLayout class="input-field">
<Label text="Last Name" class="label font-weight-bold m-b-5" />
<TextField class="input" v-model="input.lastname" />
<StackLayout class="hr-light"></StackLayout>
</StackLayout>
<GridLayout rows="auto, auto" columns="*, *">
<Button text="Save" @tap="save" class="btn btn-primary" row="0" col="0" />
<Button text="Load" @tap="load" class="btn btn-primary" row="0" col="1" />
<Button text="Clear" @tap="clear" class="btn btn-primary" row="1" col="0" colSpan="2" />
</GridLayout>
</StackLayout>
本质上,我们有一些输入字段和一些按钮。输入字段通过 v-model
属性绑定到我们初始化的变量,按钮通过 @tap
属性绑定到我们的方法。我们这里的大部分内容都是标准标记,没有真正的逻辑。
现在让我们看看后半部分
<ListView for="person in $store.state.data" class="list-group" row="1" col="0">
<v-template>
<StackLayout class="list-group-item">
<Label v-bind:text="person.firstname + ' ' + person.lastname" />
</StackLayout>
</v-template>
</ListView>
我们有一个简单的列表,它从存储中的数据填充。请记住,我们不仅将所有数据存储在数据库中,还将其作为 JSON 存储。
你刚刚了解了如何在 使用 NativeScript 的 Vue.js 应用程序中使用 SQLite。在使用 SQLite 时使用 Vuex 不是必需的,但它是处理数据时的建议方法。我们的示例很简单,因为它只使用了一个表和一些简单的查询,但只需付出很少的额外努力,它就可以变得更加复杂。如果 Vue.js 不是你的首选,我还写了一个名为《在 NativeScript Angular 移动应用程序中使用 SQLite》的教程,它关注的是相同概念,但使用的是 Angular。
如果你想使用键值存储而不是 SQLite,请务必查看我的 上一篇文章。