使用Vue構建Ionic混合APP系列教程(四):資料儲存
大多數應用程式基本都需要儲存一些在應用重新載入時需要的資料。我們經常使用使用者裝置上的本地儲存來實現。當使用Ionic/Angular的時候,我們可以簡單的使用Ionic內建的Storage API,並不需要知道背後的原理——Ionic會自動地選擇最合適的儲存方式。
再說一次,在Vue裡我們沒有現成的東西可以用,是安裝一個庫還是自己搭建解決方案全決定於我們自己。
在這篇教程,我們將擴充套件上篇教程裡的Reddit應用,允許我們可以切換多個不同的subreddits方法。我們會儲存使用者的選擇,當重新進入應用時會使用儲存的資料。
開始
本教程緊跟前面的教程。如果你想一步步走, 你應該先完成之前的教程。不過, 您可以輕鬆地將本教程中的概念應用於任何應用程式, 因此如果您不想完成前面的教程也無大礙。
localForage 和 Ionic 儲存模組
進入程式碼之前我們先討論一點理論知識。我們會在ionic-angular庫的Ionic Storage Module程式碼的基礎上進行。具體點說,我們會參考 the Storage class的程式碼。
Ionic使用 localForage來和儲存後臺互動。通過使用 localForage我們可以有一些外部的API進行互動,不用管具體使用了哪個技術:Native SQLite storage, IndexedDB, WebSQL或者是簡單的瀏覽器的local storage。
Ionic的儲存模組封裝了localForage ,而且根據可用的選擇,去自動的設定合適的方法。這意味著如果你使用Cordova
我們想在Ionic/Vue應用裡模擬這種行為,所以來看看Ionic是怎麼做的。我們會專注於 storage.ts裡最關鍵的部分:
import LocalForage from 'localforage';
import CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';
localForage 庫必須安裝和匯入,如果我們想支援使用
constructor(config: StorageConfig) {
this._dbPromise = new Promise((resolve, reject) => {
let db: LocalForage;
const defaultConfig = getDefaultConfig();
const actualConfig = Object.assign(defaultConfig, config || {});
LocalForage.defineDriver(CordovaSQLiteDriver).then(() => {
db = LocalForage.createInstance(actualConfig);
})
.then(() => db.setDriver(this._getDriverOrder(actualConfig.driverOrder)))
.then(() => {
this._driver = db.driver();
resolve(db);
})
.catch(reason => reject(reason));
});
}
在建構函式constructor 裡建立了一個例項化localForage的Promise。我們先要定義CordovaSQLiteDriver,一旦完成我們可以用構造物件建立一個Local Forage例項。
預設的Ionic Storage API 的構造是這樣的:
{
name: '_ionicstorage',
storeName: '_ionickv',
driverOrder: ['sqlite', 'indexeddb', 'websql', 'localstorage']
};
這個配置最重要的部分就是drivers,這決定了儲存機制的效能。在這個例子裡,sqlite是首選,localstorage 是最後的選擇。為了確保這些驅動命名正確,用到了下面這個方法:
_getDriverOrder(driverOrder) {
return driverOrder.map((driver) => {
switch (driver) {
case 'sqlite':
return CordovaSQLiteDriver._driver;
case 'indexeddb':
return LocalForage.INDEXEDDB;
case 'websql':
return LocalForage.WEBSQL;
case 'localstorage':
return LocalForage.LOCALSTORAGE;
}
});
}
這個方法會對映driver order陣列來使用由localForage提供的合適的值。如果你不熟悉陣列的map方法,你可能會對這個視訊感興趣。基本概念就是map操作會對數組裡的每個元素進行一些轉換。在這個例子裡,我們會使用由localForage定義的實際的名稱來替換數組裡的值。
剩下的API基本都是一些圍繞著localForage 的方法的簡單封裝:
get(key: string): Promise<any> {
return this._dbPromise.then(db => db.getItem(key));
}
set(key: string, value: any): Promise<any> {
return this._dbPromise.then(db => db.setItem(key, value));
}
所有Ionic儲存模組在做的事情就是通過檢查dbPromise 的類成員,來確保儲存已經完成了正確的例項化。一旦promise完成,getItem 和setItem 方法就會被用來在儲存空間裡設定(或者銷燬)值。
在Vue裡建立Storage服務
我們可以直接在我們的元件裡使用localForage 介面,但是如果我們想做的像Ionic的Staorage API那樣智慧,把它獨立到它自己的服務裡會更有意義。我們將使用剛才學習到的那些概念,用它們在Ionic/Vue應用裡來建立一個Storage服務。
首先,我們需要安裝必要的依賴庫:
npm install localforage --save
npm install localforage-cordovasqlitedriver --save
建立src/services/storage.js檔案:
import LocalForage from 'localforage';
import CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';
export default class Storage {
dbPromise;
constructor(){
this.dbPromise = new Promise((resolve, reject) => {
let db;
let config = {
name: '_vuestorage',
storeName: '_vuekv',
driverOrder: ['sqlite', 'indexeddb', 'websql', 'localstorage']
}
LocalForage.defineDriver(CordovaSQLiteDriver).then(() => {
db = LocalForage.createInstance(config);
})
.then(() => db.setDriver(this.getDriverOrder(config.driverOrder)))
.then(() => {
resolve(db);
})
.catch(reason => reject(reason));
});
}
ready(){
return this.dbPromise;
}
getDriverOrder(driverOrder){
return driverOrder.map((driver) => {
switch(driver){
case 'sqlite':
return CordovaSQLiteDriver._driver;
case 'indexeddb':
return LocalForage.INDEXEDDB;
case 'websql':
return LocalForage.WEBSQL;
case 'localstorage':
return LocalForage.LOCALSTORAGE;
}
});
}
get(key){
return this.dbPromise.then(db => db.getItem(key));
}
set(key, value){
return this.dbPromise.then(db => db.setItem(key, value));
}
remove(key){
return this.dbPromise.then(db => db.removeItem(key));
}
clear(){
return this.dbPromise.then(db => db.clear());
}
}
這只是Ionic Storage API 相同程式碼的簡化版——它或多或少地以同樣的方式起作用。為了看起來更友好我留下了一些功能沒封裝,但是你沒有理由不去實現剩下的那些功能。
使用Storage服務儲存和獲取資料
現在,我們要做的是使用我們的服務。儲存資料很簡單:
storage.set('something', 'somevalue');
獲取資料:
storage.get('something').then((value) => {
console.log(value);
});
修改 src/components/HelloWorld.vue如下:
<template>
<ion-app>
<ion-header>
<ion-navbar>
<ion-title>REDDIT!</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<ion-button @click="switchSubreddit('funny')">Funny</ion-button>
<ion-button @click="switchSubreddit('gifs')">Gifs</ion-button>
<ion-button @click="switchSubreddit('worldnews')">Worldnews</ion-button>
<ion-list>
<ion-item v-for="post in posts" v-bind:key="post.data.id">
{{post.data.title}}
</ion-item>
</ion-list>
</ion-content>
</ion-app>
</template>
<script>
import RedditService from '../services/reddit';
import Storage from '../services/storage';
const storage = new Storage();
export default {
name: 'HelloWorld',
data () {
return {
posts: []
}
},
created() {
storage.get('subreddit').then((value) => {
if(value === null){
value = 'gifs';
}
RedditService.getPosts(value).then(response => {
this.posts = response.body.data.children;
});
});
},
methods: {
switchSubreddit(subreddit){
storage.set('subreddit', subreddit);
RedditService.getPosts(subreddit).then(response => {
this.posts = response.body.data.children;
});
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
我們引入了剛建立的storage服務,我們首先在created裡呼叫了get方法。created 方法會在元件已建立就執行,所以它會立即檢查storage裡subreddit存不存在,如果存在,subreddit值會被用於呼叫API,如果不存在,預設的使用gifs代替。
我們也設定了三個按鈕以不同的值觸發switchSubreddit 。這個方法會儲存新的值到storage,然後呼叫getPosts。下次應用重啟的時候,儲存在Storage的值會代替預設的gifs 被使用。
備註:如果你沒有看之前的教程,你可能不知道如何使用這個模板的元件。
總結
沒有做太多額外的工作,我們就建立了自己的storage服務,和Ionic/Angular應用的儲存機制一樣的便利和有用。