本章内容有一定的难度,但其中的技巧和知识还是很丰富的。本章通过编写几乎所有内容型应用都会附带的“评论”“点赞”“阅读计数”“收藏”等功能,来学习使用小程序的交互反馈组件、缓存的应用、图片选择和预览、屏蔽关键字、录音、拍照以及播放录音等功能

7.1 收藏、评论、点赞、计数功能准备工作

7.1 收藏、评论、点赞、计数功能准备工作

接下来我们将要连续实现4个非常有意思的功能,这些功能在内容型应用中是非常常见的,分别是收藏、点赞、评论和计数。

我们先来编写收藏、评论和点赞的功能按钮。阅读计数是一项被动功能,无须用户有意识地主动触发。在post-detail.wxml中新增一段工具栏代码。

代码清单  7-1      编写3个功能的功能按钮                                post-detail.wxml

<!--pages/post/post-detail/post-detail.wxml-->
<view class="container">
  <image class="head-image" src="{{post.postImg}}"></image>
  <text class="title">{{post.title}}</text>
  <view class="author-date">
    <view class="author-box">
      <image class="avatar" src="{{post.avatar}}"></image>
      <text class="author">{{post.author}}</text>
    </view>
    <text class="date">{{post.dataTime}}</text>
  </view>
  <text class="detail">{{post.detail}}</text>
</view>

<view class="tool">
  <view class="tool-item" catchtap='onUpTop' data-post-id="{{post.postId}}">
    <image src="/images/icon/wx_app_like.png"></image>
    <text>{{post.upNum}}</text>
  </view>
  <view class="tool-item comment" catchtap='onCommentTap' data-post-id="{{post.postId}}">
    <image src="/images/icon/wx_app_message.png"></image>
    <text>{{post.commentNum}}</text>
  </view>
  <view class="tool-item" catchtap='onCollectionTap' data-post-id="{{post.postId}}">
   <image src="/images/icon/wx_app_collected.png"></image>
    <text>{{post.upNum}}</text>
  </view>
</view>

在post-detail.wxml页面的container中添加了一段<view class="tool">的相关代码。该代码实现了收藏、评论和点赞3个功能按钮。每个功能按钮都绑定了对应的点击事件,注意view组件上的catchtap属性。除此之外,我们还在每个功能按钮上使用data-post-id绑定了当前文章的id号。

upNum、commentNum和collectionNum等数据已经在5.13小节中全部添加到了data.js文件中。

接着编写3个功能按钮的样式

代码清单 7-2      编写3个功能按钮的样式                                    post-detail.wxss

.tool{
  height: 64rpx;
  text-align: center;
  line-height: 64rpx;
  margin: 20rpx 28rpx 20rpx 0;
}
.tool-item{
  display: inline-block;
  vertical-align: top;
  margin-right: 30rpx;
}
.tool-item image{
  height: 30rpx;
  width: 30rpx;
  vertical-align: -3px;
  margin-right: 10rpx;
}
.comment image{
  transform: scale(.85);
}

保存刷新后,3个功能按钮将出现在post-detail页面的正下方,如图7-1所示。

点赞评论收藏java 点赞评论收藏叫什么_点赞评论收藏java

图7-1 3个功能按钮的样式和位置

7.2 文章收藏功能

7.2 文章收藏功能

我们首先来实现文章收藏功能。文章收藏功能需要记录两个变量值:

  • 自己是否收藏了文章。如果自己收藏了,那么需要将收藏的图片更换为已收藏。
  • 所有用户收藏文章的总数量。需要注意的是,由于我们的数据库只在本地,无法多次收藏同一篇文章,所以收藏数量永远只能在初始数量的基础上+1或者-1,分别对应取消收藏和点击收藏两种状态。但在真实的项目中,这个收藏数量却是要受到所有用户取消、收藏文章动作影响的。同样的情况也会出现在“文章点赞”这个功能里。

当页面从post跳转到post-detail时,我们就需要知道该文章是否已被用户收藏。在data.js中,我们使用collectionStatus这个属性表示文章是否已被收藏,这个变量的类型是Boolean。那么如何根据collectionStatus这个变量的取值来动态切换收藏图标呢?

熟悉传统Web开发的读者很容易想到用jQuery获取image标签,再动态地设置image的src属性。再次强调,小程序没有dom,一切都是数据绑定,请抛弃dom的思维方式。

7.2.1 条件渲染:wx:if与wx:else

collectionStatus只有两种取值:true或者false。我们需要做的是,当collectionStatus为false时,显示图7-2未收藏状态的图标,而当collectionStatus为true时,显示图7-3收藏状态的图标。

点赞评论收藏java 点赞评论收藏叫什么_xml_02

以上需求是不是就是编程中非常经典的if else?如果wxml组件也像js代码一样有if else就可以解决动态显示收藏图片的问题。下面来看看如何实现这个功能。

小程序提供了wx:if与wx:else来实现条件渲染。当变量为true时,执行wx:if,否则将执行wx:else。修改收藏按钮的wxml代码如下:

代码清单  7-3  条件渲染                                               post-detail.wxml

