第四章模块、模块与缓冲
4.1将文章数据从业务中分离
在项目的更目录下新建一个文件夹data,在该文件下新建data.js,将post,js文件中Onload函数下的postList数组的数据整体剪切到data.js文件中,从而避免污染业务层,将所有的内容都堆积在post.js文件中.
4.2小程序的模块
我们还需要使用module.exports向外部暴露一个接口。在data.js文件的最下部添加以下代码:module.exports = { postList:postList}
定义好模块后,接下来就可以在其他js文件中引用这个模块。
我们需要在post.js中引入data.js这个模块。
var dataObj = require("../../data/data.js")
;
代码中的require(path)将模块引入到post.js中,并将模块对象赋值给dataObj。随后在onLoad函数里取出postList数据,并进行数据绑定。
onLoad:function(){
this.setData({
postList:dataObj.postList
})
}
- 使用require引用js模块时,要特别注意以下几点:
(1)被引用的文件一定要带有扩展名js,这一点是不同于页面路径的。
(2)path路径不可以使用绝对路径,否则会报错。应该使用相对路径。
(3)在JavaScript文件中声明的变量和函数只在该文件中有效,不同的文件中可以声明相同名字的变量和函数,不会互相影响。 - 注意为什么是dataObj.postList?因为在输出模块时,我们是将postList作为一个object的属性赋值给module.exports的,所以在require时,得到的也是一个object并非是postList,需要使用dataObj.postList才能获取到真实的文章数据。
这样的做的好处是,object不仅可以包含postList,你还可以在data.js文件中定义除postList外的其他数据,并作为object的属性一起输出。
4.3小程序的模块化
要使用模板,自然需要先新建模板文件。在/pages/post下新建目录post-item,作为模板文件目录。接着在该目录下新建2个文件:post-item-tpl.wxml和post-item-tpl.wxss。
- 在post.wxml的顶部使用
<import src="templatePath" />
来引用模板。对于templatePath路径 <template name="postItemTpl"> <template>
- `定义模块后,其他页面也可以引用该模块
<import src="post-item/post-item-tpl.wxml"/>
//省略代码
<block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx" wx:key="key">
<template is="postItemTpl" data="{{item}}"></template>
</block>
- template的is属性指定要使用哪个模板
- 函数通常可以定义若干个参数,并从函数调用方传入一些数据。同样,模板也可以传入数据。通过template的data属性,可以向template传递数据。这里将wx:for得到的item传入到template里,这样就可以在template内部使用这个item了。要注意的是,向模板里传入数据,同样要使用{{}}的数据绑定语法,比如data={{item}}。
4.4消除template模板对外部变量名的依赖
- 消除template对于外部变量名的依赖,可以使用扩展运算符“…”展开传入对象变量来消除这个问题。
- {{…item}}可以将item这个对象展开。展开之后再传入到template里,就可以保证template不再依赖item这个变量名。
4.5include与import引用模板的区别
- import需要先引入template,然后再使用template;但include不需要预先引入,直接在需要的地方引入模板即可。
- 使用include需要将…item转换成item,以及post.wxml中的{{}}中添加item
- include模式非常简单,就是简单的代码替换,不存在作用域,也不能像import一样使用data传递变量。
- 如果模板仅仅是静态wxml,不涉及数据的传递,可以使用include。但如果模板涉及数据绑定,还是建议使用import。
4.6CSS模块化
(1)将post.wxss中同文章相关的样式(所有以post-开头的样式)全部剪切到post-item-tpl.wxss文件中,post.wxss文件只留下swiper组件相关的样式。
(2)在定义了postItemTpl后,我们需要在post.wxml中引用它。同样,当定义了模板的wxss文件后,也需要在post.wxss文件中引用它。引用样式文件的语法是@import “src”。
- 在引入CSS文件时,既可以是以上代码中所使用的相对路径,也可以是绝对路径。保存后,文章列表的样式恢复正常。
4.7使用缓冲在本地模拟服务器数据库
4.7.1应用程序的生命周期
在页面的JS文件中,我们使用Page(object)来注册页面,并在object中指定页面的生命周期函数等。同样,可以在app.js文件中使用App(object)来注册小程序,并在object中指定小程序的生命周期函数等。
- Object参数有以下几个:
(1)onLaunch 监听小程序初始化,当小程序初始化完成时,会触发onLaunch(全局只触发一次)。
(2)onShow 监听小程序显示,当小程序启动,或从后台进入前台显示,会触发onShow,相当于是onHide的反向动作。
(3)onHide 监听小程序隐藏,当小程序从前台进入后台,会触发onHide。
(4)onError 错误监听函数,当小程序发生脚本错误,或者API调用失败时,会触发onError并带上错误信息。
除了以上几个MINA框架给予的特定函数,开发者还可以添加任意函数或数据到Object参数中,用"this"可以访问这些函数和数据。
可以在开发工具中模拟应用程序的“进入后台”和“从后台显示”这两个动作,从而触发onShow和onHide。开发工具提供了一个【后台】按钮,点击后应用程序将模拟进入后台的效果,再点击一次将从后台返回到前台,
4.7.2使用 Storage缓冲初始本地数据库
进过分析,最好的初始化数据库的时机是在应用程序启动时,在app.js中加入以下代码:
var dataObj = require("data/data.js");
App({
onLaunch:function(){
wx.setStorage({
data: dataObj.postList,
key: 'postList',
success:function(res){
//succss
},
fail:function(){
//fail
},
complete:function(){
//compete
}
})
},
})
上述代码中,首先通过require加载data.js文件作为初始化数据。在应用程序生命周期函数onLaunch里,使用wx.setStorage方法将初始化数据存入到小程序的缓存中。
- 缓存让小程序具备了本地存储数据的能力,它具有以下几个特点:
(1)只要用户不主动清除缓存,则缓存一直存在。
(2) 缓存以key:value键值对的形式存在,很类似于服务器流行的memcache或者redis缓存型数据库。
(3)小程序提供了一系列API用来操作缓存,包括:存储、读取、移除、清除全部和获取缓存信息。每种操作同时都具有同步和异步两个方法。 - 请注意移除和清除的区别。删除某一个key的缓存,请使用wx.removeStorage方法;而如果想清除所有的缓存请使用wx.clearStorage方法。
- 要注意,小程序的缓存永久存在,不存在过期时间这个概念。如果想清除缓存,则需要主动调用清除缓存的API。
- 小程序的本地缓存有容量上限,最大不允许超过10MB。
- wx.setStorage(object)是一个异步方法,参数object包含key,data和success、fail、complete这3个通用方法(几乎所有小程序的异步API方法中都包含这3个方法)。
- key用来设置缓存的键,而data用来设置缓存的值,可以是JavaScript对象或者字符串。
所有的缓存操作方法还有一个同步的版本,用同步的方法来改写一下代码清单
var dataObj = require("data/data.js");
App({
onLaunch:function(){
wx.setStorageSync('postList', dataObj.postList);
},
})
- 同步方法wx.setStorageSync是在异步方法名wx.setStorage后加了一个后缀“Sync”。不仅仅是setStorage,小程序中几乎所有同步方法的方法名都是在异步方法名后增加了“Sync”。
同步方法的参数非常简单,它接收2个参数,例如
wx.setStorageSync(key, data)
同步方法没有success、fail、complete等回调方法。 - 上面的代码将在小程序每次启动时,都会执行一次require和一次setStorage。但实际上,缓存如果不主动清除,它是一直存在的,因此完全没有必要每次启动小程序时都执行一次初始化数据库。仅当缓存不存在时,执行一次上述代码即可。
对数据库进行修改,因为如果每次启动时都重新初始化缓存,那么对数据库的修改就会被初始化数据覆盖,这并不是我们想看到的结果。
App({
onLaunch:function(){
var storageData = wx.getStorageSync('postList');
if(!StorageData){
var dataObj = require("data/data.js");
wx.clearStorageSync();
wx.setStorageSync('postList',dataObj.postList);
}
},
})
wx.getStorageSync(key)这个方法可以获取指定key的缓存内容。如果指定key的缓存不存在,则说明数据库还没有初始化。那么此时首先使用wx.clearStorageSync()清除所有的缓存数据,接着再重新读取并设置初始化数据。
以上代码只有当缓存数据库不存在时,才通过require加载data.js文件,并初始化数据库。这样可以避免每次启动应用程序都重复初始化数据库。
虽然通常来说,require都是放在代码文件的顶部,但我们也可以在需要的时候才引用它。
本地缓存数据库,我们就初步建立完成了,后续内容我们还会持续完善这个数据库。
4.9编写缓冲数据库操作类
- 在项目根目录下新建db文件夹,并在该文件夹下新建DBPost.js文件,并在文件中写入以下代码:
var DBPost = function(){
this.storageKeyName = "postList";//所有文章的缓冲键值}
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.exports={
DBPost:DBPost
};
上述代码首先定义了一个DBPost构造函数。在构造函数中,我们将post数据在缓存数据库中的key, postList,赋值给构造函数的this变量。注意,这个postList必须同app.js中我们初始化数据库时设置的文章数据的key相同,否则无法读取数据。
随后,我们在构造函数的原型链上添加一个对象,这个对象的所有属性和方法都会被构造函数的实例继承。eg:我们在这个对象中增加了一个getAllPostData方法,这个方法将可以获取缓存数据库中所有的文章数据。
在getAllPostData中,我们做了一个判断,如果缓存不存在将重新加载data.js数据文件,并存入到缓存数据库中。
最后,还是使用module.exports将DBPost输出。
4.10 使用缓冲数据库操作类
现在,我们尝试在post.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中。
4.11 使用ES6改写缓冲操作类
class DBPost{
constructor(url){
this.storageKeyName="postList";
}
getAllPostData(){
var res = wx.getStorageSync(this.storageKeyName);
if(!res){
res = require("../data/data.js").postList;
this.initPostList(res);
}
return res;
}
execSetStorageSync(data){
wx.setStorageSync(this.storageKeyName,data)
}
};
export{DBPost}
注意Class中定义的两个函数,它们是不需要function关键字的。同时,方法之间不要加“,”,否则会报错。
最后export输出语法也非常简洁,如export {DBPost};
接着,我们再看如何使用ES6版本的DBPost。
import {DBPost} from "../../db/DBPost.js";
Page({
data: {},
onLoad:function(){
var dbPost = new DBPost();
this.setData({
postList:dbPost.getAllPostData()
});
},
})
注意,这里不再使用require来加载DBPost.js这个文件,而是使用ES6导入模块的关键字import将DBPost导入进来。
4.12完善文章数据
在data.js文件中添加两篇文章
-注意:点击开发工具左侧的【缓存】按钮,随后在弹出的菜单中点击【清除数据缓存】,然后再编译项目,发现文章列表里已经有5篇文章了。