主要页面
分别是主页流行,书单,书单详情,我的页面(喜欢)
项目gif图部分展示
gif图太大,不好一一做展示
对整个项目做一个回顾和复盘总结,整个项目是由12个组件,4个页面构成,我们就按页面的构成来复盘整个项目
首先是首页(流行):
首页主要由like(喜欢)组件,epsoid(月份),classic(电影,音乐,诗歌)组件构成;
like(喜欢)组件:
1.组件主要是接收外部传过来的2个参数,一个是喜欢的状态,一个是喜欢的数量,拿到后在组件内直接进行渲染
2.重点是点击事件的逻辑要在父组件进行处理,子组件只负责触发点击事件,并且把当前的状态传给父组件,如果放在组件里面进行业务逻辑处理,那这个组件的复用性就会比较差
//喜欢组件的点击事件处理
methods: {
typeoff: function(event) {
//喜欢的数量
let num = this.data.num;
//根据当前like的状态判断是加1还是减1
num = this.data.like ? num - 1 : num + 1;
this.setData({
num: num,
like: !this.properties.like
})
//当前like组件的状态
let behavier = this.properties.like ? 'like' : 'cancel'
//激活事件,把like状态传给父组件
this.triggerEvent('like',{
behavier:behavier
},{})
}
当前月份组件:
1.主要接收外部穿过来的当前书刊序号,接口返回的数据是中文月份,主要实现的方式通过私有组件中的observer属性实现属性计算,并返回对应的月份
2.observer每当接收数值的这个值发生了变化,他就会执行一次
Component({
properties: {
index: {
type: String,
//observer类似于vue中的wacth,每当这个index的值发生改变,这个ob函数就会执行
//无限递归的原因,不断的执行ob函数
observer:function(newVal,odlVal){
let val = newVal < 10? '0'+ newVal : newVal;
this.setData({
_index:val
})
}
},
},
/**
* 页面的初始数据
*/
data: {
arr: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月','十二月'],
year:'',
month:'',
_index:''
},
//第一次加载的时候会执行,在attached加载后就加载onload
attached:function(){
let time = new Date();
this.setData({
year: time.getFullYear(),
month: this.data.arr[time.getMonth()]
})
console.log(this.data);
},
})
页面展示组件(电影,诗歌,音乐)
包括三个子组件音乐,诗歌,电影,他们都有自己的公用属性,img,center属性,后台会根据类容的不同返回不同的type值
在首页接收到返回的数据的时候,通过返回数据中的type值,就可以判断是什么类型
<move wx:if="{{classic.type===100}}" img="{{classic.image}}" centent="{{classic.content}}" />
<music wx:if="{{classic.type===200}}" img="{{classic.image}}" centent="{{classic.content}}" src="{{classic.url}}" />
<essay wx:if="{{classic.type===300}}" img="{{classic.image}}" centent="{{classic.content}}" />
重点:当每次点击的时候都需要发送一次请求,于是想到用本地缓存来进行优化
思路:
1.当每次点击都会发送一次新的请求,把这次的请求数据存在,通过每次请求后返回数据中的index去做为key
2.每次点击的的时候,都会传入一个index到一个方法中,这个方法会根据当前的index的值作为参数去本地缓存中拿数据,如果拿到的数据不为空,就直接把拿到的数据作为参数返回
3.如果拿到的数据为空就发送请求,并且把这次请求存到本次缓存中
class ClassicModel extends HTTP{
//获取最新的样刊,页面加载的时候
getlatest(callback){
this.request({
url: 'classic/latest',
success: (data) => {
callback(data);
//把最新期刊加载的key存入
let key = this._name(data.data.index);
//设置本地存储,把当前最新的的key存入一个data
wx.setStorageSync(key,data);
//把当前的index传入方法,方法会根据传入的最新期刊的index,在缓存中设置一个当前的最新期刊对应的index值
this._setindex(data.data.index);
}
})
}
//每当点击下一页或者上一页
_getClassic(index,name,callback){
// 1.key的名字,如果是向左名字就是class拼接index+1,否则就是减1
// let key = name == 'next' ? 'classic' + (index + 1) : 'classic'+ (index-1);
let key = name == 'next' ? this._name(index + 1) : this._name(index-1);
//取出本地的缓存数据
let classic = wx.getStorageSync(key);
//如果取出来的数据是undefined
if(!classic){
//发送请求
this.request({
url:'classic/'+ index + '/'+ name,
success:(data=>{
//请求成功后
//1.记录当前写入的数据名称
let key = this._name(data.data.index);
//2.写入数据
wx.setStorageSync(key,data)
//3.把data作为参数传入
callback(data)
})
})
//如果取出来的不是空,就直接把取出来的classic数据传入
}else{
callback(classic)
}
}
}
由于加入缓存出现的bug
由于like(点赞)组件是通过首页请求的数据进行赋值,而首页的切换后续拿到的是缓存中的数据,所以like组件渲染的就一直是缓存的数据
解决方案:定义一个新的中间变量去给like组件赋值,第一次请求到的参数作为默认值传给这个新变量,每次like组件被点击的时候,再把新的返回数据覆盖这个新变量
//绑定新的变量,而不是直接绑定之前的变量
<like-cmp bind:like="onlike" like="{{likeType}}" num="{{likeNum}}" />
书单页面
book组件
接收两个参数:
1.Obj:book的基本信息(img,title,text)
2.Num:书本喜欢的人数
直接书写html,css然后父组件发送请求后传参数进行渲染
搜索组件
历史搜索(注释写的非常详细了)
//回車搜索与点击搜索
onSearch(event) {
//组件被点击输入的值
//回车输入的值
let text = event.detail.text || event.detail.value;
//更改页面开关和vulae值双向绑定
this.setData({
searchoff: true,
q: text,
searchBook:[]
})
//加載
this._loadingT();
//获取最新的value值
//将数据读入到缓存中(接收text读入缓存)
keyword.sethistory(text);
//书籍搜索的请求
books.getbook(0, text)
.then(res => {
this.setData({
searchoff: true,
searchBook: res.data.books
})
//没有加载到书籍的
if (this.data.searchBook.length === 0) {
this.setData({
empty: true
})
}
//加载开关
this._loadingF();
})
},
热门搜索
1.请求热门搜索接口后,拿到数据直接渲染短评组件
搜索后的书单懒加载
思路:
页面下拉到底部,
/**
* 页面下拉触底事件的处理函数
*/
onReachBottom: function () {
console.log('我被触发了')
//more是自定义的一个变量,每次触发都进行累加,这样就可以重复触发子组件的observer
this.data.more +=1;
this.setData({
more: this.data.more
})
console.log(this.data.more)
},
主要问题:
如何避免父组件下拉发送多次请求-----通过设置Boolean值来控制请求的状态
如何避免请求数量完成,发送无效请求 ----- 记录当前书籍数量和当前书籍总数量,再加入判断
//监控父组件的下拉事件
more: {
type: String,
observer: function() {
//页面滚到底部
//父组件的值改变触发子组件的observer方法
//请求参数1:从哪里开始搜索(从当前数组的最后一个下标开始)
//请求参数2:搜索的内容
let q = this.data.q;
let length = this.properties.searchBook.length;
//请求的开关(避免无效请求)
console.log(this.properties.searchbuttom)
//如果开关为trun就可以发送请求
if (this.properties.searchbuttom) {
//发送请求后立马改为false
this.properties.searchbuttom = false;
//当前的请求数量是否等于数组返回最大的响应数量
if (this.properties.start == this.properties.total || length ==0) {
//如果相等就不要再发送无效的请求了
//同时把请求的开关改为true
this.properties.searchbuttom = true;
return;
} else {
//正在加载中
this.loadingCenterT();
books.getbook(length, q)
.then(res => {
console.log('我发送了一次请求')
//1.拼接数组返回给searchbook
console.log(res)
let newarr = res.data.books;
//2.数组的拼接
let arr = this.properties.searchBook.concat(newarr)
//3.避免多次无效请求,记录当前的书籍数量和总数量
this.properties.start = res.data.start;
this.properties.total = res.data.total;
//把拼接的数组传给searchBook,渲染页面
this.setData({
searchBook: arr
})
//加载结束
this.loadingCenterF()
this.properties.searchbuttom = true;
})
}
} else {
return
}
}
}
页面优化
书单详情页总共发送了3次请求,但是短评的响应速度较慢,页面渲染的速度不统一,
采用promise.all()方法进行优化
promise.all()接收一个数组,数组中传入promise实例,当数组中所有的promise实例都请求成功后,才能进行调用
与之对应的还有一个promise.race(),他接收的promise实例只要有一个成功后他就会调用
调用成功的参数中通过下标就可以拿到传入的promise实例调用成功后的数据
//获取详情
const getinfo = books.getinfo(id);
//获取点赞
const getfavor = books.getfavor(id);
//获取短评
const getshort = books.getshort(id);
/**
* 接收一个参数array
* array中存放promise实例
* res返回的是实例对象中的res数据,根据对应的下标可以取到
*/
Promise.all([getinfo,getfavor,getshort])
.then(res=>{
this.setData({
info: res[0].data,
favor:res[1].data,
count:res[1].data.fav_nums,
short:res[2].data.comments
})
//停止加载
wx.hideLoading()
})
书单详情页
短评组件:实现输入短评后渲染用户输入的value,如果点击是旧值,就直接进行加1渲染
1.短评组件
1.考虑到要在搜索组件进行复用,在这个组件中启用了插槽,启用插槽,要在组件的js文件中加入这一段代码
//支持插槽
options:{
multipleSlots:true
},
2.当短评被点击的时候,把当前的短评text作为参数传给父组件去做业务处理
onTap(event){
this.triggerEvent('gettext', {
text: this.properties.text
})
},
喜欢页面
用户授权组件
需要用户点击按钮后才能拉起授权弹窗,但是微信提供的默认按钮的样式是固定的
思路:
1.让子组件来拉起授权,button需要传入的参数open-type让父组件给他
2.当子组件拉起授权后,把授权的用户信息通过自定义事件作为参数传给父组件进行业务处理
1.子组件触发
<button bind:getuserinfo="onGetUserInfo" open-type='{{openType}}' plain='{{true}}' class="container">
<slot name="img"></slot>
</button>
methods: {
onGetUserInfo(event) {
//授权的信息
console.log(event.detail)
this.triggerEvent('getuserinfo', event.detail, {})
},
}
2.父组件的业务处理
//子组件监听授权点击函数
onGetUserInfo: function (event) {
//获取用户信息
let userInfo = event.detail.userInfo
//如果信息不为空,则已经授权,把信息传入userinfo中
if (userInfo) {
this.setData({
hasUserInfo: true,
userInfo: userInfo
})
}
},
用户授权检查
onLoad: function (options) {
//查看用户是否已经授权
this.getUserInfo();
//加载用户喜欢的书籍数
this.getMyBook();
//获取用户喜欢的书刊
this.GetMyfavor();
},
//获取用户信息
getUserInfo(){
wx.getSetting({
success: (res) => {
console.log(res);
if (res.authSetting['scope.userInfo']) {
//这里表示已经授权成功了
wx.getUserInfo({
success: (res) => {
this.setData({
hasUserInfo: true,
userInfo: res.userInfo
})
}
})
//没有授权成功的话
} else {
this.setData({
hasUserInfo: false,
})
}
}
})
},
本文正在参加“写编程博客瓜分千元现金”活动,关注公众号“饥人谷”回复“编程博客”参与活动。