主界面如下:通过调用豆瓣电影的API接口获取到的信息,每次 加载10条数据,当滚动 条每次滚动到最低端的时候,自动通过异步再加载10条
评价按钮:
还可以上传图片并提交评价,提交的评价或者图片都是 储存到云存储当中,吧图片、评分以及评价的fileID存到我们 的云数据库当中,
而另一个选项:显示头像、用户名称以及openid
那么我们开始创建项目:后端服务选择小程序.云开发
目录也要创建在一个空文件夹
初始界面如下:
然后把pages下一级目录全部删除,这个时候会有报错,
那么 我们把app.json文件内容的路由部分全删除就OK了!
删除之后我们重新创建两个文件夹,在创建文件夹的时候千万不要上下级目录重名,否则可能会创建失败。创建好后并且保存(Ctrl+S),这个时候两个文件夹下面会自动生成配置文件等等。
app.json文件初始配置
{
"pages": [
"pages/movie/movies",
"pages/profile/profiles"
],
"window": {
"backgroundColor": "#F6F6F6",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#E54847",
"navigationBarTitleText": "最新电影",
"navigationBarTextStyle": "white"
},
"tabBar": {
"color": "#000",//表示字体没有被选择的时候的颜色
"selectedColor": "#E54847",//表示字体被选择的时候的颜色
"list": [{
"pagePath": "pages/movie/movies",//tabBar分页路由路劲
"text": "电影",
"iconPath": "images/j.png",//指的是当前tabBar没有被选中的图片路劲
"selectedIconPath": "images/k.png"//指的是当前tabBar被选中的图片路劲
},{
"pagePath": "pages/profile/profiles",//tabBar分页路由路劲
"text": "我的",
"iconPath": "images/l.png",//指的是当前tabBar没有被选中的图片路劲
"selectedIconPath": "images/m.png"//指的是当前tabBar被选中的图片路劲
}]
}
}
然后把pages以及images文件夹下面的文件或者图片都删除,然后用自己准备的图片,或者 重新创建分页文件等。
上面这段代码中list数组是有要求的,最少2个选项,最多5个选项。如下就是2个的。
接下来学习如何在我们的小程序项目当中引用第三方组件库,其实
第三方组件库的作用,是我们在小程序项目开发过程中,有些组件或者有些样式我们是不需要去自己写的,我们可以引用第三方组件库,就能快速实现我们界面的一个需求。
那么如何安装我们的组件库呢?先去官网
安装:
当然,在安装之前,我们需要进行一些代码的初始化设置,
首先找到miniprogram文件夹,打开终端,当然笔者不知道 为什么,我打开终端会显示npm或者node 不是内部或者 外部命令 ,但是笔者环境都是配置好了的,但是笔者想到了一个 最佳的办法
那就是先点击硬盘打开,然后在同时在进入 miniprogram文件夹里面,然后按住shift并同时点击 鼠标左键,选择打开命令窗口
这个时候再输入相应的命令,进行下载或者操作 。
首先输入npm init命令,这个相当于初始miliprogram文件
当然在初始化过程中,我们遇到下面如版本号啥的,就一路回车就好了!
一路回车完了后,在 miniprogram文件夹下面就会生成一个package.json文件,这个文件时 对当前miniprogram这个项目的配置信息以及一些另外的包的管理在这个文件当中进行相应的配置,
然后安装vant,并输入命令npm i vant-weapp -S --production这个命令是通过npm安装vant,安装的过程中或者结束,务必点击菜单栏的“工具”选项,然后点击“构建npm”选项。然后点击确定。
这个时候miniprogram文件夹下面多了一个miniprogram_npm包,这个包里面实际上就是我们的一个vant这样一个请求库。
然后点击界面的“详情”,选择“使用npm模块”
选中之后,我们的vant组件库才能起作用。
那么我们接下来先验证一下vant能否起作用?
当然,在 使用组件库之前我们应该引入组件库 、
视频看到 4-2Vant组件库 。!!!!!!!!!!
我们先显示一个vant组件库的按钮。
这个时候就可以看到对应的按钮就可以在界面上显示了,也就是说,我们的vant组件库已经顺利的在我们的项目中被引用了。
接下来我们去完成电影列表的问题。
电影列表的信息,是通过豆瓣电影的API接口获取的,而不是笔者写死的。
也就是我要从我的小程序端要想豆瓣电影的服务端去发起一个请求,那么我们先来看一下在小程序端如何发起请求 ,
发起请求的方式一共有两种,小程序端和云函数。
一种是在小程序端发送请求,另一种是我们可以在云函数中发起请求。
如果在小程序 端发送请求,那么小程序端中有一个 API函数叫做wx.request(),通过这个函数可以通过小程序端直接向某一个服务端发起请求,如果我们通过云函数发起请求,那么其实是通过小程序的后端去发送请求。这个时候我们可以在小程序的云函数当中去安装第三方库。比如说常用的request,got等都可以 发起请求,在云函数端支持的协议是取决于第三方库的。一般情况下支持https/http均可以支持,在小程序端发起请求,请求回来的域名一定要经过ICP备案,才能去请求,否则 的话是不可以的,那么在云函数端的请求他的限制是比较开放一些。可以不经过备案,也可以去发送请求。所以 我们可以通过两种方式向豆瓣电影接口发起请求。而我们接下来是通过云函数发起请求来演示的。
我们先来 演示如何 通过第三方库request发送请求,首先登陆GitHub官网,
选择request当中一个request-promise方法,点击进去。当然你也可以直接在GitHub上面直接搜索request-promise方法,
如果需要用这个方法(request-promise),我们需要先安装request,然后再安装request-promise方法(如下:)
cloudfunctions作为云函数端,我们要通过云函数,首先我们得先创建一个云函数,在cloudfunctions下一级目录。
创建好了我们就使用终端打开,在终端中输入第一条安装request的命令,安装完这个包后,就安装request -promise这个包,但是得记住,必须得先按照request这个包,然后再安装request-promise这个包。
当安装好这两个包后,在我们的cloudfunctions下一级目录movielist里的package.json里面就会对应两个包的版本。(如下:)
那么我们如何通过request-promise去发送一个请求呢?当然也很简单,我们打开index.js文件,首先引入我们的request-promise,
指令为
var rp = require('request-promise');
这条指令在GitHub里面也有,
引入之后,我们再看GitHub里面有一个rp函数,
rp('http://www.google.com')
.then(function (htmlString) {
// Process html...
})
.catch(function (err) {
// Crawling failed...
});
我们直接在云函数入口函数中直接通过rp函数发起请求,直接把请求结果返回,
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
var rp = require('request-promise');
// 云函数入口函数
exports.main = async (event, context) => {
return rp(`http://api.douban.com/v2/movie/in_theaters`)
.then(function(htmlString) {
.........
})
.catch(function(err) {
.........
});
}
rp的第一个参数是当前我要请求的地址,这个地址指的是豆瓣电影的列表接口
其实我们在请求的时候,我们要对数据进行分页的请求,比如说我们不可能一次把所有数据都请求回来,假设我每次请求10条,通过ES6的一个叫字符串模板拼接,这样的话我们可以不通过加号拼接,这样看起来 可读性较强。
start是请求回来的值,而count是一次去请求多少条数据。当然这些值在调用的时候都会传回到我们的云函数里面。
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
var rp = require('request-promise');
// 云函数入口函数
exports.main = async (event, context) => {
return rp(`http://api.douban.com/v2/movie/in_theaters?start=${event.start}&count=${event.count}`)
.then(function(res) {
return res;
})
.catch(function(err) {
console.err(err);
});
}
.then是表示请求成功的话,而.catch是表示请求失败的话。res表示请求回来的结果,return直接把请求回来的结果返回,如果报错我们也通过控制台打印错误信息,便于我们去找到对应的问题。
当我们定义好云函数后,我们就在云函数的文件上右键上传并部署我们的云函数 。
我们回到我们的电影主界面,movie目录,电机打开movies.js文件,在这个.js文件当中我们需要做的是当加载电影这个 界面的时候,我们就要去发送请求,也就是说,我需要去调用刚刚定义好的云函数 ,在movies.js当中定义了很多生命周期函数,也就是钩子函数。 这些钩子函数是会当生命周期到达这个阶段的时候会被自动的调用,那么我们直接在onLoad这个函数,这是一个监听页面加载函数,也就是页面加载的时候会自动去加载这个onLoad函数,
然后我们直接在onLoad里面直接去调用我们的云函数 ,用cloud.callFunction()的方式去调用云函数,
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
wx.cloud.callFunction({
name: 'movielist'//云函数的名字
}).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
});
},
那么在movielist/index.js里面的回调函数中我也想打印这个值,怎么办呢?通过云函数在控制台显示,写一个console.log(res);
因为我想演示一下,这个打印值在哪儿去看。一定要谨记,只要云函数被改动,一定得随时上传并部署。
// 云函数入口函数
exports.main = async (event, context) => {
return rp(`http://api.douban.com/v2/movie/in_theaters?start=${event.start}&count=${event.count}`)
.then(function(res) {
console.log(res);//在调用云函数的时候也看看这个值
return res;
})
.catch(function(err) {
console.err(err);
});
}
打开云开发,点击云函数,在我们的云开发中,已经有我们刚创建的云函数movielist,点击刷新可出来。
那么我们来试一下,现在打开我们的调试器,点击编译,可以看当前在控制台显示的结果,当然这个结果我们是在小程序端打印的。
其中注意,云函数里面写的console.log(res)并不会打印在console里面,因为我们的云函数是运行于服务端的。服务端的日志是不会打印纸top控制台里面,其云函数的日志可以在 云开发控制台的云函数的日志里面看到,如下:
所以,movielist/index.js下面的console.log(res)打印的日志就如上图所示,而控制台显示的并不是这个console.log(res)的信息。
找日志,不要去云函数 找,找不到的,因为我们的云函数是运行于服务端的。这个日志是res,而console.log他就是在控制台打印,那么说console.log(res)是不是就是把日志打印到了控制台呢,并不是,只有return res才把日志返回到服务端的云函数日志里面。所以控制台 显示的并不是res。
而小程序控制台显示的是结果,如下:
那么如果,我们想要去显示数据在页面上,需要在pages/movie.js的data函数里面去定义一个对应的值。
比如说我们定义一个电影的列表:movieList: [ ]
data: {
movieList: []
},
那么我们也可以看到,控制台里面返回的result其实是一个字符串(通过看大括号有双引号),字符串并不利于我们去获取这个值,而我们想要的是这个字符串里面subject里面的数组,subject对应的其实才是我们想要的列表。
那么,我们想要这个列表,我们就在onLoad函数里面加一个this.setData方法,通过这个方法 给movieList赋值,在赋值的时候我们直接去解析我们当前的字符串,我们要解析的是res.result,如何解析呢?有JS基础的同学都知道,如何把一个JS变成一个 对象,我们应该通过JSON.parse的方式,我们通过这种方式直接把字符串解析成对象,代码如下:
onLoad: function (options) {
wx.cloud.callFunction({
name: 'movielist'//云函数的名字
}).then(res=>{
console.log(res);
this.setData({
movieList:JSON.parse(res.result).subjects
})
}).catch(err=>{
console.error(err);
});
},
后面还得加一个subjects,因为我们需要的是字符串result里面的subject。
而我们需要把当前返回的结果追加到movieList的结果里面。为什么要追加,而不是直接赋值?
你想想,如果直接赋值,那每次刷新10条(异步加载 10条),如果 不是追加,那么他将会覆盖掉之前的,所以我们要追加,就不会覆盖以前的。
所以 我们每次操作,应该先取到movieList的值然后把获取到的字符串列表转化的对象追加到原来的movieList。
先去到movieList的值: this.data.movieLst()
然后追加到movieList: movieList:this.data.movieList(JOSN.parse(res.result).subjects)
所以代码如下:
onLoad: function (options) {
wx.cloud.callFunction({
name: 'movielist'//云函数的名字
}).then(res=>{
console.log(res);
this.setData({
movieList: this.data.movieList(JSON.parse(res.result).subjects)
})
}).catch(err=>{
console.error(err);
});
},
那么,我们每次取的时候,还要跟一个对应的参数,所以我们应该在云函数名字后面再跟一个参数。也许你会想到下面这种,但是不正确。
onLoad: function (options) {
wx.cloud.callFunction({
name: 'movielist',//云函数的名字
data: {
start: 0,
count: 10
}
}).then(res=>{
console.log(res);
this.setData({
movieList: this.data.movieList(JSON.parse(res.result).subjects)
})
}).catch(err=>{
console.error(err);
});
},
参数内容包括我们从第几条开始去取,第一次从第0条开始取所以start=0,每次取10条,所以count=10,那第二次呢?第二次从第10条开始取,取到第20条,那么第三次呢第四次呢?
所以每次取的值start是改变的!
那么这个start的值其实和movieList的长度是一样的!
正确的代码如下:
onLoad: function (options) {
wx.cloud.callFunction({
name: 'movielist',//云函数的名字
data: {
start: this.data.movieList.length,
count: 10
}
}).then(res=>{
console.log(res);
this.setData({
movieList: this.data.movieList(JSON.parse(res.result).subjects)
})
}).catch(err=>{
console.error(err);
});
},
那么我们也应该将这些内容呈现在“电影页面”,所以我们要写movie/movies.wxml文件
{{item.title}}
观众评分: {{item.rating.average}}分
{{item.name}}
年份:{{item.year}}
写好后,这个时候 ,编辑窗体显示如下:
这个时候我们再去写一些CSS代码,这个在微信小程序里面,应该叫微信SS(wxss)
.movie{
height: 300rpx;//给我们的电影设置高度
display: flex;//设置一个弹性盒子
}
因为view这样一个标签是块儿元素,他自己会占用一行,所以各位演员的名字各自占了一行,如果要排成一行,我们就不能用view标签。所以我们应该把循环演员的标签作为一个行级标签 放在块级标签里面。(如下:)
演员:
{{item.name}}
那么关于 演员名字之间的空格,有两种方式。
第一是:view标签演员的后面打空格
第二是:{{item.name}}后面加空格
那么此时movie/movies.wxss文件
.movies{
height: 300rpx;
display: flex;
padding: 10px;
border-bottom: 1px solid #ccc;
}
.movie-img{
width: 200rpx;
height: 100%;
}
.movie-info{
flex: 1;
}
.movie-title{
color: #666;
font-size: 40rpx;
font-weight: bolder;
}
.movie-average{
color: #faaf00;
}
那么如何让我们在翻到最底部的时候,能够异步加载呢?
当我们界面加载的时候,其实
wx.cloud.callFunction({
name: 'movielist',//云函数的名字
data: {
start: this.data.movieList.length,
count: 10
}
}).then(res=>{
console.log(res);
this.setData({
movieList: this.data.movieList.concat(JSON.parse(res.result).subjects)
});
}).catch(err=>{
console.error(err);
});
上面这个函数(这段代码)的意思是向服务端发送请求,请求回我们的电影信息,当我们滚动条滚动到底部的时候是不是还应该执行这段代码?
所以我们把这段代码单独提取到一个函数里,比如说我们把这段代码提取到getMovieList函数了,其内容就为这段代码,当然,我们在请求的时候,我们应该有一些更好的提示,比如一个等待加载框,当请求成功后,我们就把这个加载框隐藏。通过wx.hideLoading()方法隐藏,当然,失败的话我们也要把它隐藏掉。
代码如下:
// pages/movie/movies.js
Page({
/**
* 页面的初始数据
*/
data: {
movieList: []
},
getMovieList: function(){
wx.showLoading({
title: '加载中',
})
wx.cloud.callFunction({
name: 'movielist',//云函数的名字
data: {
start: this.data.movieList.length,
count: 10
}
}).then(res => {
console.log(res);
this.setData({
movieList: this.data.movieList.concat(JSON.parse(res.result).subjects)
});
wx.hideLoading()//当请求成功后隐藏加载框
}).catch(err => {
console.error(err);
wx.hideLoading()//如果失败也要隐藏掉
});
},
//我们现在要通过movies.wxml这个文件跳转到comments.wxml
gotoComment: function(event){
wx.navigateTo({
url: `../comment/comments?movieid=${event.target.dataset.movieid}`,
});
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.getMovieList();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
this.getMovieList();
},
})
下面来做一个评价按钮,以及一个评价界面。
{{item.title}}
观众评分:
{{item.rating.average}}分
演员:
{{item.name}}
年份:{{item.year}}
.movie-conmment{
height: 60rpx;
background: #E54847;
color: #fff;
font-size: 26rpx;
margin-top: 120rpx;
}
//pages/comment/comments.js
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
console.log(options);
},
现在评价按钮以及movies.js里面的界面都做好了。
其中,options是一个对象,里面对应的是movieid;
那么,我们现在可以根据这个movieid再去 豆瓣电影接口去获取到id所对应的详情界面,还是如上,在云函数里面创建一个node.js云函数,用于获取详情。
每次创建新的云函数的时候,都要安装request以及request-promise这两个包,安装这两个包的命令前面也提到过,安装好后
要用云函数,就要把request-promise引入过来,即
var rp = require('request-promise');
放在cloud.init()的下面。
然后把查询到的结果返回到小程序端,
查询到的结果如下:
rp('http://www.google.com')
.then(function (htmlString) {
// Process html...
})
.catch(function (err) {
// Crawling failed...
});
放到小程序端,
将云函数里面的这些东西都提前删除,用不到的,也就是现在注释的这些。
// 云函数入口函数
exports.main = async (event, context) => {
// const wxContext = cloud.getWXContext()
return {
// event,
// openid: wxContext.OPENID,
// appid: wxContext.APPID,
// unionid: wxContext.UNIONID,
}
}
然后就变为下面这:
// 云函数入口函数
exports.main = async (event, context) => {
return {
}
}
将查询到的结果,用于返回到小程序端:
// 云函数入口函数
exports.main = async (event, context) => {
return rp('详情的URL地址')
.then(function (htmlString) {
// Process html...
})
.catch(function (err) {
// Crawling failed...
});
}
注意:详情的URL地址 的两端用ES6的字符串模板符,即 ` ` 这样的符号,他是英文状态下的Esc下面的那个键,可以直接去拼接其他东西,比英文状态下的单引号简单一点。
rp(`http://api.douban.com/v2/movie/subject/id`)
那么用详情的URL地址去拼接上前端传回来的movieid
rp(`http://api.douban.com/v2/movie/subject/?apikey=0df993c66c0c636e29ecbb5344252a4a${event.movieid}`)
可以看到,加载的10个电影,而且点击评价后,跳转到页面pages/comment/comments,并且有50条数据。
然后我们在吧这个评价在pages/comment/comments.wxml这个页面中好好做一下。