小程序简介
小程序是安装运行在微信上的app,是一种轻量级的应用。
特点:
- 依托微信,有强大的用户基数,易发展使用群体
- 体量小,无需安装、即用即走
- 不能调用浏览器提供的的BOM和DOM相关API,但可以调用微信提供的API,比如扫码支付、地理定位等
小程序三剑客
- wxml(结构,如同房子的钢材结构)
- wxss(样式,如同房子的装修)
- javascript(功能,如同房子的灯光、供水、供电)
小程序结构
基本结构
小程序会创建时候会自动创建项目模板,主要组成有:
- pages文件夹,存放页面
- utils文件夹,存放可供全局调用的工具性质的js模块
- app.js、app.json、app.wxss,整个app的逻辑、配置、样式文件
- project.config.json,项目的配置文件
- stemap.json配置是否允许被微信搜索到
页面结构
微信官方建议将页面文件放入pages文件夹中,每个页面包含四个同名文件
- .json 后缀的 JSON 配置文件(页面配置,配置窗口的外观、表现等)
- .wxml 后缀的 WXML 模板文件(页面结构)
- .wxss 后缀的 WXSS 样式文件(页面样式)
- .js 后缀的 JS 脚本逻辑文件(页面脚本,存放页面数据,事件处理函数)
基础知识
- 小程序中任何页面宽度都是750rpx 在不同大小设备中 1rpx对应不同的px 故要使用rpx为单位 实现设备等比适配
- 小程序最外层标签固定为page标签(隐形的)
小程序宿主环境
小程序不是安装在操作系统之上的,而是直接安装在微信之上。小程序宿主环境即是手机微信,因此小程序不需要为不同设备作适配。
而微信为小程序提供了一系列服务支持,包括如下:
- 通信模型
- 运行机制
- 组件
- API接口
小程序通信模型
主体:JS逻辑层和页面渲染层(wxml+wxss)
小程序运行机制
运行机制包含启动和页面渲染两部分
小程序的启动
- 请求微信服务器,将代码包下载到本地
- 解析app.json全局配置文件
- 执行app.js入口文件,调用**App()**创建小程序实例
- 渲染小程序首页
- 启动完成
页面渲染过程
- 加载页面的.json配置文件
- 加载页面的.wxml结构和.wxss样式
- 执行页面.js文件,调用**Page()**方法创建页面实例
小程序基本操作
创建页面
一个页面包含四个文件,每一个要用页面的都需要在app.json中注册,加入到其pages标签中
创建方法:
直接在pages标签中加入创建的页面路径,保存后系统会自动创建对应的四个页面文件;
若手动创建文件,则创建后也需要在app.json中注册新创建的页面
数据绑定
①、提前将需要的数据定义好,每个数据都有一个对应的名称
data: {
// 定义字符串数据name,内容为小辣鸡
name:'小辣鸡'
},
②、在wxml中使用Mustache语法(双大括号)使用该数据
<button type="primary">{{name}}</button>
保存数据的一般方法:
在data中保存、定义var全局变量、定义全局常量const
注意:data是页面数据和数据库数据的中转站,需要将页面数据和数据库数据都保存在data中,再js代码中进行数据交互
双括号法的三个应用
- 动态绑定内容,将{{数据名称}}直接用于显示
- 动态绑定属性,将{{数据名称}}赋值给某属性
- 运算(三元运算、算术运算)
data的作用
- 保存页面数据和数据后台数据
- 根据data动态控制页面组件状态
- 各个页面之间的数据交互,如通过全局的data数据来交互
事件绑定
事件:渲染层到逻辑层的通信方式
小程序中有三种事件:
-
tap,类似click点击事件
-
input,文本框输入事件
-
change,状态改变事件
用法:bindtap、bindinput、bindchange或bind:tap、bind:input、bind:change
事件对象的属性
将每个事件分别抽象成一个对象event,每个对象有其属性如下
target和currentTarget区别
当点击内层组件时,点击事件会以冒泡的方式向外扩散,则event.target指的是最内层的源头组件对象,event.currentTarget指的是最外层的当前对象
bindTap语法格式
定义print函数
print(e){//会将点击事件对象作为形参传入到函数中
console.log(e);
},
绑定事件
<button bindtap="print">普通按钮</button>
为data赋新值
【this.data.数据名称】代表当前数据,赋值语句如下
this.setData({
count:this.data.count+1
})
为事件处理函数传参
加入属性,data-参数名=”{{数据内容}}“
bindIput语法格式
显示data数据
定义数据:pre_string:"预定义字符串",
显示内容:<input value="{{pre_string}}"></input>
将输入数据同步到data中
定义数据
info:"0"
定义同步函数
syncInputTo_data(e){
this.setData({
info:e.detail.value//拿到输入框当前value
})
},
输入框绑定同步函数
<input value="{{pre_string}}" bindinput="syncInputTo_data"></input>
WXML模板语法
wx:if条件渲染
小程序用wx:if=“{{条件语句}}”控制当前语句是否被渲染
<!-- 注意三个等号指的是类型及数据都相等 -->
<view wx:if="{{info===0}}">1</view>
<view wx:elif="{{info==='1'}}">2</view>
<view wx:else>其它</view>
blcok的使用
block不是一个组件,而是单纯的包裹作用的容器,不会渲染出任何效果
<!-- 用block实现控制多个组件的显示 -->
<block wx:if="{{false}}">
<view>A</view>
<view>B</view>
<view>C</view>
</block>
hidden的使用
与if相反,条件为真时渲染
<view hidden="{{条件}}">AAAAAAAAAA</view>
hidden与wx:if的比较
hidden只是为元素加了一条display:none属性,而wx:if则是动态的增加或删除一个元素,资源消耗大
使用建议
wx:for遍历数组
定义数组
array1:['玛卡巴卡','唔西迪西','依古比古','飞飞鱼','叮叮车']
遍历时,{{index}}代表下标,{{item}}代表数据内容
<!-- wx:for的使用 -->
<view wx:for="{{array1}}">
item下标:{{index}}
item内容:{{item}}
</view>
也可通过wx:for-index=“下标名”/wx:for-item="子项目名"修改默认的名称
wx:key的使用
遍历数组时,建议加入wx:key="索引名"来提高页面渲染效率
页面跳转及数据传递
跳转方式
wx.navigateTo -页面跳转
- 不关闭当前页面,跳转到其它页面(非导航栏页面),之后可以返回当前页
- url中可以加上"?param1=value1¶m=value2…paramN=valueN"
wx.redirectTo -重定向
- 关闭当前页,跳转到其它页面
wx.switchTo
- 切换到导航栏页面
页面数据传递
方案一:全局globalData
在全局的app.js文件中定义全局globalData,在各个子页面中通过getapp()来获取app对象,再获取其中的全局data
方案二:缓存
在某页面中通过wx.setStorageSync设置数据,再在其它页面中通过wx.getStorageSync取数据
区别:全局globalData是每次运行时才存在,而缓存是直接存储在手机中,设置过一次缓存以后都存在,除非缓存过期
WXSS
wxss与css区别
wxss与css相比主要是引入了rpx单位、import导入方式,并且删去了少部分选择器、样式规则,其它都一致
rpx单位
小程序会将任意设备的宽度等分成750份,每份为1rpx,故屏幕更大的设备1rpx对应更多的px
以rpx为单位能使得内容在不同的机型中做到自动设配(等比例)
import导入方式
小程序不支持链入式,但支持导入式,用法为@import “相对路径”;
App.json全局配置
"backgroundTextStyle": "light",//可改成dark,显示下拉效果
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Weixin",
"navigationBarTextStyle": "black",//只支持black或white
"enablePullDownRefresh": true,
小程序组件
基本组件
tabBar导航栏
六个组成部分
每个tab的属性
注意:配置tabBar时,list第一个内容必须是当前页面(首页)
自定义组件
创建方法
-
项目根目录创建components文件夹,右键创建component,作为自定义组件
-
在创建的自定义组件中编写wxml和wxss
-
在要使用自定义组件的页面中注册当前组件,方法是在json文件添加usingComponents字段
"usingComponents": { "test":"../../../components/test"//取名为test标签 }
网络请求
要求:
- 只能请求HTTPS类型的接口
- 必须将接口的域名配置到白名单中
GET请求
①、在js文件中定义请求函数
②、在button中绑定请求函数
云开发
环境初始化
在project.config.json中设置项目路径
"miniprogramRoot": "项目根目录/",
"cloudfunctionRoot": "云开发根目录/",
在app.js中设置环境id
if (!wx.cloud) {
console.error('请使用 2.2.3 或以上的基础库以使用云能力');
} else {
wx.cloud.init({
// env 参数说明:
// env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源
// 此处请填入环境 ID, 环境 ID 可打开云控制台查看
// 如不填则使用默认环境(第一个创建的环境)
// env: 'my-env-id',
env:'cloud1-1g5hgnmx860ce449',
traceUser: true,
});
}
云数据库
基本模板
const db=wx.cloud.database()
var dbtools =db.collection('数据库名').【限制条件】.【具体需求】.then(res=>{
//请求结束后的回调函数内容
})
【限制条件】有哪些?
//【限制条件】包括限定id、限定记录数量、加where条件、排序等等,例子如下
.doc('xxxxxxx具体id')//指定id操作
.where({//限定字段取值
字段:限定的取值
})
.orderBy('排序字段名','排序方式')//asc表升序、desc表降序
.limit(n)//限定为前n条记录
.skip(n)//跳过前n条数据,一般用作分页处理
.filed({//过滤成指定字段
字段:true
})
【具体需求】有哪些?
//【具体需求】包括增、删、改、查、获取数量、监听数据等等
.add({//增加
data:{
//增加什么
字段:数值
}
})
.remove()//删除
.update({//更新
data:{
//更新成什么
字段:数值
}
})
.get()//查询
.watch({//监听
onChange: res=> {
//数据变化时回调
})
},
onError: err=> {
//发送错误时回调
}
})
}
)
.count()//获取数量
collection
查询数据
注意:查询数据前先设置好每个表的权限
获取数据+保存到本地data
const db=wx.cloud.database()//获取数据库
//普通查询
const todos = db.collection("user_info").get({
success:res=>{
console.log(res.data);
this.setData({
user:res.data//保存数据到本地
})
}
});
在wxml文件中显示数据:
<button bindtap="getdata">
获取数据
</button>
<view wx:for="{{user}}">{{item.nickname}}</view>
总例:
getdata(){
// 普通查询
const todos = db.collection("user_info").get({
success:res=>{
console.log(res.data);
this.setData({
user:res.data
})
}
});
// 指定查询
const todos = db.collection("user_info").doc("17e3426e622de3a8133f4c640c4660db").get({
success:res=>{
console.log(res);
this.setData({
user:res.data
})
}
});
//promise语法
const todos = db.collection("user_info").get().then(res=>{
console.log(res);
this.setData({
user:res.data
})
});
// where条件查询
const todos = db.collection("user_info").where({
nickname:"王明"
}).get().then(res=>{
console.log(res);
this.setData({
user:res.data
})
});
},
添加数据
//添加数据
adddata(){
// 加载效果
wx.showLoading({
title: '正在加载中...',
mask:true
})
const todos = db.collection("user_info").add({
data:{
nickname:"中国有嘻哈",
id:"asda"
}
}
).then(res=>{
console.log(res);
this.setData({
user:res.data
});
wx.hideLoading();
});
},
删除数据
//删除数据
delete_data(){
// 指定id
const todos=db.collection("user_info").doc("617ef50c622e168b0b54b9371bb48c0d").remove({
data:{
nickname:"王阳明"
}
}).then(res=>{
console.log(res);
})
},
修改数据
//修改数据
update_data(){
//指定id
// 注意只能修改字段中有open_id的数据
const todos=db.collection("user_info").doc("617ef50c622e168b0b54b9371bb48c0d").update({
data:{
nickname:"王阳明"
}
}).then(res=>{
console.log(res);
})
//where条件修改
const todos=db.collection("user_info").where({
nickname:"王阳明"
}).update({
data:{
nickname:"张翼德"
}
}).then(res=>{
console.log(res);
})
},
读取页面数据
读取表单提交数据
wxml代码
<view class="biaodan">
<form bindsubmit="add_user">
<input name="name" placeholder="请输入名称"></input>
<input name="pwd" placeholder="请输入密码"></input>
<textarea name="备注" placeholder="请输入备注"></textarea>
<button type="primary" form-type="submit">添加数据</button>
<button type="default" form-type="submit">重置</button>
</form>
</view>
js代码
//读取表单添加用户信息
add_user(res){
console.log(res);
var value=res.detail.value;//直接存储value对象
const todos = db.collection("user_info").add({
data:{
value
}
}
).then(res=>{
console.log(res);
this.setData({
user:res.data
});
wx.hideLoading();
});
},
读取input输入框内容
wxml代码
<input bindinput="getinput" style="border: 1px solid #536545;"></input>
js代码
// 获取输入框内容
getinput(req){
var i=req.detail.value;
console.log(req.detail.value);
this.setData({
input:i
})
},
实时更新数据
过程:加载页面时先get到数据后设置到本地data,渲染出来;定义监听器,监听器中再写setdata
wxml代码
<view wx:for="{{paper}}">//渲染到前端
<text>{{item.title}}</text>
</view>
js代码
onLoad: function (options) {
//持续监听
const watcher = db.collection('paperList').watch({
onChange: res=> {//首次加载会执行一次,每次更新都会再执行一次
console.log('snapshot', res.docs),
this.setData({
paper:res.docs//保存到本地
})
},
onError: err=> {
console.error('the watch closed because of error', err)
}
})
// login();
},
command
在对collection数据操作时,用where只能限定某些字段等于什么,而在where中使用command的方法就能实现更高级的限定
比较运算符
用比较运算来限定数据,比如大于、小于、属于、等于
const db=wx.cloud.database()//获取数据库
const _ = db.command//获取command
const openID = 'xxx'
db.collection('articles').where({//高级限定
num0: _.eq(666),//num0等于666
num1:_.gt(10),//num1大于10
num2:_.gte(5),//num2大于等于5
num3:_.lt(3)//num3小于3
num4:_.lte(5),//num4小于等于5
num5:_.in([10,20,30]),//num5在集合10/20/30之中
num6:_.nin([10,20,30])//num6不在集合10/20/30之中
}).get()
逻辑运算符
用逻辑运算来实现多个条件,比如与、或、非
const db=wx.cloud.database()//获取数据库
const _ = db.command//获取command
const openID = 'xxx'
db.collection('articles').where({//用and限定给同一字段加多个条件
num0: _.and(_.gt(10),_.lt(20))//num0大于10且小于20
}).get()
db.collection('articles').where(_.and([//用and给不同字段做多个限定
{
num0:_.lt(20)//num0小于20
},
{
num1:_gt(11)//num1大于11
}
])).get()
//or的用法和and的一致
字段操作符
const db=wx.cloud.database()//获取数据库
const _ = db.command//获取command
db.collection('articles').where({
tittle:_.exists(true),//限定存在tittle字段
tittle:_.size(3),//tittle数组只有三个元素
tittle:_.all,//tittle数组中包含["数码","科技"]
tittle:_.elemMatch({
age:_.lt(18)//tittle对象数组中,至少存在一个对象age<18
}),
}).get()
update更新字段
const db=wx.cloud.database()//获取数据库
const _ = db.command//获取command
db.collection('articles').where({
data:{
hits:_.inc(1),//自增1,负数变自减
hits:_.remove(),//删除hits字段
hits:_.set(123)
}
}),
}).get()
//对于更新对象字段,注意set更新和不同set更新的区别:set更新会将旧数据清零,变成为set里面的数据;
//而不用set时只修改data中指定的字段,其它未涉及到的字段不改变
云函数
将页面js写的函数代码,部署在云端服务器上,就变成了云函数,主要就是用来操作数据库。
好处:
- 实现前后端分离,客户端只需要调用接口就可以
- 减少重复的代码编写,将重复的数据库操作代码封装起来,供页面调用
注意点:每次更新云函数之后,必须部署上去才会生效!!!
新建云函数
右键云开发文件夹、新建Node.js云函数
初始化
因为在云函数中需要用到微信提供的sdk提供的api,所以要安装依赖的js包,即通过node.js的npm指令安装。npm是前端的包管理工具,就相当于java领域中maven的作用。
①安装Node.js
官网:https://nodejs.org/en/download/
②检查是否安装成功
打开cmd,输入如下指令,若出现版本号则是安装成功
node -v
③为云函数安装依赖
右键需要依赖的文件夹(各云函数的父文件夹),点击【在内建终端内打开】,输入指令:
npm install --save wx-server-sdk@latest
④重启开发工具
如果重启后还是报错,比如报XXX is not a function.那么此时把这个云函数删除,再重新创建,然后下依赖即可。
云函数使用
传递参数
在小程序端,通过设置wx.cloud.callFunction的data来传递数据。
在云函数端,通过event接收数据。
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db=cloud.database();
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
// return db.collection("paper-info").get()
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
}
}
常见需求
css类选择器不起作用,消除button默认样式
法一:用行内式加style,因为style优先级很高
法二:用.button和.button::after设置所有按钮样
获取表单数据并且提交到js
- 新建form,设置bindsubmit属性,内容为对应的提交函数
- 在form中新建各种表单,如input,设置name作为提交数据的key,value设置为当前key对应的数据
- 在某个button中设置, form-type=“submit”,点击当前按钮后就会提交表单,执行绑定的提交函数
- 在from提交函数中设置参数req,则通过req.detail.value获取数据了
下拉触底刷新:原理是先获取m条数据渲染出来,当监听下拉触底时,则跳过当前已经渲染的数据再二外取n条数据