微信开发入门第五章:模块、模板和缓存

5.8 使用缓存在本地模拟服务器数据库

5.8 使用缓存在本地模拟服务器数据库

在之前的小节中,我们将文章相关数据分离到了data.js文件中,并在post.js文件里通过require来加载data.js文件。

引用并读取data.js当然没有问题,但我们考虑一个问题,如果要修改数据怎么办?修改后的数据,还想共享给其他页面使用,并长期保存这些数据怎么办?

比如,在后面的内容中,我们会增加文章的评论、阅读量计数、文章收藏数等动态计数功能;且当用户重启应用后这些用户数据并不应该丢失。

我们需要一个类似于数据库的概念,可以读取、保存、更新这些数据,且这些数据不会因为应用程序重启或者关闭而消失。

小程序提供了一个非常重要的特性——缓存,来支持这样的特性。

现在,将data.js这个文件视作是本地数据库的初始化数据,要做的第一件事就是将这些初始化数据“装进”缓存中,以形成数据库的初始化数据。

5.8.1 应用程序的生命周期

在什么时候将初始化数据装载到缓存中是一个需要考虑的问题。考虑一下,初始化的行为在整个应用程序生命周期里只应该发生一次,所以最好的时机是在小程序启动时来装载初始化数据。

应用程序启动时是一个MINA框架行为,如果想掌握这个时刻,并做一些我们想做的事儿,就需要框架通知我们:嗨,现在是应用程序启动的时候,你要做什么事儿,就在这个函数里做吧。

想想之前页面的生命周期,每一个重要的结点,MINA框架都会给页面一个通知,比如onLoad、onShow、onReady等。同样,整个应用程序也有自己的生命周期。

还是类比一下页面的生命周期。在页面的JS文件中,我们使用Page(object)来注册页面,并在object中指定页面的生命周期函数等。同样,可以在app.js文件中使用App(object)来注册小程序,并在object中指定小程序的生命周期函数等。

Object参数有以下几个:

  • onLaunch 监听小程序初始化,当小程序初始化完成时,会触发onLaunch(全局只触发一次)。

  • onShow 监听小程序显示,当小程序启动,或从后台进入前台显示,会触发onShow。

  • onHide 监听小程序隐藏,当小程序从前台进入后台,会触发onHide。

  • onError 错误监听函数,当小程序发生脚本错误,或者API调用失败时,会触发onError并带上错误信息。

当然,除了以上几个MINA框架给予的特定函数,开发者还可以添加任意函数或数据到Object参数中,用"this"可以访问这些函数和数据。

这里特别对onShow和onHide做一个说明。onHide会在小程序从前台进入后台时触发,比如在iPhone中通过按下“Home”键,将微信隐藏时触发onHide;而onShow不仅仅在小程序启动时会触发,还会在小程序从后台到前台时触发,相当于是onHide的反向动作。

可以在开发工具中模拟应用程序的“进入后台”和“从后台显示”这两个动作,从而触发onShow和onHide。开发工具提供了一个【后台】按钮,点击后应用程序将模拟进入后台的效果,再点击一次将从后台返回到前台,如下图所示。

                                                 

                                              图5-2 开发工具模拟小程序进入后台和返回前台的操作

5.8.2 使用Storage缓存初始化本地数据库

上一小节中我们分析了,最好的初始化数据库的时机是在应用程序启动时,在app.js中加入以下代码:

代码清单  5-12    设置数据缓存                                                  app.js

var objData = require("data/data.js");

App({

  /**
   * 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
   */
  onLaunch: function () {
    wx.setStorage({
      key: 'postList',
      data: 'objData.postList',
    })
  },

  
})

在上面的代码中,首先通过require加载data.js文件作为初始化数据。在应用程序生命周期函数onLaunch里,使用wx.setStorage方法将初始化数据存入到小程序的缓存中。

什么是缓存?

缓存让小程序具备了本地存储数据的能力,它具有以下几个特点:

  • 只要用户不主动清除缓存,则缓存一直存在。

  • 缓存以key:value键值对的形式存在,很类似于服务器流行的memcache或者redis缓存型数据库。

  • 小程序提供了一系列API用来操作缓存,包括:存储、读取、移除、清除全部和获取缓存信息。每种操作同时都具有同步和异步两个方法。具体API请参考官方文档。

  • 请注意移除和清除的区别。删除某一个key的缓存,请使用wx.removeStorage方法;而如果想清除所有的缓存请使用wx.clearStorage方法。

  • 要注意,小程序的缓存永久存在,不存在过期时间这个概念。如果想清除缓存,则需要主动调用清除缓存的API。

  • 小程序的本地缓存有容量上限,最大不允许超过10MB。

