1.代码结构介绍
1.1.CloudFunctions
CloudFunctions指定腾讯云项目的目录
1.2.miniprogram
小程序端的代码
1.2.1.文件介绍
- images文件夹下存放的是小程序对应的默认的图片文件
- pages文件夹下存放的是与小程序界面相关的文件
- .json:配置文件,以json格式存储一些配置(全局配置、页面配置、项目配置)
- .wxml:模版文件,描述页面结构,相当于HTML
- .wxss:样式文件,调整页面样式,相当于css
- .js:脚本逻辑文件,页面和用户的交互逻辑
1.2.Json文件
更多具体配置见微信官方文档介绍
- project.config.json:项目配置文件
- 对项目进行配置
- app.json:全局配置
- 在miniprogram下面
- pages配置项中的内容相当于每个页面的路由
- window配置项对应于窗口的配置
- “backgroundColor”: 窗口颜色
- “backgroundTextStyle”: 窗口文字样式
- “navigationBarBackgroundColor”: 导航栏颜色
- “navigationBarTitleText”:可以用来修改导航栏文字部分
- “navigationBarTextStyle”: 可以用来修改导航栏文字的颜色
- tabBar配置
- “color”: 未被选中时tabBar文字的颜色
- “selectedColor":被选中时tabBar文字的颜色
- “list”:个数最少两个,最多五个
- “pagePath”:这个地址一定要是pages中配置过的地址
- “text”: tabBar中显示的文字
- “iconPath”: “images/base.png”(tabBar没有被选中时显示的图片路径)
- “selectedIconPath”: “images/base-actived.png”(tabBar被选中时显示的图片路径)
- page.json:页面配置
- 如项目中的cloud.json中(局部样式,只对cloud界面有效)
- “navigationBarBackgroundColor”: 页面头部背景颜色
- “navigationBarTextStyle”: 页面头部字体颜色
- “navigationBarTitleText”: 页面头部文字内容
- 如项目中的cloud.json中(局部样式,只对cloud界面有效)
1.3.页面结构WXML
是小程序框架设计的一套标签语言,结合小程序的基础组件、事件系统,可以构建出页面的结构。充当的就是类似html的角色。
1.3.1.数据绑定
小程序中的数据一般情况下需要动态的从服务端获取,然后再渲染输出到视图中显示。
WXML中的动态数据均来自对应Page的data
数据绑定使用Mustache语法(双大括号)将变量包起来
1.3.2.常见组件
常见组件见微信文档
<view>{{msg}}</view> //双向绑定
<image src="{{img}}"></image>
<view wx:for="{{arr}}" wx:key="{{index}}">
{{index}} {{item}}
</view> //对数组arr的操作,遍历数组
<view wx:for="{{list}}" wx:key="{{index}}">
{{item.name}}-{{item.age}}
</view>
1.3.3.条件渲染
- 使用wx:if="{{condition}}"来判断是否需要渲染该代码块,也可以使用wx:elif和x:else来添加一个else块
<view wx:if="{{isLogin}}"> jerry </view> <view wx:else>请登录</view>
- wx:if VS hidden:
hidden后面的值为true就会隐藏,为false就显示;使用wx:if不显示的内容不会出现在wxml中,使用hidden不显示的内容仍然会出现在wxml中。(如果需要频繁切换的情景下,用hidden更好;如果在运行时条件不大可能改变则wx:if 更好)<view hidden="{{isLogin}}"> hidden </view>
1.4.页面样式WXSS
- WXSS是一套用于小程序的样式语言,用于描述WXML的组件样式,也就是视觉上的效果
- 尺寸单位:rpx(可以根据屏幕宽度进行自适应,适配不同宽度的屏幕)
- 引入外部wxss:@import ‘……’
Eg:
//引入外部样式common.wxss
@import '../../style/common.wxss';
//class='box'的组件的样式
.box{
width: 200rpx;
height: 200rpx;
background: #ccc;
}
1.4.1.第三方样式库
- WeUI
- iView Weapp
- Vant Weapp
1.5.页面交互JS
1.5.1.js负责逻辑交互
- 一个计数器的demo
bindtap=‘ ’用于绑定函数/事件(事件是对用户的交互操作行为的响应),在js文件中的onTabHandler就是对应函数的具体实现。在修改data中的值时,要用setData方法进行修改<button size='mini' bindtap='onTabHandler'>点我加一</button> <view>{{count}}</view>
onTabHandler: function(){ this.setData({ count:this.data.count+1 }) }
1.5.2.bind VS catch
- 使用bind绑定的子元素会进行事件冒泡,即点击子元素时,子元素的事件会逐层向父元素传播,从而调用父元素的事件
- 使用catch绑定的子元素可以避免事件冒泡
-
<view class="box" bindtap='onTabBoxHandler'> <view class="child" bindtap='onTabChildHandler'> </view> </view> <view class="box" catch:tap='onTabBoxHandler'> <view class="child" catch:tap='onTabChildHandler'> </view> </view>
1.5.3.事件对象
事件对象代表事件的状态。当组件绑定的事件被触发时,就会传递一个事件对象到绑定的函数当中。
2.小程序云开发
2.1.云开发简介
微信小程云开发是腾讯云和微信团队推出的全新的小程序的解决方案。它提出了云函数、云数据库、云存储三大技术能力。可以将小程序服务端的部署和运维托管给腾讯云管理
2.2.小程序传统开发模式
- 客户端(相当于前端)
- 服务端(将后端代码和数据库放到服务器上,相当于后端)
- 客户端和服务端在写代码时需要进行沟通以统一数据的接口,沟通的成本很高,随之也提高了小程序开发的成本。
- 运维(包括数据库运维、文件存储、内容加速、网络防护、容器服务、负载均衡、安全加固……)成本高
2.3.小程序云开发模式
- 客户端(相当于前端)
- 云开发(包括云函数<node.js>、云数据库、云存储)
- 可以直接在客户端调用云数据库中的内容、调用云函数(可以在云函数中处理业务逻辑,也可以在云函数中调用云数据库)、通过云存储上传下载文件
- 全部部署在腾讯云上,不需要额外的运维人员,运维成本大大降低
2.4.三大基础能力支持
2.4.1.云函数
- 为用户提供了在云端运行代码的能力。
- 可以很方便的获取到用户登录的信息和openid
- 可以很方便的获取小程序的appid
- 生成分享图
- 调用腾讯云SDK
- ……
- 云函数是运行在云端的代码,相当于小程序的后台代码
- 云函数在cloudfunctions文件夹下面,每次修改云函数都要重新上传并部署云函数
- 三个例子
- 求和函数sum()
- 在sum目录下的index.js文件作修改
// 云函数入口函数 // event包括当前调用云函数时在小程序端对应的参数 // context:当前调用的上下文,包括一些当前用户的信息 exports.main = async (event, context) => { return{ sum: event.a+event.b } }
- 在cloud.wxml中添加如下代码,用于绑定事件
<button bindtap='sum'>调用云函数sum</button>
- 在cloud.js中添加如下代码
sum:function(){ //通过wx.cloud.callFunction调用云函数 //name代表云函数的名称 //data代表要传递给云函数的参数 wx.cloud.callFunction({ name:'sum', data:{ a:2, b:3 } }).then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }); }
- 在sum目录下的index.js文件作修改
- 获取当前用户的openid
- 传统微信用户登录:
- 通过用户端小程序发起请求,调用wx.login从微信服务端获取code,再通过wx.request将code传递给后端服务器,使用code从后但服务器换取openid和session-key,将openid发送给小程序本地存储
- 云开发微信登录:
- 用户点击按钮获取用户信息,小程序调用云函数获取用户信息。云函数可以直接返回当前用户的openid,小程序将用户信息存储到云数据库中
- login云函数为自带的,可以不用写。只需要在cloud.js中完成getOpenId函数即可
getOpenId:function(){ wx.cloud.callFunction({ name:'login' }).then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }); }
- 传统微信用户登录:
- 批量删除云数据库的数据
- 创建batchDelete函数,并在js文件中编写
const db=cloud.database(); //初始化数据库 // 云函数入口函数 // await是对js异步操作的一种方式 exports.main = async (event, context) => { try{ //删除name是jerry的记录 return await db.collection('user').where({ name:'jerry' }).remove(); }catch(err){ console.log(err); } }
- 在cloud.wxml中创建按钮并绑定batchDelete事件
- 在cloud.js完成batchDelete函数
batchDelete:function(){ wx.cloud.callFunction({ name:'batchDelete' }).then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }) }
- 创建batchDelete函数,并在js文件中编写
- 求和函数sum()
2.4.2.云数据库
数据增加、删除、修改、查询
- 云开发提供了一个JSON数据库(数据库中的每一条数据都是json格式的对象)
- 不同于传统的关系型数据库(有行有列),云数据库是文档型数据库。一个文档型数据库包含了多个集合(一个集合就相当于关系型数据库中的表)、多条记录(一条就相当于关系型数据库中的一行)、多个字段(一个字段就相当于关系型数据库中的列)
- 数据库数据类型:
- String:字符串
- Number:数字
- Object:对象
- Array:数组
- Bool:布尔值
- GeoPoint:地理位置点(用经纬度唯一标记一个点,查询时要建立地理标志的索引)
- Date:时间(小程序端创建的时间是客户端的时间,不是服务端的时间)
- Null
- 操作云数据库的方式:
- 小程序控制(读写数据库受权限控制限制)
- 云函数控制(拥有所有读写数据库的权限)
- 可视化控制台控制(拥有所有读写数据库的权限)
- 云数据库权限管理:
- 仅创建者可写,所有人可读
- 仅创建者可读写(适用于私密相册)
- 仅管理端可写,所有人可读(适合于商品信息)
- 尽管理端可读写
- 数据库初始化:
const db=wx.cloud.database() //初始化 //切换环境(小程序云开发提供两个环境) const testDB=wx.cloud.database({ //通过环境名称切换环境 env: 'test' })
- 对数据库的操作:
首先在cloud.wxml中通过按钮绑定事件函数,再在cloud.js文件中初始化数据库并且完成事件函数的编写// 初始化数据库,db代表云数据库 const db=wx.cloud.database(); // 查询操作 // 回调函数的写法 insert:function(){ // 通过db.collection取到云数据库中的集合user // 通过add方法增加数据 db.collection('user').add({ data:{ name:'jerry', age:20 }, // 当插入成功时会调用success这个回调函数的内容 success: res=>{ //箭头函数 console.log(res); }, // 当插入失败时会调用fail这个回调函数的内容 fail: err=>{ console.log(err); } }) } // promise的写法 // 当插入成功时会进入then里面进行操作 // 当插入失败时会进入catch里面进行操作 insert:function(){ db.collection('user').add({ data:{ name:'jack', age:18 } }).then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }) } //更新操作,一定要通过云数据库中对应的id进行更新 //通过doc函数和id号的到要更新的数据 update:function(){ db.collection('user').doc ('cbddf0af606d6fd700c914e2447ac166').update({ data:{ age: 21 } }).then(res=>{ console.log(res) }).catch(err=>{ console.log(err) }) } //查找操作,通过where函数进行查找,查找时要注意查找权限的问题 search:function(){ db.collection('user').where({ name:'jerry' }).get().then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }) } //删除操作,删除操作也需要知道要删除数据的id,调用doc函数和remove函数 //单条数据的删除 delete:function(){ db.collection('user') .doc('cbddf0af606d6fd700c914e2447ac166') .remove() .then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }) } //数据批量删除需要在云函数中进行操作
2.4.3.云存储
管理、上传、下载、分享文件
- 相关命令
wx.cloud.uploadFile //上传文件 wx.cloud.downloadFile. //下载文件 wx.cloud.deleteFile //删除文件 wx.cloud.getTempalteFileURL //获取临时链接
- 文件上传
- 用户选择图片或拍照上传小程序,小程序上传所选图片到云存储,云存储返回图片对应的fileID给小程序(通过fileID可以找到该图片),小程序再将fieldID存到云数据库中,方便进行后续操作
- 上传图片
upload:function(){ //选择本地的图片 wx.chooseImage({ //count表示当前选择图片的个数 count: 1, //当前是以原文件的形式上传还是以压缩的形式上传 sizeType: ['original', 'compressed'], //当前文件的来源 sourceType: ['album', 'camera'], //回调函数,获取到当前选择图片的临时路径 success (res) { // tempFilePath可以作为img标签的src属性显示图片 // tempFilePath就是当前选择图片的临时路径,可以通过该路径将图片上传到对应的云存储中 // tempFilePaths是一个数组 const tempFilePaths = res.tempFilePaths wx.cloud.uploadFile({ cloudPath: 'new Date().getTime() +'.png'', //上传至云端的路径(文件名称),注意要每一个都不一样 filePath: tempFilePaths[0], // 小程序临时文件路径 }).then(res => { // 返回文件ID console.log(res.fileID) //将filedID存储到云数据库中 db.collection('image').add({ data:{ fileID:res.fileID } }).then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }) }).catch(error => { // handle error console.log(error) }) } }) }
- 将选择的图片展示
- 首先在page的data区域中新建一个images列表,用于存放图片的路径
-
<block wx:for="{{images}}"> <image src="{{item.fileID}}"></image> </block> //在wxml文件中遍历images列表,并展示图片
- 根据当前用户的openid获得该用户上传的图片文件的fileID,并赋值给images列表
getFile:function(){ //获取当前用户的openid wx.cloud.callFunction({ name:'login' }).then(res=>{ //查询匹配的数据 db.collection('image').where({ _openid: res.result.openid }).get().then(res2=>{ console.log(res2); //将fileID赋值给images列表 this.setData({ images:res2.data }) }) }) }
- 文件下载
- 首先通过微信小程序去云存储中获取文件的fileID,用户点击下载按钮后,小程序发送文件下载请求到云数据库。云数据库返回对应文件信息给小程序,小程序就可以下载文件到手机相册
- 下载图片并存到手机相册
<block wx:for="{{images}}"> <image src="{{item.fileID}}"></image> <button data-fileid="{{item.fileID}}" size="mini" bindtap='downloadFile'>文件下载</button> </block>
downloadFile:function(event){ //微信小程序api wx.cloud.downloadFile({ //要下载的当前文件的fileID fileID: event.target.dataset.fileid //cloud.wxml中自定义的属性 文件的fileid }).then(res => { // 临时文件路径 console.log(res.tempFilePath) //保存图片到手机相册 微信小程序api wx.saveImageToPhotosAlbum({ // 临时文件路径 filePath: res.tempFilePath, success(res) { //小程序的轻提示组件 wx.showToast({ title: '保存成功' }) } }) }).catch(error => { // handle error console.log(error) }) },
3.电影小程序案例
3.1.功能介绍和环境搭建
- 引入vant组件库:
- 将miniprogram在终端打开,运行cnpm init和cnpm i @vant/weapp -S --production命令下载vant组件库
- 微信开发者工具上方菜单栏->工具->构建npm->在miniprogram文件夹下出现miniprogram_npm
- 微信开发者工具右上角详情->本地配置->勾选使用npm模块
- 根据官方文档指引在.json文件中进行配置
3.2.电影列表
电影列表的信息是通过调用豆瓣api的接口获取的
- 发送请求的方式:
- 在小程序端发送请求:
- 发送方法:wx.request(),只支持https协议
- 要经过ICP备案
- 域名个数限制:20个
- 在云函数中发送请求:(相当于通过小程序的后端去发送请求)
- 第三方库(request、got、axios等),支持的协议根据第三方库来决定
- 不经过备案也可以发送请求
- 没有域名个数限制
- 使用方法:
- 首先新建一个云函数
- 对新建的云函数右键使用外部终端打开,运行以下命令:
cnpm install axios
- 在云函数的js文件中引入axios,并在调用电影列表API时,增加headers对应参数(该参数一定要加,否则API会调用失败)
const axios = require('axios') // 云函数入口函数 exports.main = async (event, context) => { // 云函数中引入axios,并在调用电影列表API时,增加headers对应参数(该参数一定要加,否则API会调用失败) try { const { data } = await axios({ // url是豆瓣电影列表的接口 // event.start表示从第几条数据去取,event.count代表一次取多少条数据,这两个参数会在调用云函数时传入云函数 url: `https://frodo.douban.com/api/v2/subject_collection/movie_showing/items?start=${event.start}&count=${event.count}&apiKey=054022eaeae0b00e0fc068c0c0a2102a`, method: 'get', headers: { "Host": "frodo.douban.com", "Connection": "keep-alive", 'content-type': 'application/json', 'Accept-Encoding': 'gzip,compress,br,deflate', 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.2(0x18000236) NetType/WIFI Language/zh_CN', 'Referer': 'https://servicewechat.com/wx2f9b06c1de1ccfca/81/page-frame.html' } }) return data } catch (e) { console.log(e) } }
- 在小程序端发送请求:
- movie.js文件中要做的事就是在加载电影界面的时候去发送请求,要调用moveList云函数
- 如果要将请求到的数据展示在界面上,要在page的data区域中新建一个movieList列表,用于存放数据
- 代码如下
/** * 生命周期函数--监听页面加载 */ // 当生命周期到达该阶段的时候会自动调用 // 即页面加载时会自动调用该函数 onLoad: function (options) { wx.cloud.callFunction({ name:'movieList', data:{ start:this.data.movieList.length, count:10 } }).then(res=>{ console.log(res); //给movieList进行赋值,采用追加模式,防止覆盖掉之前的数据 this.setData({ movieList: this.data.movieList.concat(res.result.subject_collection_items) }); }).catch(err=>{ console.log(err); }) },
- 当滚动条滚动到底部时,仍要运行上述代码进行加载,因此上述代码可以被提取出来
getMovieList:function(){ // 显示提示 wx.showLoading({ title: '加载中', }) wx.cloud.callFunction({ name:'movieList', data:{ start:this.data.movieList.length, count:10 } }).then(res=>{ console.log(res); //给movieList进行赋值,采用追加模式,防止覆盖掉之前的数据 this.setData({ movieList: this.data.movieList.concat(res.result.subject_collection_items) }); // 请求成功后隐藏提示框 wx.hideLoading(); }).catch(err=>{ console.log(err); wx.hideLoading(); }) }, /** * 生命周期函数--监听页面加载 */ // 当生命周期到达该阶段的时候会自动调用 // 即页面加载时会自动调用该函数 onLoad: function (options) { this.getMovieList(); }, /** * 页面上拉触底事件的处理函数 */ // 当滚动条滚动到底部时会自动调用该函数 onReachBottom: function () { this.getMovieList(); } ``
4.Promise语法的学习
4.1.promise状态
- Promise有三种状态:
- pending 初始化状态
- resolved 成功
- reject 失败
- Promise对象的状态改变,只有两种可能:从pending变为resolved、从pending变为rejected,之后状态不会在改变了且状态不可逆。
4.2.promise常用基本语法
4.2.1.resolve & reject
- New Promise 实例,要return
- 传入函数,要有resolve、reject两个参数
- 成功执行resolve,失败执行reject
- then监听并接收结果
- eg:
function loadImg(src) { const promise = new Promise(function(resolve,reject){ img.onload = function () { resolve(img) } img.onerror = function () { reject() } img.src = src }) return promise }
4.2.2.catch捕获
catch通常用于最后统一捕获,我们try catch一样
Error和eject都可以捕获
result.then(function(img){
// dosomthing
}).then(function(){
// dosomthing
}).catch(function(error){
console.log(error)
})
4.2.3.多个串联
如果我们希望我们的需求按顺序加载(例如,先加载用户信息,然后再通过用户信息渲染好友列表之类的)
我们需要在.then之后return 另外一个Promise 就可以了
var src1 = 'www.xxx.com/1.jpg'
var src2 = 'www.xxx.com/2.jpg'
var img1 = loadImg(src1)
var img2 = loadImg(src2)
//链式操作
img1.then(function(img){
console.log('图片一加载完成', img)
return img2 //接下来就是对img2进行异步操作了
}).then(function(img){
console.log('图片二加载完成', img)
}).catch(function(er){
console.log(er)
})
4.2.4.Promise.all
Promise.all 接收一个Promose对象数组待全部完成之后一起执行success
.then方法接收的datas是一个数组,依次包含多个Promise返回值
Promise.all([result1,result2]).then(datas => {
console.log(datas[0])
console.log(datas[1])
})
4.2.5.Promise.race
和all不一样的是 数组中只要有一个完成 就执行success
Promise.race([result1,result2]).then(data => {
console.log(data)
})
5.flex布局
5.1.flex-direction
在给view设置属性display: flex;之后,view的布局方式就会变为flex模式,此外还需要设置其flex-direction控制布局方向,flex-direction有4个值:
- row:从左到右的水平方向为主轴
- row-reverse:从右到左的水平方向为主轴
- column:从上到下的垂直方向为主轴
- column-reverse:从下到上的垂直方向为主轴
5.1.1.flex-direction: row
<view class='flex'>
<view class='flex-item' style='background:red;'>1</view>
<view class='flex-item' style='background:blue;'>2</view>
<view class='flex-item' style='background:yellow;'>3</view>
</view>
.flex {
display: flex;
flex-direction: row;
background: lightgray
}
.flex-item {
width: 60px;
}
5.2.justify-content
设置完view的布局方向之后,想要控制内容的对齐方式,需要设置justify-content属性,该属性值有:
- flex-start:主轴起点对齐(默认值)。
- flex-end:主轴终点对齐。
- center:在主轴中居中对齐。
- space-between:两端对齐,除了两端的子元素分别靠向两端的容器之外,其他子元素之间的间隔都相等。
- space-around:每个子元素之间的距离相等,两端的子元素距离容器的距离也和其它子元素之间的距离相同。
Eg: flex-start
<view class='flex'>
<view class='flex-item' style='background:red;'>1</view>
<view class='flex-item' style='background:blue;'>2</view>
<view class='flex-item' style='background:yellow;'>3</view>
</view>
.flex {
display: flex;
flex-direction: row;
justify-content: flex-start;
background: lightgray
}
.flex-item {
width: 60px;
}
5.3.align-items
align-items
属性值有:
- stretch 填充整个容器(默认值)
- flex-start 起点对齐
- flex-end 终点对齐
- center 居中对齐
- baseline 以子元素的第一行文字对齐
Eg: baseline
<view class='flex'>
<view class='flex-item' style='background:red; height:50px;'>1</view>
<view class='flex-item' style='background:blue; height:70px;'>2</view>
<view class='flex-item' style='background:yellow;height:90px;'>3</view>
</view>
.flex {
display: flex;
flex-direction: row;
background: lightgray;
justify-content: space-around;
align-items: baseline;
height: 100px;
}
.flex-item {
width: 60px;
}
5.4.align-self
子 View 还有个属性 align-self,可以覆盖父元素的 align-items 属性,它有6个值可选:auto | flex-start | flex-end | center | baseline | stretch (auto 为继承父元素 align-items 属性,其他和 align-items 一致)
此外还有 flex-wrap 属性,用于控制子 View 是否换行,有3个值可选:
- nowrap:不换行(默认)
- wrap:换行
- wrap-reverse:换行,第一行在最下面
5.5.order
子 View 有个 order 属性,可以控制子元素的排列顺序,默认为0。