WeChat 模块、模板与缓存

2024-03-19 14:07:57 浏览数 (1)

UnsplashUnsplash

本次的系列博文的知识点讲解和代码,主要是来自于 七月老师 的书籍《微信小程序开发:入门与实践》,由个人总结并编写,关于更多微信小程序开发中的各项技能,以及常见问题的解决方案,还请大家购买书籍进行学习实践,该系列博文的发布已得到七月老师的授权许可

授权许可授权许可

我们在 WeChat 文章列表页面(二) 中,已经完成了数据的绑定和页面的跳转了,效果图如下所示

文章列表页面文章列表页面

模块

我们先把所有文章的数据分离到一个单独的 js 文件中,防止污染我们的业务层,在根目录下新建一个文件夹,命名为 data,在 data 文件夹下新建一个 js 文件,命名为 data.js,并将原来复杂对象的数据绑定修改成简单的字符串

代码语言:javascript复制
var postList = [{
      date: "Jan 28 2017",
      title: "小时候的冰棍儿与雪糕",
      postImg: "/images/post/post-4.jpg",
      avatar: "/images/avatar/avatar-5.png",
      content: "冰棍与雪糕绝对不是同一个东西。3到5毛钱的雪糕犹如现在的哈根达斯,而5分1毛的冰棍儿就像现在的老冰棒。时过境迁,...",
      readingNum: 0,
      collectionNum: 0,
      commentNum: 0
    },
    {
      date: "Jan 9 2017",
      title: "从童年呼啸而过的火车",
      postImg: "/images/post/post-5.jpg",
      avatar: "/images/avatar/avatar-1.png",
      content: "小时候,家的后面有一条铁路。听说从南方北上的火车都必须经过这条铁路。火车大多在晚上经过,但也不定时只有在夜深人静的时候,火车的声音才能从远方传来...",
      readingNum: 0,
      collectionNum: 0,
      commentNum: 0
    },
    {
      date: "Jan 29 2017",
      title: "记忆里的春节",
      postImg: "/images/post/post-1.jpg",
      avatar: "/images/avatar/avatar-3.png",
      content: "年少时,有几样东西,是春节里必不可少的:烟花、心意、凉茶、压岁钱、饺子。年分大小年,有的地方是腊月二十三过小年,有的地方是腊月二十四...",
      readingNum: 0,
      collectionNum: 0,
      commentNum: 0
    },
  ]

我们提取的数据文件 data.js 可以视作是小程序的一个模块,若是想让其他文件访问这个模块,还需要使用 module.exports 向外部暴露一个接口,在 data.js 文件的最下方添加以下代码:

代码语言:javascript复制
module.exports = {
    postList:postList
}

定义好模块后,我们还需要在 post.js 中引入 data.js 这个模块

代码语言:javascript复制
var dataObj = require("../../data/data.js");

Page({
  data: {

  },
  onLoad:function(){
    this.setData({
      postList: dataObj.postList
    })
  }
})

在这里使用 require(path) 引用 js 模块时,要特别注意以下几点:

  • 被引用的文件一定要带有扩展名 js,这一点是不同于页面路径的
  • path 路径不可以使用绝对路径,否则会报错,应使用相对路径
  • 在 JavaScript 文件中声明的变量和函数只在该文件中有效,不同的文件中可以声明相同名字的变量和函数,不会相互影响

最后,记得调整 post.wxml 中 {{}} 的语法,在这里就不再演示了

模板

我们在文章列表里使用了列表渲染,但如果其他页面同样需要显示文章列表该怎么办呢?通常情况下,我们会考虑把一些公共的、经常使用的业务逻辑提取成一个公共函数,当在多个地方需要使用函数时,只需要调用这个函数即可完成相应的业务

而小程序也提供了一个称作模板的技术来支持对 wxml 组件的封装,但这种封装仅仅只是 wxml 的代码片段,并不像 AngularJS 中可以把 HTML、JS 作为一个整体被封装起来

要使用模板,首先需要新建模板文件,在 /pages/post 下新建目录 post-item,作为模板文件目录,并在该目录下新建 2 个文件:post-item-tpl.wxml 和 post-item-tpl.wxss 文件

使用模板是为了简化 post.wxml 中文章的写法,让文章可以成为一个单独的“组件”,供其他多个地方使用,就像是一个简单的 image 组件就可以实现图片的显示功能

接下来,我们把 post.wxml 中 <block> 标签中关于文章的代码剪切到 post-item-tpl.wxml 中,让这段代码成为一个可复用的“组件”

