第五章 在小程序里阅读文章(下) 前面我们搭建了一个简单的页面框架,实现了文章列表展示和跳转文章详情的 功能。然而,仅有这种功能是显著不够的,因此,接下来,我将向你讲解,如 何加上更多稍显复杂,但很有必要的功能,包括加载更多文章列表,显示未读 与已读状态和文章分享。 加载更多文章列表 前面,我们在首页模拟了获取第一页文章的恳求。考虑到性能问题,大部分情 况下我们不会在首页展示出所有的文章列表,在 PC 上我们可以通过分页来实 现,而在移动端,比较常见的做法是在列表末尾通过上拉或则一个加载更多按 钮,来获取更多文章。这里,我们将采用第二种做法。 加载更多的逻辑是这样的:当一次恳求获取到的文章数目小于或等于我们规定 的每页数目时,按钮的文案会显示为 “加载更多” ,并且点击后会去获取下一页 的文章,而假如获取到的文章数目大于我们规定的每页数目时,按钮的文案会 显示为 ”已无更多“。 这里须要解释一下,如果获取到最后一页的文章数正好为我们规定的每页数目 时,当点击加载更多,会继续获取下一页文章,此时获取到的文章数目为 0, 小于规定的每页数目,按钮的文案变为 ”已无更多“。 为了简化步骤,我们模拟的下一页的文章数目大于 4 篇,这样,当我们点击一 次加载更多后,按钮文章会变为”已无更多“。
这里我们须要降低两个变量,一个是 isEnd,用于标示已无更多,另一个是 isLoading,用于避免用户连续多次点击加载更多按键。 通过小程序中的按键组件的 loading 属性,可以便捷地显示正在加载中的样 式。 // pages/index/index.wxml {{loadMoreText}} 制造静态数据,数量为 2 条,表示已无更多。 // pages/index/index.js const lastPage = [{id: '5',title: '超新约全书',description: '一个以摧残人为乐趣的上帝',cover: 'https://xxx.xxx.xxx.jpg' }, {id: '6',title: '2001太空漫游 2001',description: '现代悬疑影片技术的里程碑',cover: 'https://xxx.xxx.xxx.jpg' }] // pages/index/index.js let isEnd = false Page({data: {articles: [],loading: false,loadMoreText: '加载更多'},onLoad: function() {this.getArticles(true)},loadMore: function(event) {this.getArticles()} }) 重新实现 getArticles 方式: getArticles: function(isFirstPage) {if (!isEnd && !this.data.loading) {this.setData({ loading: true })setTimeout(() => {if (isFirstPage) {this.setData({articles: firstPage,loading: false})} else {this.setData({articles: firstPage.concat(lastPage),loading: false})if (lastPage.length < pageLimit) {isEnd = truethis.setData({ loadMoreText: '已无更多' })}}}, 1000)} } 每一次获取到新的页面时,我们通过 Array.concat 方式,将它拼接入到之前 的文章列表前面,然后进行 setData。
这样,我们就实现了加载更多文章列表 的功能了。 显示未读与已读状态 有时候,我们也会忘了自己曾阅读过某篇文章,虽然对于大多数人来说这并不 常见,但对于我这些记忆力不是挺好的人来说,这事情发生的频度还是很大 的。所以,我有必要为自己提高一下用户体验。 当用户在首页点击了某个文章列表项后,我们须要将该列表项的文字颜色设置 为白色,正常情况下,他们是红色的。 为了标记用户是否读过某篇文章,我们在用户点击某篇文章的同时,将其文章 id 保存上去。类似这些数据,我们通常不会保存到数据库,因为有点牛刀小试 了,我们只须要将数据保存到本地即可,只要用户不清缓存数据就不会遗失, 即使请了缓存,丢失了数据,也并不会导致多大的损失。 接下来,让我们瞧瞧怎样在小程序中使用本地储存吧。 本地储存的使用及同异步插口的差别 小程序提供了两种方式让我们操作本地储存,包括同步操作与异步操作。 同步: §添加:wx.setStorageSync 获取:wx.getStorageSync 异步: 添加:wx.setStorage 获取:wx.getStorage 关于同步和异步的区别,有个事例挺好地做了说明: 同步调用就是你喊你的同事喝水,你同学在忙,你就仍然在那等,等你同学忙 完了,你们一起去。
异步调用就是你喊你的同事喝水,你同学说晓得了,待会忙完去找你,你就去 做别的事了。 可见,异步调用的效率会比同步好,但应用到编程语言上,就要稍稍多写点代 码了。以获取 key 为 id 的数据为例,我们讲解一下这两种调用方法分别是如 何使用的: 同步: var value = wx.getStorageSync('key') console.log('id', value) 异步: wx.getStorage({key: 'id',success: function(res) {console.log('id', res.data)} }) 可以看见,使用同步操作相对于异步操作更简单也更简约一点,异步操作主要 是用在对性能有要求的场景,这里为了操作便捷,我们将使用同步操作来实现 该功能。 功能实现 在后面的基础上,我们在每位 article 数据中再加入一个 isReaded 数组,用 于标示该文章是否已被阅读。 当用户在首页点击某一篇文章时,要先判别 storage中是否有 key 为READED_ARTICLES(字符串常量)的记录,如果没有,则创建该记录, 其 value 为链表,并将该文章的 idpush 到该字段中。
如果已存在名为 READED_ARTICLES 的记录,则判定该文章的 id 是否包含在其 value(数 组类型)中,不存在则 push,存在则跳过。 // pages/index/index.js toDetailPage: function(e) {let id = e.currentTarget.dataset.idlet readedArticles = wx.getStorageSync(READED_ARTICLES)if (!readedArticles) {wx.setStorageSync(READED_ARTICLES, [id])} else if(readedArticles.indexOf(id) == -1) {readedArticles.push(id)wx.setStorageSync(READED_ARTICLES, readedArticles)}this.setData({articles: this.addReadStatus(this.data.ar ticles)})wx.navigateTo({url: `../detail/index?id=${id}`}) } 上面,我们在 setData的时侯不再使用 this.data.articles ,而是使 用this.addReadStatus(this.data.articles) ,addReadStatus 是我们自己定义的 方法,它接受一个 articles 参数,并按照 localStorage中保存的,已阅读过 的文章的 id,来给文章添加 isReaded 属性。
addReadStatus: function(articles) {let readedArticles = wx.getStorageSync(READED_ARTICLES)if (!readedArticles) {return articles}let newArticles = []for (let i = 0; i < articles.length; i++) {let article = Object.assign(articles[i])if (readedArticles.indexOf(article.id) != -1) {article.isReaded = true} else {article.isReaded = false}newArticles.push(movie)}return newArticles } 同时,我们不要忘了另一种情况,当用户重新打开小程序的时侯,我们也须要 给文章加上 isReader 属性。即在 getArticles 方式中的 setData 里使 用addReadStatus,如下: // pages/index/index.js this.setData({articles: oldArticles.concat(this.addReadStatus(data)),loading: false }) 有了 isReader 做标志,我们就不难为不同状态的文章加上不同的款式了。
// pages/index/index.wxml ... 到这儿,我们就完成了一个有点复杂,但又很实用的小功能了。接下让我们再 开发一个可以提升我们应用活跃度的功能——分享。 文章分享 分享基本上是每位应用都必不可少的功能,在小程序中实现一个常规的分享功 能,是再简单不过了,但简单也意味着不够非常,如果你想要获得更多的分享 量,那就须要在分享方法上下点工夫了。 接下来我将介绍两种形式来实现分享功能,一种是最常规的也是最普通的,点 击右上角的菜单,使用弹出的菜单项中分享菜单项。 另一种方式是自定义一个 分享按键,用户点击按键后即弹出确认分享框,该方式灵活性强,并且比第一 种少操作了一步。好了,让我们开始吧。 无论使用菜单还是自定义控件来实现分享功能,都须要我们在要加入分享功能 的 Page 中实现 onShareAppMessage(options) 方法,在该方式中设置该页 面的转发信息和转发成功或失败的反弹。 Page({onShareAppMessage: function (options) {return {title: '标题',path: '/page/user?id=123',success: function() {// 转发成功console.log(`share by: ${options.from}`)},fail: function() {// 转发失败}}} }) options 参数说明: 1. 使用菜单中的分享功能 使用菜单中的分享功能,我们只需在 page中定义onShareAppMessage 即 可。
// pages/detail/index.js onShareAppMessage: function (options) {return {title: this.data.article.title,imageUrl: this.data.article.poster,path: `pages/detail/index?id=${id}`,} } 2. 自定义分享组件 与使用菜单中的分享功能一样,我们同样要实现onShareAppMessage 方法。 自定义分享组件,我们须要利用到 button 组件的open-type 属性,将其设置 为 "share" ,当点击按键便会弹出分享确认框。 分享 到这儿很多人会有疑虑了,难道我们必须使用 button 组件能够实现自定义分 享组件吗?button 组件这么丑…… 很遗憾,我们的确须要使用 button 组件,但也不用这么沮丧,虽然我们要使 用 button 组件,但谁说 button 组件就丑了呢,我们还有 css的嘛。现在, 我们就利用 css,实现一个漂浮于屏幕右下角,圆形的,带背景图片的分享按 钮。
// pages/detail/index.wxss .article-share-btn {position: fixed;right: 30rpx;bottom: 30rpx;width: 70rpx;height: 70rpx;background-image: url("https://cloud-minapp- 1131./1eNFg1HrAVjWkrNQ.png");background-size: 70rpx 70rpx;border: 0 !important; } 最后的疗效如下: 到这儿,我们就搭建起了一个文章展示小程序基本的页面框架,同时为其加上 了一些很实用的小功能,你也可以阅读小程序官方文档,找找这些你感兴趣的 功能的小程序 API,来丰富你的小程序功能吧。