<view class="tool-item" catchtap='onCollectionTap' data-post-id="{{post.postId}}">
   <image wx:if="{{post.collectionStatus}}" src="/images/icon/wx_app_collected.png"></image>
   <image wx:else src="/images/icon/wx_app_collect.png"></image>
    <text>{{post.collectionNum}}</text>
  </view>

上述代码中我们添加了两个image组件,分别是收藏和未收藏图片。这两个image组件各有一个wx:if和wx:else属性。当post.collectionStatus为true时将显示wx_app_collected.png图片,而当post.collectionStatus为false时将显示wx_app_collect.png图片。

由于我们已经在data.js文件中将部分文章的收藏状态设置为true,因此保存并运行项目,发现所有collectionStatus为true的文章,其收藏图片都将显示wx_app_collected.png,如图7-4所示。

点赞评论收藏java 点赞评论收藏叫什么_数据_03

图7-4 显示wx_app_collected.png

wx:if与wx:else的条件渲染在小程序中被大量使用,不仅仅被用来做图片的更换,还可以用来控制元素的显示和隐藏。

wx:if可以被单独使用,并不一定要同wx:else一起使用。

除此之外,条件渲染还可以做多级别的if else,如代码清单7-4的示例代码所示。

点赞评论收藏java 点赞评论收藏叫什么_xml_04

如果变量length的取值大于5,那么将显示数字1。

如果变量length的取值大于2且小于等于5,那么将显示数字2。

以上条件都不满足,就显示数字3。

你还可以添加更多的elif分支,以实现更多级别的条件判断。

7.2.2 实现收藏点击功能

在7.2.1小节中,我们仅仅是在post-detail页面加载时读取了该文章对于当前用户是否为收藏状态,并正确地设置和显示了这个状态。

在这个小节中,我们将实现用户点击图片进行文章的收藏和取消收藏功能。首先我们继续完善DBPost这个数据库操作类。在DBPost类中添加一个方法,用以处理文章的收藏操作。

代码清单 7-5  添加处理文章收藏的方法                                   DBPost.js

//收藏文章
collect()
{
  return this.updatePostData('collect');
}

该方法中调用了DBPost类的updatePostData方法,这个方法我们还没有编写。

在DBPost类中添加updatePostData方法。该方法是处理点赞、评论、收藏、阅读的核心方法。


代码清单  7-6  添加updatePostData方法                                DBPost.js



//更新本地的点赞、评论信息、收藏、阅读量
updatePostData(category){
  var itemData = this.getPostItemById(),
   postData = itemData.data,
  allPostData = this.getAllPostData();
  switch(category){
      case 'collect':
        //处理收藏
        if(!postData.collectionStatus){
          //如果当前状态是未收藏
          postData.collectionNum++;
          postData.collectionStatus = true;
        }else{
          //如果当前状态是已收藏
          postData.collectionNum--;
          postData.collectionStatus = false;
        }
        break;
      default:break;
  }
  //更新缓存数据库  
  allPostData[itemData.index] = postData;
  this.execSetStorageSync(allPostData);
  return postData;
}

我们目前仅处理collect这一种操作,后续我们将继续在代码清单7-6的switch case中添加评论、阅读数、点赞等处理分支。

这样,DBPost就具备了处理文章收藏的能力。当用户点击收藏按钮后,在点击事件函数中调用DBPost的collect方法即可。

处理文章收藏动作的事件函数是onCollectionTap,这个事件函数已在代码清单7-1中被注册在了收藏功能按钮上。我们只需要在post-detail.js中编写这个方法即可

代码清单   7-7编写onCollectionTap方法                                            post-detail.js

nCollectionTap:function(event){
        //dbpost对象已在onLoad函数中被保存到了this变量中,无需再次实例化
        var newData = this.dbPost.collect();
        //从新绑定数据,注意,不要将整个newData全部作为setData的参数,应当有选择的更新部分数据
        this.setData(
          {
            'post.collectionSataus':newData.collectionStatus,
            'post.collectionNum':newData.collectionNum
          }
        ) 
}

7.2.3 交互反馈wx:showToast

现在,我们已经实现了文章的收藏与取消收藏功能,但收藏功能的体验并不好,用户在收藏和取消收藏后没有任何交互反馈提示。

小程序提供了一些交互反馈API来帮助开发者处理交互相关的问题。目前,小程序提供了以下4个交互反馈API:

  • wx.showToast
  • wx.hideToast
  • wx.showModal
  • wx.showActionSheet

我们选用wx.showToast(object)来制作文章收藏功能的交互反馈。


代码清单 7-8  文章收藏功能的交互反馈                              post-detail.js


//交互反馈
    wx.showToast({
      title:newData.collectionStatus?"收藏成功":"收藏取消",
      duration:1000,
      icon:"sucess",
      make:true
     })

其中,object参数的title属性用于设置提醒消息的内容;duration设置提醒的自动消失时间,最长10000毫秒,默认值为1500毫秒;icon可以设置一个小图标,其取值只能是success和loading;mask指定是否显示透明的蒙层,以防止触摸穿透,默认值为false。