代码清单5-12中的wx.setStorage(object)是一个异步方法,参数object包含key,data和success、fail、complete这3个通用方法(关于这3个通用方法,之前我们反复强调,几乎所有小程序的异步API方法中都包含这3个方法,后面的内容将不再列出这些方法,请开发者根据自己的需求来使用这些方法)。

key用来设置缓存的键,而data用来设置缓存的值,可以是JavaScript对象或者字符串。

是的,这就是我们搭建的一个简易本地数据库,它具有增、删、改的功能。当然,它也具备简单的查询功能,但并不如MySQL这类数据库的查询功能强大。注意,将本地缓存理解为一个简易数据库的思想非常重要,我们应当像在服务器编写数据库访问类一样,编写一组操作自己业务缓存的通用方法,而且最好将这些方法集中在一个“类”中。这样的做法将大大提高代码的可阅读性与可维护性。在实际项目中,本地缓存是非常重要的功能,可以极大地改善用户体验。

所有的缓存操作方法还有一个同步的版本,用同步的方法来改写一下代码清单5-12。

代码清单  5-13    同步设置缓存                                                              app.js

var objData = require("data/data.js");

App({

  /**
   * 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
   */
  onLaunch: function () {
   wx.setStorageSync("postList", objData.postList);
  },

  
})

同步方法wx.setStorageSync是在异步方法名wx.setStorage后加了一个后缀“Sync”。不仅仅是setStorage,小程序中几乎所有同步方法的方法名都是在异步方法名后增加了“Sync”。

同步方法的参数非常简单,它接收2个参数,例如

wx.setStorageSync(key, data)

同步方法没有success、fail、complete等回调方法。在本书的后续代码中如果没有特殊情况,通常都用同步方法。开发者可以根据自己的业务和环境选取异步方法。但要注意的是,选取异步方法会大大增加代码风险率和调试难度。如果没有必要(通常是处于性能和体验的考虑),建议优先考虑同步方法。

代码清单5-12和代码清单5-13分别用异步和同步的方法设置了缓存。但考虑一下上面的代码还有没有问题。

上面的代码将在小程序每次启动时,都会执行一次require和一次setStorage。但实际上,缓存如果不主动清除,它是一直存在的,因此完全没有必要每次启动小程序时都执行一次初始化数据库。仅当缓存不存在时,执行一次上述代码即可。

下面我们对数据库进行修改,因为如果每次启动时都重新初始化缓存,那么对数据库的修改就会被初始化数据覆盖,这并不是我们想看到的结果。

修改上述代码如下。

代码清单  5-14   优化缓存初始化判断                                app.js

App({

  /**
   * 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
   */
  onLaunch: function () {
    var storageData = wx.getStorageSync("postList");
    if (!storageData){
      wx.clearStorageSync();
      var objData = require("data/data.js");
      wx.setStorageSync("postList", objData.postList);
    }
  },

  
})

wx.getStorageSync(key)这个方法可以获取指定key的缓存内容。如果指定key的缓存不存在,则说明数据库还没有初始化。那么此时首先使用wx.clearStorageSync()清除所有的缓存数据,接着再重新读取并设置初始化数据。

以上代码优化了初始化缓存数据库的方案。只有当缓存数据库不存在时,才通过require加载data.js文件,并初始化数据库。这样可以避免每次启动应用程序都重复初始化数据库。

虽然通常来说,require都是放在代码文件的顶部,但我们也可以在需要的时候才引用它。代码清单5-14中演示了这种用法。

本地缓存数据库,我们就初步建立完成了,后续内容我们还会持续完善这个数据库。

5.8.3 缓存的强制清理及注意事项

除了使用wx.clearStorageSync()代码清除缓存外,在模拟器中还可以通过开发工具左侧的【缓存】工具进行缓存清理。【缓存】工具点击后会弹出4个选项,其中【清除数据存储】就是清除Storage的功能。

但要特别注意,真机上没有类似于开发工具这样的强制清理缓存的按钮。微信自带的缓存清理并不是用来清除小程序缓存的,这点要特别注意。

笔者在实际开发过程中遇到很多缓存引起的问题,其中大多数是因为更新了初始化数据后,却忘记在手机上清除缓存,以至于没办法更新真机上的初始化数据。

建议的解决方案是,在开发过程中,临时在页面里增加一个按钮,点击按钮执行wx.clearStorageSync(),强制清理缓存。这样重启应用程序后,由于本机没有缓存,所以会重新加载初始化数据。本书将在后面编写setting设置页面时,增加一个清理缓存的选项。

在处理缓存相关问题时,开发者要保持头脑清醒,否则有时候一个小小的缓存没更新的问题,将浪费开发者大量的时间。

