微信小程序项目实战(五)
前言
若文章有误,欢迎读者留言反馈💻Installation1
git clone https://github.com/coding327/mymovies.git
详情页
绘制详情页
在pages
目录下新建一个文件夹为detail
,接着在里面鼠标右键选择新建Page
,输入detail
回车即可,这样页面就创建好了
每项电影都是我们的film-item
组件,点击跳转到详情页,只需要更改film-item
组件,把最外层的view
换成navigator
就可以了,并添加上跳转地址url="/pages/detail/detail"
1
2
3<!-- 最外层view标签替换为下面这个 -->
<navigator url="/pages/detail/detail">
</navigator>
为了方便观察详情页,我们去app.json
把pages
中的detail
放最前面,这样应用就会直接显示详情页1
2
3
4
5
6"pages": [
"pages/detail/detail",
"pages/home/home",
"pages/list/list",
"pages/about/about"
],
进入detail.wxml
,把默认内容清除掉,首先放容器view
,根据设计图,划分板块,书写布局和样式代码
代码比较多这里我就不做展示了,大家可以拉取代码仓库进入/pages/detail
文件夹下查看
电影详情页数据的加载
首先看下电影详情的接口地址
https://m.douban.com/rexxar/api/v2/movie/电影ID
,我们可以发现这里传参的方式并不是采用传统?
的方式,而是写在路径里作为路径的一部分,我们叫它Rest
风格
回到api.js
文件中,封装请求api
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// 电影详情页数据
const loadFilmByFilmId = function (filmId) {
return new Promise((resolve, reject) => {
wx.request({
url: URLS.detailUrl + filmId,
success: resolve,
fail: reject
})
}).then(res => {
if (res.statusCode == 200) {
return res.data
} else {
return Promise.reject({
message: res.errMsg
})
}
})
}
// 注意要暴露出去
module.exports = {
showError,
loadHotFilms,
loadLatestFilms,
loadFreeFilms,
loadFilmByFilmId
}
进入app.json
文件中,把首页放到最上面1
2
3
4
5
6"pages": [
"pages/home/home",
"pages/detail/detail",
"pages/list/list",
"pages/about/about"
],
我们要传递电影的id给到详情页,进入film-item.wxml
文件中,url
后面拼接id
,注意film
里面是有个id
的,如果你之前在控制台仔细查看过数据,是会发现有个id
的1
2<navigator url="/pages/detail/detail?filmId={{ film.id }}">
</navigator>
接收filmId
,同时把它保存到我们的数据仓库中,进入detail.js
文件中1
2
3
4
5
6
7
8
9
10
11
12
13/**
* 页面的初始数据
*/
data: {
filmId: '', // 电影id
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.data.filmId = options.filmId
},
导入api
,在onReady
中发请求1
2
3
4
5
6
7
8
9
10
11
12
13
14const api = require('../../api/api')
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
api.loadFilmByFilmId(this.data.filmId).then(data => {
// console.log(data); // 成功返回电影信息
// 将它们保存到数据仓库中,页面上需要直接使用
this.setData({
film: data
})
}).catch(api.showError)
},
展示电影数据,进入detail.wxml
文件中,控制台的AppData
打开,找到数据仓库中的film
,到其中找数据,看哪个字段合适放上去
【注意: 由于接口问题】演员的图片那里目前少数据,所以没更换,然后评论数据是有个单独的数据接口
电影评论数据的加载
分析这个获取评论的接口地址,我们可以发现它前面是
detailUrl
的地址【rest
风格】但是后面又是查询字符串格式,所以我们可以直接使用detailUrl
,没有将它单独写到统一的URLS
中,然后后面拼接参数即可
进入api.js
文件中,封装获取评论数据的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 // 电影详情页评论数据
const loadCommentsByFilmId = function (filmId,params={}) {
return new Promise((resolve, reject) => {
wx.request({
url: URLS.detailUrl+filmId+'/interests',
data:params,
success: resolve,
fail: reject
})
}).then(res => {
if (res.statusCode == 200) {
return res.data
} else {
return Promise.reject({
message: res.errMsg
})
}
})
}
// 注意要暴露出去
module.exports = {
showError,
loadHotFilms,
loadLatestFilms,
loadFreeFilms,
loadFilmByFilmId,
loadCommentsByFilmId
}
回到detail.js
文件中,调用接口获取数据
首先需要在数据仓库定义两个字段1
2
3
4
5
6
7
8
9
10/**
* 页面的初始数据
*/
data: {
filmId: '', // 电影id
film: {}, // 存储电影信息
start: 0, // 评论的起始索引
count: 10, // 评论的条数
comments: [], // 存储电影的评论
},
接着依旧是在onReady
中发请求1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
api.loadFilmByFilmId(this.data.filmId).then(data => {
// console.log(data); // 成功返回电影信息
// 将它们保存到数据仓库中,页面上需要直接使用
this.setData({
film: data
})
}).catch(api.showError)
// 单独加载电影的评论
api.loadCommentsByFilmId(this.data.filmId, {
start: this.data.start,
count: this.data.count,
order_by: 'time' // 时间的降序,最新评论在最上面
}).then(data => {
console.log(data); // 打印下data,找到评论数据
this.setData({
comments: data.interests
})
}).catch(api.showError)
},
接着我们可以在AppData
中看下我们的数据仓库中有没有评论数据
展示数据,进入detail.wxml
文件中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!-- 电影评论 start -->
<view class="film-comment">
<view class="comment-title">
<text>评论:</text>
</view>
<view class="comment-item" wx:for="{{ comments }}" wx:for-item="comment" wx:key="index">
<view class="left">
<image class="user-img" src="{{ comment.user.avatar }}"></image>
</view>
<view class="right">
<view class="name">{{ comment.user.name }}</view>
<view class="time">{{ comment.create_time }}</view>
<view class="content">{{ comment.comment }}</view>
</view>
</view>
</view>
<!-- 电影评论 end -->
上拉加载更多评论
依旧是使用
onReachBottom
这个函数
在数据仓库的把总的数据条数存储起来
1
2
3
4
5
6
7
8
9
10
11 /**
* 页面的初始数据
*/
data: {
filmId: '', // 电影id
film: {}, // 存储电影信息
start: 0, // 评论的起始索引
count: 10, // 评论的条数
total: 0, // 总的评论条数
comments: [], // 存储电影的评论
},
同时起始索引也还要加,total
存储起来,评论数组,要与新的评论数组合并1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
api.loadFilmByFilmId(this.data.filmId).then(data => {
// console.log(data); // 成功返回电影信息
// 将它们保存到数据仓库中,页面上需要直接使用
this.setData({
film: data
})
}).catch(api.showError)
// 单独加载电影的评论
api.loadCommentsByFilmId(this.data.filmId, {
start: this.data.start,
count: this.data.count,
order_by: 'time' // 时间的降序,最新评论在最上面
}).then(data => {
console.log(data); // 打印下data,找到评论数据
this.setData({
// comments: data.interests, // 评论数据
comments: this.data.comments.concat(data.interests), // 评论数组的累加
start: this.data.start + this.data.count, // 起始索引每次累加
total: data.total // 记录总条数
})
}).catch(api.showError)
},
判断后台有没有多的数据,让你去请求,没有就显示没有更多,在onReachBottom
这个函数中判断
加载评论单独抽离成一个方法loadComments
,因为考虑到上拉加载每次只需要加载评论即可【封装代码这里不做展示了,具体可以拉取代码查看detail.js
文件】1
2
3
4
5
6
7
8/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
if (this.data.start < this.data.total) {
this.loadComments()
}
},
为了考虑用户体验,发请求获取数据需要时间,显示loading
效果,同时没有评论了,显示没有更多
这里我们把list.wxml
中的loading
和nomore
代码给复制过来粘贴到detail.wxml
中,同时到数据仓库中定义控制显示、隐藏的数据1
2
3
4
5
6
7
8
9
10
11
12
13/**
* 页面的初始数据
*/
data: {
filmId: '', // 电影id
film: {}, // 存储电影信息
start: 0, // 评论的起始索引
count: 10, // 评论的条数
total: 0, // 总的评论条数
comments: [], // 存储电影的评论
showLoading: false, // loading的开关
showNomore: false // 没有更多的开关
},
loading
和nomore
显示隐藏1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
if (this.data.start < this.data.total) { // 有数据
// 为了考虑用户体验,发请求获取数据需要时间,显示loading效果
this.setData({
showLoading: true
})
// 注意拿到数据之后才能再让loading隐藏,但是发请求是个异步,我们需要借助promise.then,即把之前加载评论数据封装的方法里的promise对象return出来即可
this.loadComments().then(() => {
this.setData({
showLoading: false // 拿到返回的评论数据
})
})
} else { // 没有更多数据
this.setData({
nomore: true
})
}
},
这里我们可以发现当初把loading
和nomore
抽离成模板的好处了,很多地方都会用到
最后还有一个收藏功能未实现,这里等我们把个人页【关于页】做了再来实现