代码语言:javascript复制
<template name="postItemTpl">
    <view class="post-container">
      <view class="post-author-date">
        <image src="{{item.avatar}}" />
        <text>{{item.date}}</text>
      </view>
      <text class="post-title">{{item.title}}</text>
      <image class="post-image" src="{{item.postImg}}" mode="aspectFill" />
      <text class="post-content">{{item.content}}</text>
      <view class="post-like">
        <image src="/images/icon/wx_app_collect.png" />
        <text>{{item.collectionNum}}</text>
        <image src="/images/icon/wx_app_view.png" />
        <text>{{item.readingNum}}</text>
        <image src="/images/icon/wx_app_message.png" />
        <text>{{item.commentNum}}</text>
      </view>
    </view>
</template>

template 模板相关的内容必须被包裹在 <template></tenplate> 标签中,使用 name 属性指定 template 模板的模板名,这个模板名将在引用模板时被使用

接下来,我们在 post.wxml 中引用并使用这个 template

代码语言:javascript复制
<import src="post-item/post-item-tpl.wxml" />
<view>
  <swiper indicator-dots="true" autoplay="true" interval="5000" circular="true">
    <swiper-item>
      <image src="/images/post/post-1@text.jpg" />
    </swiper-item>
    <swiper-item>
      <image src="/images/post/post-2@text.jpg" />
    </swiper-item>
    <swiper-item>
      <image src="/images/post/post-3@text.jpg" />
    </swiper-item>
  </swiper>
  <block wx:for="{{postList}}" wx:for-index="idx" wx:for-item="item">
      <template is="postItemTpl" data="{{item}}" />
  </block>
</view>
运行结果运行结果

可以看到,文章列表是可以正常地加载显示,在 post.wxml 的顶部使用 <import src="templatPath" /> 来引用模板,对于 templatePath 路径,这里需要注意,去掉 wxml 文章拓展名也是可以的,但官方示例中是带有 .wxml 扩展名的,所以建议开发者戴上模板文件的扩展名

引用模板后,就可以在这个页面中使用这个模板了,在需要模板的位置使用 <template> 标签引入,template 的 is 属性指定要使用哪个模板,通过 template 的 data 属性,可以向 template 传递数据,这里将 wx:for 得到的 item 传入到 template 里,这样就可以在 template 内部使用这个 item 了,这里需要注意的是,向模板里传入数据,同样要使用 {{}} 的数据绑定语法

消除 template 模板对外部变量名的依赖

在上一小节中,我们已经成功地将 wxml 代码做成了模板,同样的,我们也把 post.wxss 中同文章相关的样式作为模板“打包”起来,并在 post.wxss 文件的顶部添加如下代码:

代码语言:javascript复制
@import "post-item/post-item-tpl.wxss";

我们之前讲过,列表渲染中 wx:for-item 可以指定数组子元素的变量名,那我们现在尝试将 wx:for-item = "item" 改成 wx:for-item = "item1",这将使得 postList 数组子元素的变量名由 item 变成 item1,此时,如果要将数据传入到 postItemTpl 中,则应该设置 data = "{{item1}}"

运行结果运行结果

我们做完以上变更后,却发现文章数据将消失了,而之前代码可以正常运行是因为我们向 template 传入的变量名 data = "{{item}}",恰好和 template 里面数据绑定的变量名 item 一样,一旦我们更改 item 为 item1 后,template 就找不到这个 item 了

template 模板并不像函数,没有提供一个定义参数名的地方,没有办法更改从外部传入的 item1 为 item,我们当然可以通过将 postItemTpl 这个 template 内部的 item 更改为 item1 来让代码重新正常运行,但这种由定义方要求调用方遵守变量名命名的做法是不太合理的,要解决这个问题,就必须消除 template 对于外部变量名的依赖,可以使用拓展运算符 ... 展开传入对象变量来消除这个问题

代码语言:javascript复制
<block wx:for="{{postList}}" wx:for-index="idx" wx:for-item="item">
    <template is="postItemTpl" data="{{...item}}" />
</block>

接着去掉 post-item-tpl.wxml 文件中 {{}} 里所有的 item,保存运行,文章列表就可以正常显示了,{{...item}} 可以将 item 这个对象展开,展开之后再传入到 template 里,就可以保证 template 不再依赖 item 这个变量名

include 引用模板

小程序还提供了另一种引入模板的方式:include,include 在使用上同 import 有以下区别:

  • import 需要先引入 template,然后再使用 template;但 include 不需要预先引入,直接在需要的地方引入模板即可
  • include 模式非常简单,就是简单的代码替换,不存在作用域,也不能像 import 一样使用 data 传递变量