一个典型的案例是,你更改了初始化数据里的文章图片路径,但在真机上运行时,由于缓存存在,就不会重新加载新的初始化数据。这将导致你的新图片一直无法显示。

另外一种思路是,在开发阶段,不要做代码清单5-14中是否有缓存的判断,每次应用程序重启都强制更新一次初始化数据,从而保证数据一直是最新状态。

5.9 编写缓存数据库操作类

5.9 编写缓存数据库操作类

我们来构建一个访问缓存数据库的访问“类”。在JavaScript编程的世界里似乎“类”这个概念一直都不是那么流行,相当一部分原因在于JavaScript的面向对象和我们在大学和工作后所理解的诸如Java、C#这种经典的面向对象语言有很多的不同,这是由于JavaScript历史原因造成的。但JavaScript里并不是没有面向对象,只不过它是用原型链的方式来实现对象的继承机制。

ES6的出现让JavaScript这个语言重新焕发了新生,module、lambda、class等特性的支持,让JavaScript更加现代化。

考虑到本书主要内容是讲解小程序,如果全部使用ES6,必然会全面使用面向对象的思想来构建整个项目,这会给部分不熟悉ES6的开发者造成一定的困扰。所以Orange Can项目的编写并没有全面使用ES6,但ES6的重要性是不言而喻的,Orange Can将尝试对于某些模块使用ES6来编写。

不使用ES6并不代表我们没有办法编写面向对象的代码,我们将尝试用prototype和ES6的Class分别来构建缓存数据库的操作类。

在项目根目录下新建db文件夹,并在该文件夹下新建DBPost.js文件,并在文件中写入以下代码:

代码清单  5-15  prototype  构造数据操作类                           DBPost.js

var  DBPost = function(){
  this.storageKeyName="postLiat";//所有的文章本地缓存存储键值
}

DBPost.prototype={
  //得到全部信息
  getAllPostData:function(){
    var res=wx.getStorageSync(this.storageKeyName);
    if(!res){
      res  = require("../data/data.js").postList;
      this.execSetStorageSync(res);
    }
    return res;
  },

  //本地缓存  保存/更新
    execSetStorageSync:function(data){
        wx.setStorageSync(this.storageKeyName, data);
    },

}

module.import={
  DBPost:DBPost,
}

上述代码首先定义了一个DBPost构造函数。在构造函数中,我们将post数据在缓存数据库中的key, postList,赋值给构造函数的this变量。注意,这个postList必须同app.js中我们初始化数据库时设置的文章数据的key相同,否则无法读取数据。

随后,我们在构造函数的原型链上添加一个对象,这个对象的所有属性和方法都会被构造函数的实例继承。比如,我们在这个对象中增加了一个getAllPostData方法,这个方法将可以获取缓存数据库中所有的文章数据。

在getAllPostData中,我们做了一个判断,如果缓存不存在将重新加载data.js数据文件,并存入到缓存数据库中。

最后,还是使用module.exports将DBPost输出。

如果开发者对面向对象的JavaScript不是太熟悉,建议去学习一下面向对象的JavaScript编程,网上有不少这方面的资料。

当然这里最好的写法还是使用ES6的Class和Module来编写。使用构造函数和prototype原型链构建对象,总是会让那些熟悉Java、C#等现代经典面向对象的开发者觉得很奇怪。ES6的Class优化了JavaScript的对象构建方式,让对象看起来更加符合现代的面向对象写法。但是,ES6大多特性只是一种语法糖,在本质上JavaScript的运行和解析机制并没有被改变。所以理解JavaScript的构造函数与原型链prototype依然非常重要。

5.10 使用缓存数据库操作类

5.10 使用缓存数据库操作类

现在,我们尝试在post.js中使用上一小节中定义的数据库操作类,将post.js代码更改一下

代码清单  5-16             使用DBPost.js                           post.js

var DBPost=require('../../db/DBPost.js').DBPost;

Page({

    data:{ },
    onLoad:function(){
      var dbPost = new DBPost();
     
      this.setData({
        postList:dbPost.getAllPostData(),
        
        });
      }
    
         
})
          
    

需特别注意的是,这里没有直接使用require加载data.js文件,因为data.js现在只是初始化数据,它已经在app.js中被装载到缓存数据库中。所以,我们现在require的是DB操作类所在的模块文件,通过这个类来操作文章数据。

代码第一行同样使用reuqire加载DBPost.js文件,并读取DBPost。

那么,如果要使用DBPost,必须先使用操作符“new”将DBPost实例化。实例化DBPost后,就可以调用该对象的getAllPostData方法,从而读取所有文章的缓存数据并绑定到postList中。

保存后,程序可以正常地运行。



















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值