感觉上章没有讲完,这章继续讲,两章配合阅读。
1.Template模板
当一块区域需要在多个地方使用的时候,可以把这个区域做成一个模版,在使用的时候调用这个模版即可,这样即减少重复代码的编写,又易于维护,让代码整洁。
模板代码的编写
<template name='postItem'>
<view class='box'>
<view class='title-box'>
<text class='title'>{{item.title}}</text>
<text class='date'>{{item.date}}</text>
</view>
<image src='{{item.imgUrl}}' class='cont-image'></image>
<text class='content'>{{item.content}}</text>
</view>
</template>
为了方便管理我们的模版文件,可以在pages文件夹下面,新建一个模版文件,里面存放编写的模版;我们要使用标签template来包裹代码,并且要给其加一个name属性,当然也要编写对应的样式,和正常写样式的方法没有区别。
新建的模版文件里面只能存放模版的wxml和wxss文件,目前小程序的模版文件里面放入其他的文件类型不起作用,不能进行复用,虽然不报错,比如js文件。
template标签只是一种占位符,告诉编译器这个地方是加载模版代码的,当页面编译完成,这个标签就消失,所以要对与模版文件响应事件,我们就需要在加载模版文件的区域,使用一个可以添加事件的标签把其模版文件包裹起来,比如:view / block
<view bindtap=” ....”> template模版代码 </view>
使用模板代码
编写好了模版代码,怎么样在需要加载模版代码的地方使用呢?
首先,模版代码是在其他文件夹中,使用它,就必然要引入该模版文件,
一般在对应要引入模版文件的文件开头部分引入:
// 在对应的wxml文件中开头引入
<import src = '模版wxml文件路径' />
// 在对应的wxss文件中开头引入,注意末尾的分号
@import '模版wxss文件路径' ;
然后在引入模版文件的wxml文件,适当位置使用
<template is = '模版的名字' data="{{要绑定到模版的数据}}"
到此,简单的模版引入和模版数据的绑定就完成,当我们要循环模版的时候,只需要如下,在外面加一层标签即可:
// bolck标签的作用是作用事件到template模版上面
<block wx:for=" 服务器获取的数据集" wx:for-item='item' wx:for-index='index' >
<template is = '模版的名字' data="{{要绑定到模版的数据,一般是服务器获取的一个数据对象item}}"
</block>
2.缓存实践:文章收藏和文章阅读数增加
收藏和Toast提示:
var postsData = require('../../../data/posts-data.js')
var app = getApp();
Page({
data: {
isPlayingMusic: false
},
onLoad: function (option) {
//获取传递过来的id
var postId = option.id;
//下面读取缓存需要用postId,我们利用currentPostID存储到data里面,这样整个文件就可以都使用这个值了
this.data.currentPostId = postId;
//把根据id获取的数据放入到data中
var postData = postsData.postList[postId];
this.setData({
postData: postData
})
/*
posts_collected缓存数据可能类似如下形式(key-value形式,true代表收藏,false方便没收藏)
posts_collected = {
0:"true"
1:"true",
2:"false",
3:"true",
...
}
*/
//读取所有的缓存,key为posts_collected,value是一个对象,为{0:'',1:",2:''};
var postsCollected = wx.getStorageSync('posts_collected')
if (postsCollected) {
//根据点击的postId获取对应页面的缓存
var postCollected = postsCollected[postId]
if (postCollected){ //如果缓存存在,我们把collected缓存对应ID的值,即true/false
this.setData({
collected: postCollected
})
}
}
else {//如果不存在缓存
var postsCollected = {};
//postsCollected缓存中对应postID设置为false
postsCollected[postId] = false;
//如果不存在缓存,则进行设置
wx.setStorageSync('posts_collected', postsCollected);
}
},
//点击已收藏/未收藏图片时函数,主要是针对缓存的处理,根据缓存中collectd的值,以及设置collected的值
onColletionTap: function (event) {
this.getPostsCollectedSyc();
},
//同步
getPostsCollectedSyc: function () {
//postCollected是一个对象{0:'',1:'',2:'',...}
var postsCollected = wx.getStorageSync('posts_collected');
var postCollected = postsCollected[this.data.currentPostId];
// 收藏变成未收藏,未收藏变成收藏,但是这只是改变值,没有赋值到data中(showToast赋值到data中)
postCollected = !postCollected;
//更新缓存对象中当前数据(currentPostId)的key/value值
postsCollected[this.data.currentPostId] = postCollected;
//postsCollected是一个缓存对象{0:'',1:'',2:'',...}
//postCollected是代表收藏/没收藏的true/false
this.showToast(postsCollected, postCollected);
},
// 更新缓存 收藏成功/取消成功的提示
showToast: function (postsCollected, postCollected) {
// 更新文章是否的缓存值,postCollected是一个对象{0:'',1:'',2:'',...}
wx.setStorageSync('posts_collected', postsCollected);
// 更新数据绑定变量collected为true/false,从而实现切换图片
this.setData({
collected: postCollected
})
wx.showToast({
title: postCollected ? "收藏成功" : "取消成功",
duration: 1000,
icon: "success",
success:function(){
},
fail:function(){
},
complete:function(){
}
})
},
})
播放音乐:
var postsData = require('../../../data/posts-data.js')
var app = getApp();
Page({
data: {
isPlayingMusic: false
},
onLoad: function (option) {
//获取传递过来的id
var postId = option.id;
//下面读取缓存需要用postId,我们利用currentPostID存储到data里面,这样整个文件就可以都使用这个值了
this.data.currentPostId = postId;
//把根据id获取的数据放入到data中
var postData = postsData.postList[postId];
this.setData({
postData: postData
})
//数据的缓存和获取(例如收藏状态,已经收藏过要保持收藏状态)
/*
posts_collected缓存数据可能类似如下形式(key-value形式,true代表收藏,false方便没收藏)
posts_collected = {
0:"true"
1:"true",
2:"false",
3:"true",
...
}
*/
//读取所有的缓存,key为posts_collected,value是一个对象,为{0:'',1:",2:''};
var postsCollected = wx.getStorageSync('posts_collected')
if (postsCollected) {
//根据点击的postId获取对应页面的缓存
var postCollected = postsCollected[postId]
if (postCollected){ //如果缓存存在,我们把collected缓存对应ID的值,即true/false
this.setData({
collected: postCollected
})
}
}
else {//如果不存在缓存
var postsCollected = {};
//postsCollected缓存中对应postID设置为false
postsCollected[postId] = false;
//如果不存在缓存,则进行设置
wx.setStorageSync('posts_collected', postsCollected);
}
//把全局的App变量打印出来(测试)
console.log(app.globalData);
//g_isPlayingMusic作用主要是新闻详情页的音乐播放和停止,要与播放器音乐播放与停止保持同步(更换图标等同步),
//需要一个全局变量,因为一个页面返回,变量就会重新初始化。
//页面返回时g_isPlayingMusic如果为true而且g_currentMusicPostId为当前postId,则此时isPlayingMusic为真
//定义全局变量g_currentMusicPostID表示哪一个音乐正在被播放,因为A页面你把g_isPlayingMusic
//设置为true,当你打开B页面此时还为true,这样isPlayingMusic也为true,所以我们要判断正在播放的是否和当前一直
if (app.globalData.g_isPlayingMusic && app.globalData.g_currentMusicPostId
=== postId) {
this.setData({
isPlayingMusic: true
})
}
this.setMusicMonitor();
},
//在这改变g_isPlayingMusic的值
setMusicMonitor: function () {
//点击播放图标和总控开关都会触发这个函数
var that = this;
//监听音乐播放
wx.onBackgroundAudioPlay(function (event) {
// 播放当前页面音乐才改变图标
that.setData({
isPlayingMusic: true
})
app.globalData.g_isPlayingMusic = true;
app.globalData.g_currentMusicPostId = that.data.currentPostId;
});
//监听音乐暂停
wx.onBackgroundAudioPause(function () {
that.setData({
isPlayingMusic: false
})
//g_isPlayingMusic设置为false
app.globalData.g_isPlayingMusic = false;
app.globalData.g_currentMusicPostId = null;
});
//监听音乐停止
wx.onBackgroundAudioStop(function () {
that.setData({
isPlayingMusic: false
})
//g_isPlayingMusic设置为false
app.globalData.g_isPlayingMusic = false;
app.globalData.g_currentMusicPostId = null;
});
},
//点击已收藏/未收藏图片时函数,主要是针对缓存的处理,根据缓存中collectd的值,以及设置collected的值
onColletionTap: function (event) {
this.getPostsCollectedSyc();
},
//同步
getPostsCollectedSyc: function () {
//postCollected是一个对象{0:'',1:'',2:'',...}
var postsCollected = wx.getStorageSync('posts_collected');
var postCollected = postsCollected[this.data.currentPostId];
// 收藏变成未收藏,未收藏变成收藏,但是这只是改变值,没有赋值到data中(showToast赋值到data中)
postCollected = !postCollected;
//更新缓存对象中当前数据(currentPostId)的key/value值
postsCollected[this.data.currentPostId] = postCollected;
//postsCollected是一个缓存对象{0:'',1:'',2:'',...}
//postCollected是代表收藏/没收藏的true/false
this.showToast(postsCollected, postCollected);
},
// 更新缓存 收藏成功/取消成功的提示
showToast: function (postsCollected, postCollected) {
// 更新文章是否的缓存值,postCollected是一个对象{0:'',1:'',2:'',...}
wx.setStorageSync('posts_collected', postsCollected);
// 更新数据绑定变量collected为true/false,从而实现切换图片
this.setData({
collected: postCollected
})
wx.showToast({
title: postCollected ? "收藏成功" : "取消成功",
duration: 1000,
icon: "success",
success:function(){
},
fail:function(){
},
complete:function(){
}
})
},
onMusicTap: function (event) {
//获取当前ID
var currentPostId = this.data.currentPostId;
//通过postData获取音乐url、title和coverImg
var postData = postsData.postList[currentPostId];
var isPlayingMusic = this.data.isPlayingMusic;
if (isPlayingMusic) {
wx.pauseBackgroundAudio();
this.setData({
isPlayingMusic: false
})
// app.globalData.g_currentMusicPostId = null;
app.globalData.g_isPlayingMusic = false;
}
else {
wx.playBackgroundAudio({
dataUrl: postData.music.url,
title: postData.music.title,
coverImgUrl: postData.music.coverImg,
})
this.setData({
isPlayingMusic: true
})
app.globalData.g_currentMusicPostId = this.data.currentPostId;
app.globalData.g_isPlayingMusic = true;
}
},
/*
* 定义页面分享函数
*/
onShareAppMessage: function (event) {
return {
title: '离思五首·其四',
desc: '曾经沧海难为水,除却巫山不是云',
path: '/pages/posts/post-detail/post-detail?id=0'
}
}
})
3.小程序中的block
block常用于列表循环(如下图所示)
<!--pages/posts/post.wxml-->
<import src="post-item/post-item-template.wxml" />
<block wx:for="{{posts_key}}" wx:for-item="item">
<!--template和block都会在编译后,消失,也就是源代码中没有-->
<view catchtap='onPostTap' data-postID='{{item.postId}}'>
<!--...相当于把之前一个item里面的数据进行展开和平铺了,所以模板里面不需要item了,因为是item里面的内容了-->
<template is="postItem" data="{{...item}}"></template>
</view>
</block>
//post-item-template.wxml
<template name="postItem">
<view class="post-container">
<view class="post-author-date">
<image class="post-author" src="{{avatar}}"></image>
<text class="post-date">{{date}}</text>
</view>
<text class="post-title">{{title}}</text>
<image class="post-image" src="{{imgSrc}}"></image>
<text class="post-content">{{content}}</text>
<view class="post-like">
<image class="post-like-image" src="/images/icon/chat.png"></image>
<text class="post-like-font">{{collection}}</text>
<image class="post-like-image" src="/images/icon/view.png"></image>
<text class="post-like-font">{{reading}}</text>
</view>
</view>
</template>
view和block
两者的区别是,<view>
是一个组件,会在页面上做渲染;<block>
不是一个组件,它仅仅是一个包装元素,只接受控制属性,不会在页面中做任何渲染。
参考文章:
https://blog.csdn.net/henryhu712/article/details/80464723
https://www.jianshu.com/p/1ca1787990a9
4.事件驱动和数据优先/数据绑定
事件驱动思想, 使用事件驱动来实现模块之间传递参数与解耦.比如在A模块发射一个事件,在B模块监听这个事件,中间使用数据来进行传递即可,像这里就是在总模块里面监听子模块里面的音乐播放状态。
数据优先思想,改变UI,只需要改变数据绑定的对应的数据就可以,这点和angular,IOS中的RAC,响应式编程思想类似,数据绑定优势还有,一个数据可以绑定多个事件,这样只需要在JS文件里面写上对应的多个事件即可,不像jQuery一样要获取多个dom元素才能进行多个事件描述,可能利于做单元测试。w
5.页面关闭状态丢失和全局变量设置g_isPlayingMusic
var postsData = require('../../../data/posts-data.js')
var app = getApp();
Page({
data: {
//音乐的暂停/启动状态
isPlayingMusic: false
},
onLoad: function (option) {
//获取传递过来的id
var postId = option.id;
//下面读取缓存需要用postId,我们利用currentPostID存储到data里面,这样整个文件就可以都使用这个值了
this.data.currentPostId = postId;
//把根据id获取的数据放入到data中
var postData = postsData.postList[postId];
this.setData({
postData: postData
})
//isPlayingMusic是记录播放状态,collected是缓存里面true/false
//数据的缓存和获取(例如收藏状态,已经收藏过要保持收藏状态)
/*
posts_collected缓存数据可能类似如下形式(key-value形式,true代表收藏,false方便没收藏)
posts_collected = {
0:"true"
1:"true",
2:"false",
3:"true",
...
}
*/
//读取所有的缓存,key为posts_collected,value是一个对象,为{0:'',1:",2:''};
var postsCollected = wx.getStorageSync('posts_collected')
if (postsCollected) {
//根据点击的postId获取对应页面的缓存
var postCollected = postsCollected[postId]
if (postCollected){ //如果缓存存在,我们把collected缓存对应ID的值,即true/false
this.setData({
collected: postCollected
})
}
}
else {//如果不存在缓存
var postsCollected = {};
//postsCollected缓存中对应postID设置为false
postsCollected[postId] = false;
//如果不存在缓存,则进行设置
wx.setStorageSync('posts_collected', postsCollected);
}
//把全局的App变量打印出来(测试)
console.log(app.globalData);
//作用主要是新闻详情页的音乐播放和停止,要与播放器音乐播放与停止保持同步(更换图标等同步),
//需要一个全局变量,因为一个页面返回,变量就会重新初始化。
//
if (app.globalData.g_isPlayingMusic && app.globalData.g_currentMusicPostId
=== postId) {
this.setData({
isPlayingMusic: true
})
}
this.setMusicMonitor();
},
//在这改变g_isPlayingMusic的值
setMusicMonitor: function () {
//点击播放图标和总控开关都会触发这个函数
var that = this;
//监听音乐播放
wx.onBackgroundAudioPlay(function (event) {
var pages = getCurrentPages();
var currentPage = pages[pages.length - 1];
if (currentPage.data.currentPostId === that.data.currentPostId) {
// 打开多个post-detail页面后,每个页面不会关闭,只会隐藏。通过页面栈拿到到
// 当前页面的postid,只处理当前页面的音乐播放。
if (app.globalData.g_currentMusicPostId == that.data.currentPostId) {
// 播放当前页面音乐才改变图标
that.setData({
isPlayingMusic: true
})
}
// if(app.globalData.g_currentMusicPostId == that.data.currentPostId )
// app.globalData.g_currentMusicPostId = that.data.currentPostId;
}
// 音乐播放了,就把g_isPlayingMusic设置为true
app.globalData.g_isPlayingMusic = true;
});
//监听音乐暂停
wx.onBackgroundAudioPause(function () {
var pages = getCurrentPages();
var currentPage = pages[pages.length - 1];
if (currentPage.data.currentPostId === that.data.currentPostId) {
if (app.globalData.g_currentMusicPostId == that.data.currentPostId) {
that.setData({
isPlayingMusic: false
})
}
}
//g_isPlayingMusic设置为false
app.globalData.g_isPlayingMusic = false;
// app.globalData.g_currentMusicPostId = null;
});
//监听音乐停止
wx.onBackgroundAudioStop(function () {
that.setData({
isPlayingMusic: false
})
//g_isPlayingMusic设置为false
app.globalData.g_isPlayingMusic = false;
// app.globalData.g_currentMusicPostId = null;
});
},
//点击已收藏/未收藏图片时函数,主要是针对缓存的处理,根据缓存中collectd的值,以及设置collected的值
onColletionTap: function (event) {
// this.getPostsCollectedSyc();
this.getPostsCollectedAsy();
},
//异步
getPostsCollectedAsy: function () {
var that = this;
wx.getStorage({
key: "posts_collected",
success: function (res) {
//getStorage成功回调success,res.data就是返回数据,就是缓存
var postsCollected = res.data;
//this.data.currentPostID是对应数据的ID,然后根据ID从缓存数据postCollected中取对应缓存。
//为什么是currentPostId,因为涉及到postId传递的问题,上面我们把currentPostId传递到data中了
var postCollected = postsCollected[that.data.currentPostId];
// 收藏变成未收藏,未收藏变成收藏(postCollected取反)
postCollected = !postCollected;
//取反之后,更新缓存,这个只是更新缓存中value的值,我们还要进行setStorageSync和把data中的collected进行设置
postsCollected[that.data.currentPostId] = postCollected;
//更新文章是否的缓存值,wx.setStorageSync('posts_collected', postsCollected);,这是把key和更新后的value对应起来
//更新数据绑定变量,从而实现切换图片
/*
this.setData({
collected: postCollected
})
*/
//收藏成功/取消成功的提示
that.showToast(postsCollected, postCollected);
}
})
},
//同步
getPostsCollectedSyc: function () {
//postCollected是一个对象{0:'',1:'',2:'',...}
var postsCollected = wx.getStorageSync('posts_collected');
var postCollected = postsCollected[this.data.currentPostId];
// 收藏变成未收藏,未收藏变成收藏,但是这只是改变值,没有赋值到data中(showToast赋值到data中)
postCollected = !postCollected;
//更新缓存对象中当前数据(currentPostId)的key/value值
postsCollected[this.data.currentPostId] = postCollected;
//postsCollected是一个缓存对象{0:'',1:'',2:'',...}
//postCollected是代表收藏/没收藏的true/false
this.showToast(postsCollected, postCollected);
},
showModal: function (postsCollected, postCollected) {
var that = this;
wx.showModal({
title: "收藏",
content: postCollected ? "收藏该文章?" : "取消收藏该文章?",
showCancel: "true",
cancelText: "取消",
cancelColor: "#333",
confirmText: "确认",
confirmColor: "#405f80",
success: function (res) {
if (res.confirm) {
wx.setStorageSync('posts_collected', postsCollected);
// 更新数据绑定变量,从而实现切换图片,根据collected为true/false更新数据
that.setData({
collected: postCollected
})
}
}
})
},
// 更新缓存 收藏成功/取消成功的提示
showToast: function (postsCollected, postCollected) {
// 更新文章是否的缓存值,postCollected是一个对象{0:'',1:'',2:'',...}
wx.setStorageSync('posts_collected', postsCollected);
// 更新数据绑定变量collected为true/false,从而实现切换图片
this.setData({
collected: postCollected
})
wx.showToast({
title: postCollected ? "收藏成功" : "取消成功",
duration: 1000,
icon: "success",
success:function(){
},
fail:function(){
},
complete:function(){
}
})
},
//用户分享触发的函数
onShareTap: function (event) {
var itemList = [
"分享给微信好友",
"分享到朋友圈",
"分享到QQ",
"分享到微博"
];
//showActionSheet是显示操作菜单
wx.showActionSheet({
itemList: itemList,
itemColor: "#405f80",
success: function (res) { //如果成功,则弹出模拟框
// res.cancel 用户是不是点击了取消按钮
// res.tapIndex 数组元素的序号,从0开始
wx.showModal({
title: "用户 " + itemList[res.tapIndex],
content: "用户是否取消?" + res.cancel + "现在无法实现分享功能,什么时候能支持呢"
})
}
})
},
onMusicTap: function (event) {
//获取当前ID
var currentPostId = this.data.currentPostId;
//通过postData获取音乐url、title和coverImg
var postData = postsData.postList[currentPostId];
//获取音乐是启动/暂停状态
var isPlayingMusic = this.data.isPlayingMusic;
if (isPlayingMusic) {//如果是true说明正在播放,我们点击就会暂停,同时还要设置isPlayingMusic记录此时音乐播放状态,方便下次判断
wx.pauseBackgroundAudio();
this.setData({
isPlayingMusic: false
})
// app.globalData.g_currentMusicPostId = null;
//需要一个全局变量,这样不同页面才能共享音乐播放状态
app.globalData.g_isPlayingMusic = false;
}
else {//如果是false,说明停止播放了,我们点击就会播放,同时也要记录此时的音乐播放状态,方便下次进行判断
wx.playBackgroundAudio({
dataUrl: postData.music.url,
title: postData.music.title,
coverImgUrl: postData.music.coverImg,
})
this.setData({
isPlayingMusic: true
})
//播放时候,我们要用全局变量记录哪一首被播放
app.globalData.g_currentMusicPostId = this.data.currentPostId;
app.globalData.g_isPlayingMusic = true;
}
},
/*
* 定义页面分享函数
*/
onShareAppMessage: function (event) {
return {
title: '离思五首·其四',
desc: '曾经沧海难为水,除却巫山不是云',
path: '/pages/posts/post-detail/post-detail?id=0'
}
}
})
本来以为data里面有isPlayingMusic记录播放状态,页面关闭之后应该有状态啊,后来想了想页面进入时候data中isPlayingMusic是false,没有改变,因为我们在页面改变是通过点击事件改变isPlayingMusic状态,当我们重新进入页面时候只会执行onLoad函数,此时isPlayingMusic状态只能是data中设定的。
所以,当我们页面关闭之后,所有的状态都会恢复到初始状态,不符合音乐播放状态的要求,所以,要一个与页面无关的状态保持,当我页面关闭的时候,这个状态依然保持着变量,而当我的应用程序全部关闭之后,这些所有的状态变量会恢复到默认值,全局变量合适App({})。
参考文章:
https://segmentfault.com/a/1190000013298922#articleHeader2
https://segmentfault.com/a/1190000013513917