代码语言:javascript复制
<block wx:for="{{postList}}" wx:for-index="idx" wx:for-item="item">
    <include src="post-item/post-item-tpl.wxml" />
</block>

以上代码将 block 标签中的 template 更换成了 <include>,include 同样使用 src 属性指向模板文件,需要注意的是,include 无法引入包含有 template 标签的代码,所以需要把 post-item-tpl.wxml 里的 template 标签删除,只保留文章本身的 wxml 代码,而文章数据部分,还需要再次在 post-item-tpl.wxml 中的 {{}} 加入 item 这个变量名,开发者可自行尝试

除了在使用上有所不同,include 和 import 还存在其他不同之处:import 有作用域的概念,即只会 import 目标文件中定义的 template,而不会 import 目标文件 import 的 template

如:C import B,B import A,在 C 中可以使用 B 定义的 template,在 B 中可以使用 A 定义的 template,但是 C 不能使用 A 定义的template

所以在这里建议大家,如果模板仅仅是静态 wxml,不涉及数据的传递,可以使用 include,但如果模板涉及数据绑定,还是建议使用 import

缓存

之前我们已经将文章相关数据分离到了 data.js 文件中,并在 post.js 文件里通过 require 来加载 data.js 文件,但现在有一个问题,如果我们要修改数据怎么办?修改后的数据,还想共享给其他页面使用,并长期保存这些数据怎么办?

小程序提供了一个缓存的特性,来支持数据的读取、保存和更新,并且这些数据不会因为应用程序重启或者关闭而消失

根据页面生命周期,将初始化数据装载到缓存的最好时期,应该是在小程序启动时,即执行 onLaunch 生命周期函数时,所以我们在 app.js 中加入以下代码:

代码语言:javascript复制
var dataObj = require("data/data.js")
App({
  onLaunch:function(){
    wx.setStorage({
      key: 'postList',
      data: dataObj.postList,
      success: function(res){
        //success
      },
      fail:function(){
        //fail
      },
      complete:function(){
        //complete
      }
    })
  },
})

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

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

  • 只要用户不主动清除缓存,则缓存一直存在
  • 缓存以 key:value 键值对的形式存在,很类似于服务器流行的 memcache 或者 redis 缓存型数据库
  • 小程序提供了一系列 API 用来操作缓存,包括:存储、读取、移除、清除全部或获取缓存信息,每种操作同时都具有同步和异步两个方式
  • 删除某一个 key 的缓存,请使用 wx.removeStorage 方法;而如果想清除所有的缓存请使用 wx.clearStorage 方法
  • 小程序的缓存永久存在,不存在过期时间这个概念,如果想清除缓存,则需要主动调用清除缓存的 API
  • 小程序的本地缓存有容量上限,最大不允许超过 10 MB

wx.setStorage(object) 是一个异步方法,参数 object 包含 key,data 和 success、fail、complete 这 3 个通用方法(几乎所有小程序的异步 API 方法中都包含这 3 个方法),key 用来设置缓存的键,而 data 用来设置缓存的值,可以是 JavaScript 对象或者字符串

我们可以通过 Storage 面板来查看具体的变化

Storage 面板数据情况Storage 面板数据情况

图中 postList 就是在代码中设置的 key:'postList',后面的 Array 数组就是设置的 data 对象,也就是要初始化的数据,对应的是 data.js 文件的 3 篇文章数据,Storage 面板是查看缓存的重要功能,当开发者遇到与缓存相关的问题时,请一定要到这里来看一看

同步设置缓存

同步方法 wx.setStorageSync 是在异步方法名 wx.setStorage 后加了一个后缀 “Sync”,不仅仅是 setStorage,小程序中几乎所有同步方法的方法名都是在异步方法名后增加了 “Sync”,而同步方法只接收 key 和 data 这 2 个参数,并没有 success、fail、complete 等回调方法

我们这里采取的是同步方法,开发者可以根据自己的业务和环境选取异步方法,但需要注意的是,选取异步方法会大大增加代码风险和调试难度,如果没有必要(通常是处于性能和体验的考虑),建议优先考虑同步方法

代码语言:javascript复制
App({
  onLaunch:function(){
    var storageData = wx.getStorageSync('postList');
    if(!storageData){
      //如果postList缓存不存在
      var dataObj = require("data/data.js")
      wx.clearStorageSync();
      wx.setStorageSync('postList',dataObj.postList);
    }
  },
})

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

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

该章节的内容到这里就全部结束了,源码我已经发到了 GitHub WeChat_04 上了,有需要的同学可自行下载

0 人点赞