1.搭建项目
创建新项目,你需要先注册一个小程序账号,当然也可以暂时先使用一个测试的 appid
小程序配置
2.小程序的生命周期函数
- onLoad 生命周期函数--监听页面加载
- onReady 生命周期函数--监听页面初次渲染完成
- onShow 生命周期函数--监听页面显示
- onHide 生命周期函数--监听页面隐藏
- onUnload 生命周期函数--监听页面卸载
- onPullDownRefresh 页面相关事件处理函数--监听用户下拉动作
- onReachBottom 页面上拉触底事件的处理函数
- onShareAppMessage 用户点击右上角分享
注意: - onLoad 只会在小程序中执行一次,比如当前页面是 a 页面,我们在 onLoad 发送请求去获取数据,然后我们切到 b 页面,再切回 a 页面的时候,onLoad 不会再触发,也就是不会再重新发请求获取数据
- onShow 跟 onLoad 不同,每次切回 a 页面的时候都会触发这个生命周期函数
- 所以到底是在 onLoad 发请求还是在 onShow 发请求,要根据实际的业务去做
3.获取数据
- 小程序只支持 https
- 需要到小程序后台配置域名白名单
- 项目中请求了非 https 和不在域名白名单上的接口会报错
- 开发时可以取消域名校验,就可以请求任意接口,设置方法 小程序右上角详情 =》本地设置 => 不校验合法域名.
a 发送请求
文档地址文档首页 => api => 网络 => 发起请求
// 示例
wx.request({
url: "test.php", //仅为示例,并非真实的接口地址
data: {
x: "",
y: ""
},
method: "get", // 也可以是post
header: {
"content-type": "application/json" // 默认值
},
// 成功的回调
success(res) {
console.log(res.data);
},
// 失败的回调
fail(error) {
console.log(error);
},
// 不管是成功还是失败都会调用此方法
complete() {
console.log("done");
}
});
b使用promise封装请求
//config.js
let env = 'prod';
let baseUrl = "";
if (env === "dev") {
// 本地地址
baseUrl = "https://localhost:3009"
} else if (env === "prod") {
baseUrl = "https://huruqing.cn:3009"
}
// 导出 export { baseUrl }
2. ##### 新建/utils/reques.js 文件,内容如下
```js
import { baseUrl } from "./config.js";
/**
* 封装请求
* url:请求地址
* data:请求参数
* method: 请求类型
*/
const request = (url, data, method) => {
// 获取token,登录时存的
let token = wx.getStorageSync("token");
url = baseUrl + url;
return new Promise((resolve, reject) => {
// 请求
wx.request({
url,
method,
data,
header: {
"user-token": token
},
success: res => {
if (res.data.code == 666) {
resolve(res.data);
} else if (res.data.code == 603) {
wx.removeStorageSync("token");
wx.showModal({
title: "提示",
content: "登录已过期,是否重新登录",
success(res) {
if (res.confirm) {
// 跳转到个人中心页面
wx.switchTab({
url: "/pages/my/my"
});
} else if (res.cancel) {
console.log("用户点击取消");
}
}
});
} else {
reject(res.data.msg);
}
},
fail: err => {
reject("网络异常");
}
});
});
};
const get = (url, data) => {
return request(url, data, "get");
};
const post = (url, data) => {
return request(url, data, "post");
};
export default {
get,
post
};
c挂载到 app,在页面中就不需要重复加载
// app.js
import request from "./utils/request.js";
App({
onLaunch: function() {
this.$get = request.get;
this.$post = request.post;
}
});
d在页面中使用
// my.js
// 获取app对象
const app = getApp();
Page({
onShow() {
this.getData();
},
// 发送请求
getData() {
let url = "xxxxx";
let data = { xxx: xxx };
app
.$get(url, data)
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
}
});
4.页面数据修改
1.data和setdata
Page({
data: {
count:1
},
changeCount() {
// 获取data里count的值
let count = this.data.count;
// 加1
this.setData({
count: ++count
})
}
})
2.插值表达式 {{}}
, wxml中所有的变量都使用{{}}
3.条件渲染 wx:if
4.列表渲染 wx:for
默认有item和index
5.双重wx:for
时需要其中一个指定item和index
5.绑定事件
1文档地址
// 例子1
Click me!
// js
Page({
tapName: function(event) {
console.log(event)
// 小程序事件不能像vue那样传参,只能通过自定义属性来传参
let {username} = event.currentTarget.dataset.username;
console.log(username);
}
})
2常见事件类型
类型 | 触发条件 | 最低版本 |
touchstart | 手指触摸动作开始 | |
touchmove | 手指触摸后移动 | |
touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 | |
touchend | 手指触摸动作结束 | |
tap | 手指触摸后马上离开 | |
longpress | 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 | 1.5.0 |
longtap | 手指触摸后,超过350ms再离开(推荐使用longpress事件代替) | |
transitionend | 会在 WXSS transition 或 wx.createAnimation 动画结束后触发 | |
animationstart | 会在一个 WXSS animation 动画开始时触发 | |
animationiteration | 会在一个 WXSS animation 一次迭代结束时触发 | |
animationend | 会在一个 WXSS animation 动画完成时触发 | |
touchforcechange | 在支持 3D Touch 的 iPhone 设备,重按时会触发 |
6.页面跳转
-
小程序常用两种方式,普通页面跳转和tab栏切换(跳转)
a.跳转方式一 通过组件navigator进行跳转,需要指定跳转类型open-type
跳转到新页面
切换 Tab
在当前页打开
打开绑定的小程序
b.跳转方式二 通过js进行跳转
// 普通页面跳转
wx.navigateTo({
url: '/product/product'
})
// tab栏切换
wx.switchTab({
url: '/index'
})
7.路由传参
路由传参常用方式有以下几种
1.通过url传参
// 产品列表页面
详情
// 或者
wx.navigateTo({
url: '/pages/detail/detail?productId=12345'
})
// 产品详情页面
Page({
onLoad: function(option){
let productId = option.productId;
console.log(productId);
}
})
上面的方式的参数不能是对象,如果需要传对象,可以将对象转成json字符串,然后拼接到url后面,在接收页面再将json字符串转成对象
let info = {
a:2,
b:3
}
let infoStr = JSON.stringfy(info);
wx.navigateTo({
url: '/pages/detail/detail?infoStr='+infoStr
})
// 产品详情页面
Page({
onLoad: function(option){
let infoStr = option.infoStr;
let info = JSON.parse(infoStr);
}
})
2.通过事件传参
a官网实例
// 产品列表页
wx.navigateTo({
url: url: '/pages/detail/detail'
events: {
// 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
acceptDataFromOpenedPage: function(data) {
console.log(data)
},
someEvent: function(data) {
console.log(data)
}
...
},
success: function(res) {
// 通过eventChannel向被打开页面传送数据
res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'test' })
}
})
// 产品详情页
Page({
onLoad: function(option){
const eventChannel = this.getOpenerEventChannel()
eventChannel.emit('acceptDataFromOpenedPage', {data: 'test'});
eventChannel.emit('someEvent', {data: 'test'});
// 监听acceptDataFromOpenerPage事件,获取上一页面通过eventChannel传送到当前页面的数据
eventChannel.on('acceptDataFromOpenerPage', function(data) {
console.log(data)
})
}
})
b简化例子 以上例子,在跳转的时候列表页可以向详情页传数据,详情页也可以向列表页传数据,平时我们经常都只是传数据,很少需要回传,以下是简化的例子
// 列表页
wx.navigateTo({
url: "/pages/home/detail/detail",
success: function(res) {
// 通过eventChannel向被打开页面传送数据
let data = {productId: 'sadf2323',productName:'金龙鱼花生油'};
res.eventChannel.emit("info", data);
}
});
// 详情页
onLoad: function(options) {
const eventChannel = this.getOpenerEventChannel();
// 监听info事件,获取上一页面通过eventChannel传送到当前页面的数据
eventChannel.on("info", function(data) {
console.log(data);
});
},
总结: 路由传参可以用这两种方式
- 通过url拼接参数传输,需要传对象,需要传对象就先讲对象转成json字符串再传
- 通过eventChannel(事件通道)进行传输(可以传对象)
8自定义组件使用
生命周期
- created 组件实例化,但节点树还未导入,因此这时不能用 setData
- attached 节点树完成,可以用 setData 渲染节点,但无法操作节点
- ready 组件布局完成,这时可以获取节点信息,也可以操作节点
- moved 组件实例被移动到树的另一个位置
- detached 组件实例从节点树中移除
1创建组件
- 右键点击开发工具中的 components => 新建目录 count
- 右键 count 目录 => 新建组件 => 输入 count(组件的名称)
2编写静态文件
合计
¥99.00
结算
3使用组件
新建一个页面,名字随意,比如 shopCart,编写 shopCart 页面的代码,至此我们已经能在 shopCart 页面看到了 count 组件展示出来的内容了
// shopCart.json代码,注册组件
{
"usingComponents": {
"count": "../../components/count/count"
}
}
4父给子传参
①shopCart.wxml(父组件) 添加一个总价 totalMoney
// 如果要传变量,请使用
②count(子组件) 组件接收参数,需要改动两个地方
- 修改 count.js 文件,在 properties 里添加
properties: {
totalMoney: Number
},
// 或者写成这样
properties: {
totalMoney: {
type: Number,
default: '0.00'
}
},
③修改 count.wxml
5事件和自组件传参
1.shopCart.wxml(父组件) 自定义一个事件,同时给这个事件绑定一个函数
2.shopCart.js(父组件) 编写 onSubmit 函数,event.detail 接收子组件传来的数据
onSubmit(event) {
console.log(event.detail);
},
3.修改 count.wxml(子组件), 绑定点击事件
合计
¥{{totalMoney}}
结算
4.修改 count.js(子组件), 在 methods 添加 handleClick,使用 triggerEvent 触发父组件的自定义事件
methods: {
handlClick() {
this.triggerEvent('submit',{name:'张三',age: 100})
}
}
点击结算的时候,父组件的自定义就被触发,控制台打印出{name:'张三',age: 100}
9数据缓存
1异步方法
wx.setStorage({
key: "key",
data: "value"
});
wx.getStorage({
key: "key",
success(res) {
console.log(res.data);
}
});
2同步方法
wx.setStorageSync("key", "value"); //存数据
var value = wx.getStorageSync("key"); //取数据
10用户授权登录
在做小程序电商项目的时候,添加一个商品到购物车,后台需要知道是哪个用户执行了添加的操作,这里就涉及到了用户识别的操作.
整个流程如下:
- 前端调用后台的登录接口,获得 token
- 前端发送请求带上 token,后台可以通过 token 来识别用户
在第一步中,有分为以下几个步骤
前端首先调用 wx.login 获取 code,
紧接着需要调用 wx.getUserInfo 来获得用户信息(userInfo),获得 iv 和 encryptedData需要用户授权
-
调用后台登录接口,比如 /user/login,把以上三个参数传给后台
{ code, iv, encryptedData; }
4.后台根据上面三个参数调用微信接口获得 openid,再将 openid 和用户的其他信息一并存起来,同时生成 userId,根据 userId 生成一个加密的 token 返回给小程序,小程序端再把token放入请求头,每次发请求都带上,这样后台就可以识别用户了,可以回看获取数据
5.用户授权操作
要获得用户信息,必须先征得用户同意,那么就需要用户授权,除了开放数据以外,小程序的很多功能都需要获得用户授权,才可以进行下一步的操作,
用户授权功能列表
a.用户授权 hello world
wx.authorize({
// 授权类型
scope: "scope.record",
success() {
// 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
wx.startRecord();
}
});
当你在 onLoad 加上以上代码的时候,你会发现你的小程序出现了一个麦克风一样的东西,那就是 wx 正在录音
b检查用户是否已授权
wx.getSetting({
success(res) {
// 检查录音功能是否已经授权
console.log(res.authSetting["scope.record"]);
}
});
一般来讲,我们会先检查用户是否已授权,没授权我们才会去调用授权接口,所以 1 中的代码我们会写成这样
// 可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.record" 这个 scope
wx.getSetting({
success(res) {
if (!res.authSetting["scope.record"]) {
wx.authorize({
scope: "scope.record",
success() {
// 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
wx.startRecord();
}
});
}
}
});
上面的例子是获取录音功能的授权,我们直接使用 wx.authorize({ scope: 'scope.record' })
,小程序就会弹出一个窗口去询问用户是否允许使用录音功能,但是获取用户信息的授权不能这样去操作(这是小程序的规定),必须通过用户点击按钮去授权,我想是微信为了保护用户隐私信息所做的改进吧,获取用户信息的授权步骤如下:
// wxml的代码
// js代码
getUserInfo(event) {
console.log(event);
},
当用户点击按钮,时,就能获得 event 的信息,信息如下
{
"type": "getuserinfo",
"timeStamp": 2141,
"target": {
"id": "",
"offsetLeft": 0,
"offsetTop": 0,
"dataset": {}
},
"currentTarget": {
"id": "",
"offsetLeft": 0,
"offsetTop": 0,
"dataset": {}
},
"mark": {},
"detail": {
"errMsg": "getUserInfo:ok",
"iv": "wxPrFbK3bavrAMujWck9DQ==",
"userInfo": {
"nickName": "张三",
"gender": 1,
"language": "zh_CN",
"city": "Shenzhen",
"province": "Guangdong",
"country": "China",
"avatarUrl": "https://wx.qlogo.cn/mmopen/vi_32/BD5O3DV5ZDt5pebesxZUiaj1MNYUjjL1NObGHUkxfGTI896rCwHlmQmicPDHDJLFpALoXiajehldX1eVXibZnOHgFQ/132"
}
}
}
login(event) {
console.log(event);
wx.setStorageSync('userInfo', event.detail.userInfo)
wx.setStorageSync('userimage', event.detail.userInfo.avatarUrl)
wx.setStorageSync('users', event.detail.userInfo.nickName)
this.setData({
userInfo: event.detail.userInfo,
userimage: event.detail.userInfo.avatarUrl,
users: event.detail.userInfo.nickName,
hasUserInfo: true
})
wx.login({
success: (res) => {
let { iv, encryptedData, userInfo } = event.detail;
let url = '/user/login';
let data = {
iv, encryptedData, userInfo, code: res.code
}
app.$post(url, data).then(res => {
console.log(res);
// 把token存入缓存
let { token } = res.user;
wx.setStorageSync('token', token);
}).catch(err => {
console.log(err);
})
}
})
}
其中 event.detail.userInfo 就是用户信息了,接下来就可以去用 userInfo 和 wx.login 拿到的 code 去登录了
11使用 vant-ui 库
1.安装 在小程序根目录执行一下命令
npm init -y
npm i vant-weapp -S --production
2构建npm包
3引用使用组件
以 Button 组件为例,只需要在app.json
或index.json
中配置 Button 对应的路径即可。如果你是通过下载源代码的方式使用 vant-weapp,请将路径修改为项目中 vant-weapp 所在的目录。
// app.json
"usingComponents": {
"van-button": "vant-weapp/button"
}
引入组件后,可以在 wxml 中直接使用组件
按钮
4 其他 (在开发者工具中预览示例小程序)
1下载vant-weapp
2安装依赖和运行
# 安装项目依赖
npm install
# 执行组件编译
npm run dev
在小程序中运行,打开微信开发者工具,导入
example
目录的项目就可以预览示例了。-
当在小程序能看到预览之后,上面运行的服务就可以停止了,下次打开小程序预览有赞 ui 库也不需要再执行
- 该服务运行,页面打开后实际就是 ui 库的文档
- 运行之后自动编译小程序组件(只需编译一次)
- 如果我们看文档是到 vant-weapp 官网去看的话,上面的服务就不需要再运行
12模拟数据 (mock)
现在开发项目社会的主流是前后端分离,这样前端和后端就可以根据自己的进度开发,可以不同步。后端同学接口还没做好,前端同学怎么测试呢,这样就要用到模拟数据了。
(一) 请求本地的 json 文件
项目里新建个文件夹用来存放测试用的 json 文件。
reqwest({ url: "mock/user.json" })
.then(res => {
console.log(res);
})
.fail(err => {
console.log(err);
});
(二)用 mock.js
- 安装:npm install --save mockjs
- 写模拟数据
import Mock from 'mockjs';
Mock.mock('/v1/user', {
'list|1-10': [{
'id|+1': 1,
'email': '@EMAIL'
}]
})
- 发起 ajax 请求
reqwest({
url: "/v1/user",
type: "json"
})
.then(res => {
console.log(res);
})
.fail(err => {
console.log(err);
});
- 要在项目中使用 mock, 具体方法请看这个链接
(三)用 Node.js 搭建一个服务器模拟后台
个人开源 mockServe 项目,地址:文档地址
1把项目下载到本地=>安装依赖
npm i
2全局安装 nodemon
npm i nodemon -g
3运行项目
npm run dev
4编写接口
1项目的目录结构如下:
2点开 project 文件夹,新建文件夹输入您的项目名称:比如 taobao
3我们现在在 taobao 的文件夹里写一个 login 的接口,在 taobao 目录里新建文件 login.js 把下面的代码贴到 login.js 里:
const data = {
code: '666',
msg: 'success'
}
module.exports = (ctx)=> {
ctx.body = data;
}
现在你已经成功了创建了第一个接口,在浏览器如输入:http://localhost:3000/tobao/login 就可以访问
4请求参数
ctx.query.username // 获取get请求参数
ctx.request.body.username // 获取post请求参数
参考文档
13小程序支付
小程序支付流程
- 小程序端请求后端支付接口,把支付总价传给后台
- 后端调用小程序统一下单 API,获取预预支付订单信息,给前端返回预支付信息
- 小程序端使用 wx.requestPayment 调起支付窗口
- 小程序端轮询支付结果,获取结果后给用户展示支付结果
(一) 前端请求后端预支付接口
需要给后台传支付金额,根据后台的需要再传入其他的参数
(二) 后端调用小程序统一下单 API
1小程序支付流程
2统一下单
3 给前端返回支付所需参数
(三) 小程序端使用 wx.requestPayment
1文档地址
2调用
wx.requestPayment({
// 时间戳
timeStamp: "",
// 随机字符串
nonceStr: "",
// 统一下单接口返回的 prepay_id 参数值
package: "",
// 签名类型
signType: "",
// 签名
paySign: "",
// 支付成功的回调
success(res) {},
// 支付失败的回调
fail(res) {}
});
(四) 小程序端轮询支付结果
小程序支付和其他的微信支付,都是用户直接调用微信支付,微信支付再通知商户的服务器,所以需要前端去轮询后端,获得支付结果.
虽然小程序端调起微信支付也会知道支付的结果,但是为了同步结果(确保后端也收到了同样的支付结果通知),需要轮询
-
参考链接如下
微信小程序调用支付接口支付
nodejs实现小程序微信支付
14环境判断
//
if (typeof __wxConfig =="object"){
let version = __wxConfig.envVersion;
console.log("当前环境:" + version)
if (version =="develop"){
//工具或者真机 开发环境
}else if (version =="trial"){
//测试环境(体验版)
}else if (version =="release"){
//正式环境
}
}
- 网络请求的
referer
header 不可设置。其格式固定为https://servicewechat.com/{appid}/{version}/page-frame.html
,其中{appid}
为小程序的 appid,{version}
为小程序的版本号,版本号为0
表示为开发版、体验版以及审核版本,版本号为devtools
表示为开发者工具,其余为正式版本; -
wx.request
、wx.uploadFile
、wx.downloadFile
的最大并发限制是 10 个; - 小程序进入后台运行后(非置顶聊天),如果 5s 内网络请求没有结束,会回调错误信息
fail interrupted
;在回到前台之前,网络请求接口调用都会无法调用。