mask主要用来防止用户连续点击收藏按钮。开发者可执行尝试将mask设置为true和false时的不同效果:当mask为true时连续点击收藏图标,图标不会连续做出收藏/取消收藏的响应;当mask为false时,就会不停地响应用户的点击操作。

wx.showToast的效果如图7-5所示。


点赞评论收藏java 点赞评论收藏叫什么_ico_05


图7-5 wx.showToast效果

7.3 文章点赞功能

7.3 文章点赞功能

文章点赞功能的实现思路同收藏几乎是一样的。首先在DBPost.js中增加点赞的方法。

代码清单7-9   添加处理点赞操作的方法                           DBPost.js

//点赞或取消点赞
up(){
  return this.updatePostData('up');
  
}

接着在DBPost的updatePostData方法中处理当case为up时的情况。下面给出updatePostData的全部代码。

代码清单  7-10  增加case为up时的处理方法                                                DBPost.js

//更新本地的点赞、评论信息、收藏、阅读量
updatePostData(category){
  var itemData = this.getPostItemById(),
   postData = itemData.data,
  allPostData = this.getAllPostData();
  switch(category){
      case 'collect':
        //处理收藏
        if(!postData.collectionStatus){
          //如果当前状态是未收藏
          postData.collectionNum++;
          postData.collectionStatus = true;
        }else{
          //如果当前状态是已收藏
          postData.collectionNum--;
          postData.collectionStatus = false;
        }
        break;
      case 'up':
        if(!postData.upStatus){
          postData.upNum++;
          postData.upStatus = true;
          }else{
            postData.upNum--;
            postData.upStatus = false;
          }
          break;
        
      default:break;
  }
  //更新缓存数据库  
  allPostData[itemData.index] = postData;
  this.execSetStorageSync(allPostData);
  return postData;
}

代码清单7-10同代码清单7-6相比,仅仅增加了case为'up'时的这段代码。很明显,我们可以看到处理点赞的逻辑同处理收藏时的逻辑几乎一样:改变upStatus的状态,并对upNum这个计数变量做相应的增减操作。

我们编写完DBPost中关于点赞的接口后,接着编写post-detail.js和post-detail.wxml中关于点赞的相关代码。


代码清单 7-11      编写onUpTap方法                                       post-detail.js

onUpTap:function(event){
        var newData = this.dbPost.up();
        this.setData({
          'post.upStatus':newData.upStatus,
          'post.upNum':newData.upNum,
        })
        wx.showToast({
          title: newData.upStatus ? "点赞成功" : "点赞取消",
          duration: 1000,
          icon: "sucess",
          make: true
        })

onUpTap方法响应用户点赞的动作。当用户点击点赞按钮后,onUpTap方法将调用DBPost的up方法并将返回的最新数据使用this.setData更新。

类似于收藏功能,我们还需要使用条件渲染wx:if改写wxml中的点赞按钮。

代码清单  7-12     点赞功能的条件渲染                             post-detail.wxml

<view class="tool-item" catchtap='onUpTap' data-post-id="{{post.postId}}">
    <image wx:if="{{post.upStatus}}" src="/images/icon/wx_app_liked.png"></image>
    <image wx:else  src='/images/icon/wx_app_like.png'></image>
    <text>{{post.upNum}}</text>
  </view>

以上代码将在post.upStatus为true时显示wx_app_liked.png,当post.upStatus为false时显示wx_app_like.png。

在编写完以上代码后,保存运行项目,点击点赞按钮,图片会不断切换,点赞数也将相应地+1或者-1。

很多开发者可能还不太习惯使用数据绑定的方式来做样式、状态的切换,但数据绑定的写法确实非常简化、方便。我们只需要在js中改变各类变量的状态和值,前端组件就会响应我们的操作,动态地做出变化。

7.4 本地缓存的重要性及应用举例

提供本地的key&value缓存机制是小程序的一大特点,善用本地缓存将可以极大地改善客户端的体验与服务器的性能。

前几个小节中,我们大量地使用了本地缓存来模拟服务器的数据库。这样做一方面是因为我们并没有真实的服务器,必须依靠客户端的缓存能力来记录数据;另一方面是因为即使在真实的项目中我们拥有自己的远程服务器,也依然需要在客户端管理本地缓存。

举个例子,如果我们要实现一个城市列表插件,就必然要获取全国所有城市的信息。全国大概有600多个城市,这么大的数据量难道每次打开这个插件都要去服务器取城市数据吗?这些城市的数据相对非常稳定,并不会频繁变化,每次都去服务器加载是对流量和服务器性能的严重消耗。

所以,最好的解决方案就是将城市数据保存在本地缓存中,而不是每次都去服务器请求数据。

在一个高性能的产品中,缓存的重要性是不言而喻的。建议开发者将本地缓存视作一个本地的key&value数据库,并封装一些类和公共方法,提供给项目中的各个调用方。最好不要让getStorage、setStorage等方法充斥在项目的每一个角落。

Orange Can项目中的DBPost类就是一个不错的示例,它实现了对缓存的良好管理,并向调用方提供了一系列可读性非常强的API。建议开发者参考DBPost并将这种思路应用到自己的项目中。