项目地址:github.com/wudiufo/WeC…
完成效果展示:www.bilibili.com/video/av488…
小爱心是否点赞组件 components/like
思路:
like 默认为 false,显示空心小爱心
触摸执行tap:onLike 方法,因为 this.setData({count:count,like:!like})是异步的,先执行count = like ? count - 1 : count + 1,这时like还是false,执行count+1。然后在执行this.setData()方法,将like变为true,显示实心小爱心。
let behavior = like ? 'like' : 'cancel' //自定义事件 this.triggerEvent('like', { behavior: behavior }, {})复制代码自定义事件like,当like为真时,behavior为like,在models/like.js中,let url = behavior === 'like' ? 'like/cancel' : 'like',因为behavior === 'like'为真,就调用服务器接口'like/cancel',相反就调用like接口。
刚开始实心就调用'like/cancel'接口,空心就调用'like'接口
底部左右切换组件 components/navi
思路:
在navi/index.js中:
先定义哪些数据是外部传来的数据,哪些数据是私有数据properties: {//外部传来的数据 title: String, first: Boolean, //如果是第一期向右的箭头就禁用,默认是false latest: Boolean //如果是最新的一期向左的箭头就禁用,默认是false }, data: {//私有数据 disLeftSrc: './images/triangle.dis@left.png', leftSrc: './images/triangle@left.png', disRightSrc: './images/triangle.dis@right.png', rightSrc: './images/triangle@right.png' },复制代码左箭头:
在navi/index.wxml中
image bind:tap="onLeft" class="icon" src="{{latest?disLeftSrc:leftSrc}}"/src显示图片规则:如果是最新的期刊,就显示向左禁用状态disLeftSrc箭头;如果不是最新一期的期刊,就显示向左可用状态leftSrc箭头
为图片绑定触摸事件onLeft,在
navi/index.js中:在 methods 中:如果不是最新的期刊,就继续绑定自定义事件leftonLeft: function(event) { //不是最新一期 if (!this.properties.latest) { this.triggerEvent('left', {}, {}) } },复制代码
右箭头:
在navi/index.wxml中
image bind:tap="onRight" class="icon" src="{{first?disRightSrc:rightSrc}}"/src显示图片规则:如果是第一期的期刊,就显示向右禁用状态disRightSrc箭头;如果不是第一期的期刊,就显示向右可用状态rightSrc箭头
为图片绑定触摸事件onRight,在
navi/index.js中:在 methods 中:如果不是第一期的期刊,就继续绑定自定义事件rightonRight: function(event) { //不是第一期 if (!this.properties.first) { this.triggerEvent('right', {}, {}) } }复制代码
在pages/classic中:
1:在 classic.json 中,注册使用navi自定义组件{ "usingComponents": { "v-like": "/components/like/index", "v-movie": "/components/classic/movie/index", "v-episode": "/components/episode/index", "v-navi": "/components/navi/index" }}2:在 classic.wxml 中:绑定自定义事件left, 获取当前一期的下一期;绑定自定义事件right,获取当前一期的上一期v-navi bind:left="onNext" bind:right="onPrevious" class="nav" title="{{classic.title}}" first="{{first}}" latest="{{latest}}"/ 3:在 classic.js 中:// 获取当前一期的下一期,左箭头onNext: function(evevt) {this._updateClassic('next') },// 获取当前一期的上一期,右箭头onPrevious: function(evevt) { this._updateClassic('previous') },// 重复代码过多,利用函数封装的思想,新建一个函数抽取公共代码// 发送请求,获取当前页的索引,更新数据 _updateClassic: function(nextOrPrevious) { let index = this.data.classic.index classicModel.getClassic(index, nextOrPrevious, (res) = { // console.log(res) this.setData({ classic: res, latest: classicModel.isLatest(res.index), first: classicModel.isFirst(res.index) }) }) }, 4:在 models/classic.js 中:// 当前的期刊是否为第一期,first就变为true,右箭头就显示禁用 isFirst(index) { return index === 1 ? true : false }// 当前的期刊是否为最新的一期,latest就变为TRUE,左箭头就显示禁用// 由于服务器数据还会更新,确定不了最新期刊的索引,所以就要利用缓存机制,将最新期刊的索引存入到缓存中,如果外界传进来的索引和缓存的最新期刊的索一样,latest就变为TRUE,左箭头就显示禁用 isLatest(index) { let latestIndex = this._getLatestIndex() return latestIndex === index ? true : false }// 将最新的期刊index存入缓存 _setLatestIndex(index) { wx.setStorageSync('latest', index) } // 在缓存中获取最新期刊的index _getLatestIndex() { let index = wx.getStorageSync('latest') return index }复制代码优化缓存。解决每次触摸左右箭头都会频繁向服务器发送请求,这样非常耗性能,用户体验极差。解决方法,就是把第一次发送请求的数据都缓存到本地,再次触摸箭头时,会先查找本地缓存是否有数据,有就直接从缓存中读取数据,没有就在向服务器发送请求,这样利用缓存机制大大的提高了用户的体验。(但也有一部分是需要实时更新的,比如是否点赞的小爱心组件,需要每次都向服务器发送请求获取最新数据)
在 models/classic.js 中:
1:// 设置缓存中的key 的样式,classic-1这种样式 _getKey(index) { let key = `classic-${index}` return key }2: // 因为getPrevious,getNext实现代码相似,所以为了简化代码可以合并为一个函数 // 缓存思路:在缓存中寻找key,找不到就发送请求 API,将key写入到缓存中。解决每次都调用Api向服务器发请求,耗费性能 // 在缓存中,确定key getClassic(index, nextOrPrevious, sCallback) { //0: 是next,触摸向左箭头获取下一期,触摸向右箭头否则获取上一期 let key = nextOrPrevious === 'next' ? this._getKey(index + 1) : this._getKey(index - 1) //1:在缓存中寻找key let classic = wx.getStorageSync(key) //2:如果缓存中找不到key,就调用服务器API发送请求获取数据 if (!classic) { this.request({ url: `classic/${index}/${nextOrPrevious}`, success: (res) = { //将获取到的数据设置到缓存中 wx.setStorageSync(this._getKey(res.index), res) //再把获取到的数据返回,供用户调取使用 sCallback(res) } }) } else { //3:如果在缓存中有找到key,将缓存中key对应的value值,返回给用户,供用户调取使用 sCallback(classic) } }--------------------------------------------------------------------------------// 获取最新的期刊利用缓存机制进一步优化 //获取最新的期刊 getLatest(cb) { this.request({ url: 'classic/latest', success: (res) = {//将最新的期刊index存入缓存,防止触摸向左箭头时,没有设置latest的值,左箭头会一直触发发送请求找不到最新的期刊报错 this._setLatestIndex(res.index) //再把获取到的数据返回,供用户调取使用 cb(res) // 将最新的期刊设置到缓存中,先调取 this._getKey() 方法,为最新获取的期刊设置key值,调用微信设置缓存方法将key,和对应的value值res存进去 let key = this._getKey(res.index) wx.setStorageSync(key, res) } }) }复制代码处理是否点赞小爱心组件的缓存问题:他不需要缓存,需要实时获取最新数据
在 models/like.js 中:
//编写一个获取点赞信息的方法,从服务器获取最新点赞信息的数据 // 获取点赞信息 getClassicLikeStatus(artID, category, cb) { this.request({ url: `classic/${category}/${artID}/favor`, success: cb }) }复制代码在 pages/classic/classic.js 中:
//设置私有数据初始值data: { classic: null, latest: true, first: false, likeCount: 0,//点赞的数量 likeStatus: false //点赞的状态 }, // 在classic.wxml中: v-like class="like" bind:like="onLike" like="{{likeStatus}}" count="{{likeCount}}"/ // 编写一个私有方法获取点赞信息 // 获取点赞信息 _getLikeStatus: function(artID, category) { likeModel.getClassicLikeStatus(artID, category, (res) = { this.setData({ likeCount: res.fav_nums, likeStatus: res.like_status }) }) }, //生命周期函数--监听页面加载 onLoad: function(options) { classicModel.getLatest((res) = { console.log(res) // this._getLikeStatus(res.id, res.type) //不能这样写,会多发一次favor请求,消耗性能 this.setData({ classic: res, likeCount: res.fav_nums, likeStatus: res.like_status }) })复制代码在 classic/music/index.js 中:
解决切换期刊时,其他期刊也都是播放状态的问题。应该是,切换期刊时音乐就停止播放,回到默认不播放状态
利用组件事件的通信机制,小程序中只有父子组件
在 components/classic/music/inddex.js 中:
方案一:
//利用组件生命周期,只有 wx:if 才可以从头掉起组件生命周期// 组件卸载的生命周期函数 // 组件卸载音乐停止播放,但这时不生效是因为,在classic.wxml中用的是hidden,应改为if detached: function(event) { mMgr.stop() }, // 在 pages/classic/classic.wxml 中 // v-music wx:if="{{classic.type===200}}" img="{{classic.image}}" content="{{classic.content}}" src="{{classic.url}}" title="{{classic.title}}"/ 复制代码知识点补充:
wx:if vs hidden,和Vue框架的v-if和v-show 指令一样:wx:if 》他是惰性的,如果初始值为false框架什么也不做,如果初始值为true框架才会局部渲染。true或false的切换就是从页面中局部加入或移除的过程。wx:if 有更高的切换消耗,如果在运行时条件不大可能改变则 wx:if 较好。生命周期会重新执行。hidden 》组件始终会被渲染,只是简单的控制显示与隐藏。hidden 有更高的初始渲染消耗。如果需要频繁切换的情景下,用 hidden 更好。生命周期不会重新执行。
方案二:(推荐使用)
解决切换期刊时音乐可以当做背景音乐一直播放,而其他的期刊是默认是不播放状态
在 components/classic/music/inddex.js 中:
//为了保证期刊在切换时,背景音乐可以一直播放,就要去除掉 mMgr.stop() 事件方法detached: function(event) { // mMgr.stop() //为了保证背景音乐的持续播放就不能加stop }, // 监听音乐的播放状态,如果当前页面没有播放的音乐,就设置playing为false。如果当前页面的音乐地址classic.url和当前正在播放的音乐的地址一样,就让播放状态为true_recoverStatus: function() { if (mMgr.paused) { this.setData({ playing: false }) return } if (mMgr.src === this.properties.src) { this.setData({ playing: true }) } }, // 监听播放状态,总控开关就可以控制播放状态,结局总控开关和页面不同步问题 _monitorSwitch: function() { console.log('monitorSwitch背景音频', '触发3') // 监听背景音频播放事件 mMgr.onPlay(() = { this._recoverStatus() console.log('onPlay ' + this.data.playing) }) // 监听背景音频暂停事件 mMgr.onPause(() = { this._recoverStatus() console.log('onPause ' + this.data.playing) }) // 关闭音乐控制台,监听背景音频停止事件 mMgr.onStop(() = { this._recoverStatus() console.log('onStop ' + this.data.playing) }) // 监听背景音频自然播放结束事件 mMgr.onEnded(() = { this._recoverStatus() console.log('onEnded ' + this.data.playing) }) }, //调用生命周期函数,每次切换都会触发attached生命周期 // 在组件实例进入页面节点树时执行 // hidden,ready,created都触发不了生命周期函数 attached: function(event) { console.log('attach实例进入页面', '触发1') this._monitorSwitch() this._recoverStatus() },复制代码播放动画旋转效果制作:
在 components/classic/music/index.wxss 中:
//定义帧动画用CSS3.rotation { -webkit-transform: rotate(360deg); animation: rotation 12s linear infinite; -moz-animation: rotation 12s linear infinite; -webkit-animation: rotation 12s linear infinite; -o-animation: rotation 12s linear infinite;}@-webkit-keyframes rotation { from { -webkit-transform: rotate(0deg); } to { -webkit-transform: rotate(360deg); }}复制代码补充css3知识点:
》使用CSS3开启GPU硬件加速提升网站动画渲染性能:为动画DOM元素添加CSS3样式-webkit-transform:transition3d(0,0,0)或-webkit-transform:translateZ(0);,这两个属性都会开启GPU硬件加速模式,从而让浏览器在渲染动画时从CPU转向GPU,其实说白了这是一个小伎俩,也可以算是一个Hack,-webkit-transform:transition3d和-webkit-transform:translateZ其实是为了渲染3D样式,但我们设置值为0后,并没有真正使用3D效果,但浏览器却因此开启了GPU硬件加速模式。》这种GPU硬件加速在当今PC机及移动设备上都已普及,在移动端的性能提升是相当显著地,所以建议大家在做动画时可以尝试一下开启GPU硬件加速。
》适用情况通过-webkit-transform:transition3d/translateZ开启GPU硬件加速的适用范围:
使用很多大尺寸图片(尤其是PNG24图)进行动画的页面。页面有很多大尺寸图片并且进行了css缩放处理,页面可以滚动时。使用background-size:cover设置大尺寸背景图,并且页面可以滚动时。(详见:coderwall.com/p/j5udlw)编写大量DOM元素进行CSS3动画时(transition/transform/keyframes/absTop&Left)使用很多PNG图片拼接成CSS Sprite时》总结 通过开启GPU硬件加速虽然可以提升动画渲染性能或解决一些棘手问题,但使用仍需谨慎,使用前一定要进行严谨的测试,否则它反而会大量占用浏览网页用户的系统资源,尤其是在移动端,肆无忌惮的开启GPU硬件加速会导致大量消耗设备电量,降低电池寿命等问题。
在 components/classic/music/index.wxml 中:
//为图片加上播放就旋转的类,不播放 就就为空字符串image class="classic-img {{playing?'rotation':''}}" src="{{img}}"/image复制代码用 slot 插槽,解决在公用组件中可以加入其他修饰内容问题。其实就是,在定义公用组件时,用 slot 命名插槽占位,在父组件调用时可以传递需要的内容补位。和Vue的指令 v-slot 相似。
在 components/tag/index.js 中:
//在 Component 中加入// 启用slot options: { multipleSlots: true },复制代码在定义的公共组件 components/tag/index.wxml 中:
//定义几个命名插槽,供父元素占位使用view class="container tag-class" slot name="before"/slot text{{text}}/text slot name="after"/slot/view复制代码在 pages/detail/detail.json 中:
//注册并使用组件{ "usingComponents": { "v-tag": "/components/tag/index" }}复制代码在 pages/detail/detail.wxml 中:
//使用组件v-tag,补位命名插槽v-tag tag-class="{{index===0?'ex-tag1':''||index===1?'ex-tag2':''}}" text="{{item.content}}" text class="num" slot="after"{{'+'+item.nums}}/text/v-tag复制代码在 pages/detail/detail 中,解决评论内容自定义组件 v-tag 评论前两条显示两种颜色的做法:
第一种方法:(推荐使用)
在 pages/detail/detail.wxss 中:
/* v-tag是自定义组件,不能使用css3,在微信小程序中,只有内置组件才可以用css3 *//*用CSS hack方式给自定义组件加样式*/.comment-containerv-tag:nth-child(1)view { background-color: #fffbdd;}.comment-containerv-tag:nth-child(2)view { background-color: #eefbff;}复制代码第二种方法:
定义外部样式方法,像父子组件传递属性一样,传递样式类
在 detail.wxss 中:
/* 定义外部样式 */.ex-tag1 { background-color: #fffbdd !important;}.ex-tag2 { background-color: #eefbff !important;}复制代码在 detail.wxml 中:
/*将自定义的样式类通过属性传值的方式传递给自定义子组件v-tag */v-tag tag-class="{{index===0?'ex-tag1':''||index===1?'ex-tag2':''}}" text="{{item.content}}" text class="num" slot="after"{{'+'+item.nums}}/text/v-tag复制代码在 components/tag/index.js 中:
//将外部传进来的样式写在Component中,声明一下// 外部传进来的css,样式 externalClasses: ['tag-class'],复制代码在 components/tag/index.wxml 中:
// 把父组件传递过来的类 tag-calss 写在 class 类上view class="container tag-class" slot name="before"/slot text{{text}}/text slot name="after"/slot/view复制代码解决服务器返回的内容简介有 换行符的问题:
原因:
是因为服务器返回的原始数据 是
,经过转义就变成而
在text文本标签中默认转义为换行
解决方法:
WXS:WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构。和Vue 中的 Vue.filter(过滤器名,过滤器方法) 很相似。WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致。由于运行环境的差异,在 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。在 android 设备上二者运行效率无差异。
在 utils/filter.wxs 中:
// 定义过滤器函数,处理服务器返回的数据,将 变成 // 会打印两次,undefined和请求得到的数据,因为第一次初始时text为null,发送请求得到数据后调用setData更新数据一次var format = function(text) { console.log(text) if (!text) { return } var reg = getRegExp('\', 'g') return text.replace(reg, '')}module.exports.format = format复制代码在 pages/detail/detail.wxml 中:
//引入wxs src="../../utils/filter.wxs" module="util"///在需要过滤的数据中使用text class="content"{{util.format(book.summary)}}/text复制代码解决解决服务器返回的内容简介首行缩进的问题:
在 pages/detail/detail.wxss 中:
//对需要缩进的段落前加以下的类,但这时只有第一段缩进.content { text-indent: 58rpx; font-weight: 500;}复制代码在 utils/filter.wxs 中:
//用转义字符 作为空格,但这时小程序会以 样式输出,不是我们想要的效果var format = function(text) { if (!text) { return } var reg = getRegExp('\', 'g') return text.replace(reg, ' ')}module.exports.format = format复制代码在 pages/detail/detail.wxml 中:
//加入属性 decode="{{true}}",首行缩进问题解决text class="content" decode="{{true}}"{{util.format(book.summary)}}/text复制代码解决短评过多让其只显示一部分的问题:
在 utils/filter.wxs 中:
//添加一个限制短评长度的过滤器,并导出// 限制短评的长度的过滤器var limit = function(array, length) { return array.slice(0, length)}module.exports = { format: format, limit: limit};复制代码在 pages/detail/detail.wxml 中:
wxs src="../../utils/filter.wxs" module="util"/view
版权声明
即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。














