微信小程序开发
一 基础
1.1 JSON配置
文件 | 含义 |
---|---|
project.config.json | 项目配置 |
app.json | 全局配置 |
page.json | 页面配置 |
1.2 WXML
文件 | 含义 |
---|---|
wx:for | 列表渲染 |
wx:if | 条件渲染 |
{{}} | 数据绑定 |
wx:if
与hidden
的区别:当未隐藏状态时,前者在wxml代码中不会显示,后者会显示。在频繁切换时,推荐使用hidden
。
案例代码如下:
wxml文件
<!-- 数据绑定 -->
<view>{{msg}}</view>
<image src='{{img}}'></image>
<!-- 列表渲染,数组 -->
<view wx:for='{{arr}}' wx:key='{{index}}'>
{{index}} {{item}}
</view>
<!-- 列表渲染,对象 -->
<view wx:for='{{list}}' wx:key='{{index}}'>
{{item}} {{item.name}} {{item.age}}
</view>
<!-- 条件渲染 -->
<view wx:if="{{isLogin}}">已登陆</view>
<view wx:else>未登陆</view>
<!-- hidden -->
<view hidden='{{hidden}}'>hidden</view>
对应js文件部分代码
data: {
msg:"hello world!",
img:"/images/pig.jpg",
arr:['a','b','c','d'],
list:[{
name:'jack',
age:18
},{
name:'lucy',
age:20
}],
isLogin:true,
hidden:true
}
预览效果
1.3 WXSS
1.3.1 尺寸单位
rpx: 可以根据屏幕宽度进行自适应。
设备 | rpx换算px | px换算rpx |
---|---|---|
iPhone5 | 1rpx = 0.42px | 1px = 2.34rpx |
iPhone6 | 1rpx = 0.5px | 1px = 2rpx |
iPhone6 Plus | 1rpx = 0.552px | 1px = 1.81rpx |
建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
1.3.2 样式导入
语法:@import '相对路径';
1.3.3 第三方样式库
第三方库安装步骤
第一步 miniprogram在终端打开,并输入npn init
,最后会生成package.json文件
第二步 miniprogram在终端打开,并输入安装指令
第三步 点击构建npm。到这步骤时,目录已经生成vant-weapp文件
第四步 点击详情,勾选“使用npm模块”
使用
1.4 JS
1.4.1 bind VS catch
bind事件绑定不会阻止冒泡事件,catch事件绑定可以阻止冒泡事件。
//发生冒泡
<view class='box' bind:tap='onTapBoxHandler'>
<view class='child' bind:tap='onTapChildHandler'></view>
</view>
//不发生冒泡
<view class='box' catch:tap='onTapBoxHandler'>
<view class='child' catch:tap='onTapChildHandler'></view>
</view>
1.5 发送请求
二 云开发
2.1 云数据库
云开发提供了一个 JSON 数据库
2.1.1 关系型数据库 VS JSON 数据库
关系型数据库 | 文档型数据库 |
---|---|
数据库 database | 数据库 database |
表 table | 集合 collection |
行 row | 记录 record / doc |
列 column | 字段 field |
2.1.2 权限
控制 | 对应权限 |
---|---|
小程序控制 | 读写数据库权限控制限制 |
云函数控制 | 拥有所有读写数据库的权限 |
控制台控制 | 拥有所有读写数据库的权限 |
2.1.3 增删改查
初始化数据库const db=wx.cloud.database();
插入
//wxml文件
<button bind:tap='insert'>插入数据</button>
//对应js文件部分代码(写法一:回调函数写法)
insert:function(){
db.collection('user').add({
data:{
name:'boom',
age:10
},
success:res=>{
console.log(res);
},
fail:err=>{
console.log(err);
}
})
}
//对应js文件部分代码(写法二:promise写法)
insert:function(){
db.collection('user').add({
data:{
name:'chen',
age:11
}
}).then(res => {//成功则执行then
console.log(res);
}).catch(err => {//失败则执行catch
console.log(err);
})
}
更新
//wxml文件
<button bind:tap='updata'>更新数据</button>
//对应js文件部分代码
updata:function(){
db.collection('user').doc('3e1ef27b5d2fc64309d0c9fb3e5b9317').update({
data:{
name:'boom3'
}
}).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
})
}
查询
//wxml文件
<button bind:tap='search'>查询数据</button>
//对应js文件部分代码
search:function(){
db.collection('user').where({
name:'boom4'
}).get().then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
}
删除单条记录
//wxml文件
<button bind:tap='delete'>删除数据</button>
//对应js文件部分代码
delete:function(){
db.collection('user').doc('08c248de-9ce9-414a-a328-efcf2ea4750b')
.remove()
.then(res=>{
console.log(res);
})
.catch(err=>{
console.log(err);
})
}
2.2 云函数
2.2.1 案例一:云函数sum
//wxml文件
<button bind:tap='sum'>调用云函数sum</button>
//对应js文件部分代码
sum:function(){
wx.cloud.callFunction({
name:'sum',
data:{
a:2,
b:3
}
}).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
})
}
//云函数sum下的index.js
exports.main = async (event, context) => {
return {
add:event.a+event.b
}
}
执行结果
2.2.2 案例二:微信登陆
传统微信登陆
云开发微信登陆
//wxml文件
<button bind:tap='getOpenId'>获取当前用户的openid</button>
//对应js文件部分代码
getOpenId:function() {
wx.cloud.callFunction({
name: 'login',
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
}
//云函数login下的index.js
执行结果
2.2.3 案例三:批量删除
批量删除云数据库记录只能通过云函数实现
//wxml文件
<button bind:tap='batchDelete'>批量删除数据</button>
//对应js文件部分代码
batchDelete: function () {
wx.cloud.callFunction({
name: 'batchDelete',
}).then(res => {
console.log(res);
}).catch(err => {
console.error(err);
})
}
//云函数batchDelete下的index.js
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database();
exports.main = async (event, context) => {
try {
return await db.collection('user').where({
age: 11
}).remove();
}catch(e){
console.error(e);
}
}
2.3 云存储
2.3.1 文件上传
//wxml文件
<button bind:tap='upload'>上传文件</button>
//对应js文件部分代码
upload:function(){
//1.选择图片
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],//是否压缩
sourceType: ['album', 'camera'],//选择相册还是相机
success(res) {
const tempFilePaths = res.tempFilePaths;
console.log(tempFilePaths);
//2.上传所选图片
wx.cloud.uploadFile({
cloudPath: new Date().getTime()+'.png', // 上传至云端的路径。为避免重名导致文件覆盖,所以用时间戳命名
filePath: tempFilePaths[0], // 小程序临时文件路径
success: res => {
//3.返回fileID
console.log(res.fileID);
//4.fileID存储到云数据库
db.collection('images').add({
data:{
fileID: res.fileID
}
}).then(res=>{
console.log(res);
}).catch(err=>{
console.error(err);
})
},
fail: console.error
});
}
});
}
执行结果
控制台
云存储
云数据库
2.3.2 图片展示
//wxml文件
<button bind:tap='getFile'>文件展示</button>
<block wx:for='{{images}}' wx:key='{{index}}'>
<image src='{{item.fileID}}'></image>
</block>
//对应js文件部分代码
getFile:function(){
//1.获取用户openID
wx.cloud.callFunction({
name: 'login',
}).then(res => {
console.log(res);
//2.查询数据库中对应openID用户的记录
db.collection('images').where({
_openid: res.result.openid
}).get().then(res2 => {
console.log(res2);
//3.将查询到的记录赋值给images数组
this.setData({
images:res2.data
})
}).catch(err2 => {
console.log(err2);
})
}).catch(err => {
console.log(err);
})
}
执行结果
2.3.3 文件下载
//wxml文件
<block wx:for='{{images}}' wx:key='{{index}}'>
<image src='{{item.fileID}}'></image>
<button size='mini' bind:tap='downloadFile' data-fileid='{{item.fileID}}'>下载</button>
</block>
//对应js文件部分代码
downloadFile:function(event){
wx.cloud.downloadFile({
//1.获取文件fileID
fileID: event.target.dataset.fileid
}).then(res => {
console.log(res.tempFilePath);
//2.保存图片到手机相册
wx.saveImageToPhotosAlbum({
filePath:res.tempFilePath,
success(res2){
wx.showToast({
title: '保存成功!'
})
}
})
}).catch(err => {
console.error(err);
})
}
三 电影小程序案例
3.1 界面预览
页面一:电影列表
页面二:电影详情及评价
页面三:个人中心
3.2 项目工程
3.3 配置
app.json文件
{
"pages": [
"pages/movie/movie",
"pages/profile/profile",
"pages/comment/comment"
],
"window": {
"navigationBarBackgroundColor": "#E54947",
"navigationBarTitleText": "最新电影",
"navigationBarTextStyle": "white"
},
"sitemapLocation": "sitemap.json",
"tabBar": {
"color":"#000",
"selectedColor":"#E54947",
"list": [{
"pagePath": "pages/movie/movie",
"text": "电影",
"iconPath": "images/film.png",
"selectedIconPath": "images/film-actived.png"
},{
"pagePath": "pages/profile/profile",
"text": "个人中心",
"iconPath": "images/profile.png",
"selectedIconPath": "images/profile-actived.png"
}]
}
}
3.4 页面一:电影列表
说明:该页面的重点在于解析豆瓣列表api,将数据信息赋值给
movieList
数组
首先,需要安装request-promise
npm install --save request
npm install --save request-promise
movie.wxml
<view class='movie' wx:for='{{movieList}}' wx:key='{{index}}'>
<image src='{{item.images.small}}' class='movie-img'></image>
<view class='movie-info'>
<view class='movie-title'>{{item.title}}</view>
<view>观众评分:
<text class='movie-score'>{{item.rating.average}}分</text>
</view>
<view>主演:
<text wx:for='{{item.casts}}'> {{item.name}}</text>
</view>
<view>年份:{{item.year}}</view>
</view>
<button class='movie-comment' bind:tap='gotoComment' data-movieid='{{item.id}}'>评价</button>
</view>
movie.js
Page({
/**
* 页面的初始数据
*/
data: {
movieList:[]
},
// 跳转至对应评价页面
gotoComment:function(event){
wx.navigateTo({
url: `../comment/comment?movieid=${event.target.dataset.movieid}`,
})
},
//获取电影列表
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)//字符串转对象。为避免覆盖,使用concat拼接数组
});
wx.hideLoading();
}).catch(err => {
wx.hideLoading();
console.error(err);
});
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.getMovieList();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
this.getMovieList();
}
})
云函数movieList中的js文件
// 云函数入口文件
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?apikey=0df993c66c0c636e29ecbb5344252a4a&start=${event.start}&count=${event.count}`)//豆瓣列表api
.then(function (res) {
console.log(res);
return res;
})
.catch(function (err) {
console.error(err);
});
}
3.5 页面二:电影详情及评价
3.5.1 电影详情
说明:电影详情部分的重点在于解析豆瓣电影详情api,将数据信息赋值给
detail
数组
comment.wxml对应代码
<view class='detail-container' style='background:url({{detail.images.large}}) no-repeat top/cover'></view>
<view class='detail-mask'></view>
<view class='detail-info'>
<image src='{{detail.images.large}}' class='detail-img'></image>
<view class='detail'>
<view class='detail-nm'>{{detail.title}}</view>
<view>{{detail.original_title}}</view>
<view class='detail-sc'>{{detail.rating.average}}分</view>
<view>{{detail.countries[0]}}/{{detail.durations[0]}}</view>
<view>导演:{{detail.directors[0].name}}</view>
</view>
</view>
<view class='desc'>{{detail.summary}}</view>
comment.js对应代码
onLoad: function (options) {
this.setData({
movieId: options.movieid
});
wx.cloud.callFunction({
name:'getDetail',
data:{
movieid:options.movieid
}
}).then(res=>{
this.setData({
detail:JSON.parse(res.result)
})
console.log(res);
}).catch(err=>{
console.error(err);
})
}
云函数getDetail中的js文件
// 云函数入口文件
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/subject/${event.movieid}?apikey=0df993c66c0c636e29ecbb5344252a4a`)
.then(function (res) {
console.log(res);
return res;
})
.catch(function (err) {
console.error(err);
});
}
3.5.2 电影评价
该部分的思路是:
- 当评分和评价发生改变时,通过
bind:change
修改score
和content
的值。- 上传图片:
- 点击上传按钮
- 选择文件(
wx.chooseImage
)- 得到临时路径
tempFilePaths
的值存入images
数组- 通过wxml展示图片预览
- 上传云存储:
- 点击提交按钮
- 将图片上传至云存储(
uploadFile
)- 得到
fileID
的值存入fileIds
数组- 保存至云数据库:
- 在云数据库中建立相应的
collection
- 将
content
、score
、movieId
、fileIds
保存至数据库
comment.wxml对应代码
<van-field
value='{{content}}'
placeholder='写一些评价吧'
bind:change='onContentChange'
/>
<van-rate value='{{score}}' bind:change='onScoreChange'/>
<view>
<image src='{{item}}' wx:for='{{images}}' wx:key='index' class='comment-img'></image>
</view>
<van-button type="warning" bind:tap='uploadImg'>上传图片</van-button>
<van-button type="danger" size='large' bind:tap='submit'>提交评价</van-button>
comment.js对应代码
// pages/comment/comment.js
const db=wx.cloud.database();
Page({
/**
* 页面的初始数据
*/
data: {
detail:{},
content:'',
score:5,
images:[],
fileIds:[],
movieId:-1
},
uploadImg:function(){
//1.选择图片
wx.chooseImage({
count:9,
sizeType:['original','compressed'],
sourceType:['album','camera'],
success: res=>{
const tempFilePaths=res.tempFilePaths;//临时路径
console.log(tempFilePaths);
this.setData({
images: this.data.images.concat(tempFilePaths)//将图片临时路径存入images数组
})
},
})
},
submit:function(){
wx.showLoading({
title: '评论中',
})
console.log(this.data.content);
console.log(this.data.score);
let promiseArr=[];
//上传图片到云存储
for (let i = 0; i < this.data.images.length; i++) {//遍历images数组
promiseArr.push(
new Promise(
(resolve,reject)=>{
let item = this.data.images[i];
let suffix=/\.\w+$/.exec(item)[0];//获取文件后缀
wx.cloud.uploadFile({
cloudPath:new Date().getTime()+suffix,
filePath:item,
success:res=>{
console.log(res.fileID);
this.setData({
fileIds:this.data.fileIds.concat(res.fileID)
});
resolve();
},
fail:err=>{
console.error(err);
}
})
}
)
);
}
Promise.all(promiseArr).then(res=>{
//插入数据到云数据库
db.collection('comment').add({
data:{
concent:this.data.content,
score:this.data.score,
movieId:this.data.movieId,
fileIds: this.data.fileIds
}
}).then(res=>{
wx.showToast({
title: '评价成功',
})
}).catch(err=>{
wx.hideLoading();
wx.showToast({
title: '评价失败',
})
console.error(err);
})
})
},
//当评论框内容变化时,修改content值
onContentChange:function(event){
this.setData({
content:event.detail
})
},
//当评分分数变化时,修改score值
onScoreChange: function (event) {
this.setData({
score: event.detail
})
}
})
3.6 页面三:个人中心
profile.wxml
<view class='profile'>
<open-data type='userAvatarUrl' class='profile-img'></open-data>
<open-data type='userNickName'></open-data>
</view>
<button open-type='getUserInfo' bindgetuserinfo='onGotUserInfo'>获取用户信息</button>
profile.js
onGotUserInfo:function(e){
console.log(e);
},