微信小程序笔记

微信小程序前期准备材料

  1. 部署线上接口环境,微信小程序要求 https

  2. 接口文档,有道/语雀在线文档编辑工具或者部署doclevel

  3. 申请微信小程序并提供微信小程序 appid (接口还会用到secret

  4. 登录微信小程序后台管理配置服务器域名;把我们加到项目成员(开发者权限)

  5. 申请微信商户号(与微信小程序同主体),并和微信小程序关联。

相关API、资源

微信官方文档·小程序

微信内小程序

基础

微信开发工具

app.json中配置页面路由

  • 普通编译改成“添加编译模式”,可以改变启动的页面和其他的扫码进入等等方式

  • “预览”:会在编译器左侧展示

  • “上传”:上传测试版or正式版,填写相应的版本号和备注信息

  • “测试”:微信会发一份测试报告过来

  • “详情”:不校验域名不要勾选上(否则可能在正式线上会有问题)

视图层与逻辑层关系

微信小程序视图层和逻辑层交互图

程序的每个页面都会维护一份数据源,所以在wxml中用data-字段名称 除了需要在对应的(js中)this.setDatathat.setData中声明这个字段名称,最好还需要在data数据源中加上。

that.setData({ toastHidden: false })//这里使用了that,这样就可以获取Page({})对象。在wxml中可以直接通过{{toastHidden}}获取这个值

另外:

代码结构

没有操作dom结点,通过数据去渲染,更像vuereact

Page的生命周期配置:https://mp.weixin.qq.com/debug/wxadoc/dev/framework/app-service/page.html

  • app.js:程序启动先访问它,全局配置(可以设置数据共享Storage、全局数据)
  • app.jsonpages配置页面路由(在app.jspages中添加,根目录下pages代码会自动生成),windows:写导航栏的样式(navigationBarTextStyle提供了blackwhite两种)
    • pages中新建Page的时候会自动添加,但是删除却需要手动删除app.json里面的page配置
    • windows中全局配置导航栏中间的文字:navigationBarTitleText
    • windows中navigationBarBackgroundColor:导航栏的背景颜色
    • tabbar中:底部切换页签(当设置position为top时,将不会显示icon)
  • app.wxss:全局的样式(@import "colorui/main.wxss";导入别的样式)
  • project.config.json:我们写自定义编译等的配置都会在这里面

单位:rpx

rpx单位是微信小程序中css的尺寸单位,rpx可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6(750x1334) 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素1rpx = 0.5px = 1物理像素

设备 rpx换算px (屏幕宽度/750) px换算rpx (750/屏幕宽度)
iPhone5 1rpx = 0.42px 1px = 2.34rpx
iPhone6 1rpx = 0.5px 1px = 2rpx
iPhone6s 1rpx = 0.552px 1px = 1.81rpx

微信小程序也支持rem尺寸单位,remrpx的换算关系:rem: 规定屏幕宽度为20rem1rem = (750/20)rpx
注:开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
建议:设计稿使用设备宽度750px比较容易计算750px的话1rpx=1px,这样的话,设计图上量出来的尺寸是多少px就是多少rpx,至于在不同的设备上实际上要换算成多少个rem就交给小程序自己换算

列表

wx:for="{{在index.js的data数据源中的数据对象}}"

index.js的data数据源中定义上面for的数据对象的数组

text标签才支持 \n 的换行效果

“双花括号”去js中取变量的意思

数据绑定

小程序中,几乎所有和数据相关的操作都只能使用数据绑定来完成。

动态加载的js文件,调试问题

动态加载的js文件(比如是跳转到公共页面后才有的js),调试时控制台那边的Sources里不会列出这个js文件。

具体需要在跳转的地方断点,然后等待一段时间会自动加载这些js文件(加载时间比较久)

知识点

Flex

Flex:弹性布局,主要作用于容器上

微信小程序-flex1.png

微信小程序-flex2.png

微信小程序-flex3.png

微信小程序-flex4.png

Swiper组件

滑动视图容器–swiper组件

swiper组件的直接子元素只可以是swiper-item,可以放其他组件但会被自动删除。

swiper实现轮播效果,要添加一些属性:

  • indicator-dots

    指示点。Boolean类型,默认是false

  • autoplay

    是否自动播放。Boolean类型,默认是false

  • interval

    切换时间间隔。Number类型,默认5000毫秒

Boolean陷阱

属性vertical="true"vertical="aaa"等都表示“真”。其中的内容是字符串。

要表示“假”,有两种方式:

  1. vertical="" 放空字符串

  2. vertical="{{false}}" 放数据绑定

    false里的false被认为是一个Boolean类型的变量,而不是个字符串,从而实现false即是假,true是真的效果。

image组件的4种缩放模式和9种裁剪模式

4种缩放模式

  • scaleToFill

    不保持纵横比缩放图片,使图片的宽高完全拉伸至填满image元素

  • aspectFit

    保持纵横比缩放图片,使图片的长边能完全显示出来。即可以完整显示图片

  • aspectFill

    保持纵横比缩放图片,只保证短边能完全显示出来。即长边那部分会发生截取

  • wiidthFix

    宽度不变,高度自动变换,保持原图宽高比不变(0.11.122100版本新增)

9种裁剪模式

  • top

    不缩放图片,只显示图片的顶部区域

  • bottom

    不缩放图片,只显示图片的底部区域

  • center

    不缩放图片,只显示图片的中间区域

  • left

    不缩放图片,只显示图片的左边区域

  • right

    不缩放图片,只显示图片的右边区域

  • top left

    不缩放图片,只显示图片的左上边区域

  • top right

    不缩放图片,只显示图片的右上边区域

  • bottom left

    不缩放图片,只显示图片的左下边区域

  • bottom right

    不缩放图片,只显示图片的右下边区域

例子:

1
<image class="post-image" src="/images/post/post-4.jpg" mode="scaleToFill"/>

每个page都有个属于自己的js

页面js文件默认包含了我们可能使用到的代码结构

  • 有一个Page({})方法,
  • 参数是一个Object对象,
  • 页面初始数据data
  • 生命周期on开头的函数
  • 事件处理函数等

5个生命周期

  • 加载 onLoad

    监听页面加载,一个页面只会调用一次

  • 显示 onShow

    监听页面显示,每次打开页面都会调用

  • 渲染 onReady

    监听页面渲染完成,一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互(仅用来监听“第一次渲染”完成)

  • 隐藏 onHide

    监听页面隐藏

  • 卸载 onUnload

    监听页面卸载

3个小程序特定事件处理函数

  • onPullDownRefresh

    监听用户下拉动作的事件处理函数

  • onReachBottom

    页面上拉触底事件的处理函数

  • onShareAppMessage

    用户点击右上角分享

小程序页面生命周期图解

小程序页面生命周期图解.png

数据绑定

  • 小程序完全抛弃了DOM结构(AngularJS中还有个内置的jQLite用来支持获取DOM
  • 小程序的脚本逻辑运行在JSCore中(JSCore是个没有DOM的环境),只能用数据绑定来做数据的相关操作
  • 小程序单向数据绑定,从逻辑层传递到渲染层的数据绑定,反之不行。(AngularJS是个双向数据绑定)
  • "双花括号"中的变量名与js文件中data对象的属性名称相同

数据绑定有两种:

  • 初始化数据的数据绑定,通常写在Page方法内data对象内
  • 使用setData做数据更新的数据绑定。会引起页面的Rerender(重新渲染)(可看上图《小程序页面生命周期图解》)

例子:

saleTop.js中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Page({
data:{
first:null,
total:0,
avatar:"https://qiniuimage.dolphin-free.com/ldy/miniapp/sale_ic_two.png?v=70"
},
onLoad: function(){
var iceCreamData = {
object: {
date: "Jan 28 2020"
},
title: "标题"
}
this.setData({
postData: iceCreamData
})
}
})

saleTop.wxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- first是个对象 -->
<view>
{{first.storeName}}
</view>

<view>
{{total}}
</view>

<view>
<image src="{{avatar}}"></image>
</view>

<!-- 因为setData中的key变成postData,所以取数据改变 -->
<text>{{postData.title}}</text>

wx:for

wx:for不止可以作用于block标签上,也可以作用于view组件上(但不推荐用在view上,因为view组件通常当做容器或者区域分隔)。

我们希望标签或组件元素是语义明确的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx">
<view >
<view wx:for="{{item.avatars}}" wx:for-item="avatarItem">
<view >{{avatarItem.text}}</view>
<view >
<image style="width: 200rpx; height: 200rpx; background-color: #eeeeee;" mode="{{avatarItem.mode}}"
src="{{item.avatar}}" ></image>
</view>
</view>
</view>
<text>{{item.object.date}}</text>
<text class="font50" bindtap="navigateToDetail">{{item.title}}</text>
<view style="background-color:#ee0000; width: fit-content;" catchtap='switchToMineTab'>{{item.collectionNum.array[0]+"\n"}}</view>
</block>

其中数据源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
Page({
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var postList = [
{
object:{
date:"Jan 28 2017"
},
title: "点击跳转到详情页",
avatar: "https://res.wx.qq.com/wxdoc/dist/assets/img/0.4cb08bb4.jpg",
avatars:[
],
collectionNum:{
array:[107]
}
},
{
object:{
date:"Jan 28 2017"
},
title: "点击跳转到详情页",
avatar: "https://res.wx.qq.com/wxdoc/dist/assets/img/0.4cb08bb4.jpg",
avatars:[
{
mode: 'scaleToFill',
text: 'scaleToFill:不保持纵横比缩放图片,使图片完全适应'
}, {
mode: 'aspectFit',
text: 'aspectFit:保持纵横比缩放图片,使图片的长边能完全显示出来'
}, {
mode: 'aspectFill',
text: 'aspectFill:保持纵横比缩放图片,只保证图片的短边能完全显示出来'
}, {
mode: 'top',
text: 'top:不缩放图片,只显示图片的顶部区域'
}, {
mode: 'bottom',
text: 'bottom:不缩放图片,只显示图片的底部区域'
}, {
mode: 'center',
text: 'center:不缩放图片,只显示图片的中间区域'
}, {
mode: 'left',
text: 'left:不缩放图片,只显示图片的左边区域'
}, {
mode: 'right',
text: 'right:不缩放图片,只显示图片的右边边区域'
}, {
mode: 'top left',
text: 'top left:不缩放图片,只显示图片的左上边区域'
}, {
mode: 'top right',
text: 'top right:不缩放图片,只显示图片的右上边区域'
}, {
mode: 'bottom left',
text: 'bottom left:不缩放图片,只显示图片的左下边区域'
}, {
mode: 'bottom right',
text: 'bottom right:不缩放图片,只显示图片的右下边区域'
}
],
collectionNum:{
array:[108]
},
}
]
this.setData({
postList: postList
})
}
})

wx:if

1
2
3
4
<view wx:if="{{isRangeTypeOpen}}"></view>
<view wx:if="{{isRangeTypeOpen == true}}"></view>
<view wx:if="{{isRangeTypeOpen == false}}"></view>
<view wx:if="{{rangeType == 0}}"></view>

this与that

  1. Page({})里面,this关键字指代Page({})整个对象
  2. 因此可以通过this关键字访问或者重新设置Page({})data的变量
  3. 然而在loadData函数中使用了wx.request({})API这个方法导致在wx.request({})里没办法使用this来获取Page({})对象
  4. 虽然在wx.request({})里没法使用this获取Page({})对象,但是可以在wx.request({})外面先把this存在某个变量中,所以就有了var that = this这个声明。此时that指代Page({})整个对象,这样子就可以在wx.request({})里面使用that访问或者重新设置Page({})data的变量

javascriptthis代表着当前对象,会随着程序的执行过程中的上下文改变。

事件

catchtap="methodName"bindtap="methodName"

两者的区别:

  • 相同点:都是点击函数,点击时触发。
  • 不同点:bindtap是冒泡的,catchtap是非冒泡的【冒泡:对应android中的事件向上传递的意思】

小程序中事件分冒泡事件和非冒泡事件

微信小程序点击事件是否冒泡.png

1
2
3
4
5
6
7
8
9
<view id="outer" bindtap="out">
outer view
<view id="middle" bindtap="middle">
middle view
<view id="innder" bindtap="inner">
inner view
</view>
</view>
</view>
1
2
3
4
5
6
7
out:function(e){
console.log("--out bindtap click")
}, middle: function(e){
console.log("--middle bindtap click")
}, inner: function(e){
console.log("--inner bindtap click")
}
  1. bindtap执行结果

    点击out view 打印:out bindtap click

    点击middle view 打印:middle bindtap click – out bindtap click

    点击inner view 打印:inner bindtap click – middle bindtap click – out bindtap click

  2. 修改middle view的bindtap为catchtap

    点击out view 打印:out bindtap click(因为没有上层元素故而无法向上冒泡)

    点击middle view 打印:middle bindtap click(catchtap阻止向上冒泡)

    点击inner view 打印:inner bindtap click – middle bindtap click(catchtap阻止向上冒泡)

常见冒泡事件类型

  • touchstart 手指触摸动作开始
  • touchmove 手指触摸后移动
  • touchcancel 手指触摸动作被打断,如来电提醒、弹窗
  • touchend 手指触摸动作结束
  • tap 手指触摸后马上离开(使用时有两种bindtap、catchtap)
  • longtap 手指触摸后,超过350ms再离开

除以上事件,如无特殊申明都是非冒泡事件。

非冒泡事件大多为非通用事件,是某些组件特有的事件。如<form/>的submit事件,<input/>的input事件,<scroll-view/>的scroll事件等。

模块

模板

缓存

存储数据

wx.setStorageSync("key", value);设置数据存储,

取出数据

wx.getStorageSync("key")取出存储的数据

页面传值

  1. 通过url带过去

    1
    2
    3
    4
    var url = '../../package-item/item/itemDetail/itemDetail?itemId=' + itemId + '&storeId=' + wx.getStorageSync('storeId') + "&isFromNewPrice=1";
    wx.navigateTo({
    url: url
    })

    接收(目标页面的js文件中)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    this.getItemSkuInfo.bind(this);


    getItemSkuInfo(that) {
    return new Promise(function (resolve, reject) {
    var skuInfo = "";
    ...
    wx.request({
    url: supportRequest + "miniApp/wxItemSkuInfo",
    data: {
    ...,
    IsFromNewPrice: that.data.isFromNewPrice//此处直接取到值,并
    },
    success: function (res) {
    ...
    }
    })
    })
    }

app.js的生命周期

(整个小程序的生命周期)在app.js中:onLaunch启动的一些工作(比如登录等状态校验、权限请求、获取位置信息等、获取用户信息(是否隐式登录)、数据存储)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
interface Option {
/** 生命周期回调—监听小程序初始化
*
* 小程序初始化完成时触发,全局只触发一次。
*/
onLaunch(options: LaunchShowOption): void
/** 生命周期回调—监听小程序显示
*
* 小程序启动,或从后台进入前台显示时
*/
onShow(options: LaunchShowOption): void
/** 生命周期回调—监听小程序隐藏
*
* 小程序从前台进入后台时
*/
onHide(): void
/** 错误监听函数
*
* 小程序发生脚本错误,或者 api
*/
onError(/** 错误信息,包含堆栈 */ error: string): void
/** 页面不存在监听函数
*
* 小程序要打开的页面不存在时触发,会带上页面信息回调该函数
*
* **注意:**
* 1. 如果开发者没有添加 `onPageNotFound` 监听,当跳转页面不存在时,将推入微信客户端原生的页面不存在提示页面。
* 2. 如果 `onPageNotFound` 回调中又重定向到另一个不存在的页面,将推入微信客户端原生的页面不存在提示页面,并且不再回调 `onPageNotFound`。
*
* 最低基础库: 1.9.90
*/
onPageNotFound(options: PageNotFoundOption): void
/**
* 小程序有未处理的 Promise 拒绝时触发。也可以使用 [wx.onUnhandledRejection](https://developers.weixin.qq.com/miniprogram/dev/api/base/app/app-event/wx.onUnhandledRejection.html) 绑定监听。注意事项请参考 [wx.onUnhandledRejection](https://developers.weixin.qq.com/miniprogram/dev/api/base/app/app-event/wx.onUnhandledRejection.html)。
* **参数**:与 [wx.onUnhandledRejection](https://developers.weixin.qq.com/miniprogram/dev/api/base/app/app-event/wx.onUnhandledRejection.html) 一致
*/
onUnhandledRejection: OnUnhandledRejectionCallback
}

(某个页面的生命周期)pages下各自js/Page({})中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
interface ILifetime {
/** 生命周期回调—监听页面加载
*
* 页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。
*/
onLoad(
/** 打开当前页面路径中的参数 */
query: Record<string, string | undefined>
): void
/** 生命周期回调—监听页面显示
*
* 页面显示/切入前台时触发。
*/
onShow(): void
/** 生命周期回调—监听页面初次渲染完成
*
* 页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
*
* 注意:对界面内容进行设置的 API 如`wx.setNavigationBarTitle`,请在`onReady`之后进行。
*/
onReady(): void
/** 生命周期回调—监听页面隐藏
*
* 页面隐藏/切入后台时触发。 如 `navigateTo` 或底部 `tab` 切换到其他页面,小程序切入后台等。
*/
onHide(): void
/** 生命周期回调—监听页面卸载
*
* 页面卸载时触发。如`redirectTo`或`navigateBack`到其他页面时。
*/
onUnload(): void
/** 监听用户下拉动作
*
* 监听用户下拉刷新事件。
* - 需要在`app.json`的`window`选项中或页面配置中开启`enablePullDownRefresh`。
* - 可以通过`wx.startPullDownRefresh`触发下拉刷新,调用后触发下拉刷新动画,效果与用户手动下拉刷新一致。
* - 当处理完数据刷新后,`wx.stopPullDownRefresh`可以停止当前页面的下拉刷新。
*/
onPullDownRefresh(): void
/** 页面上拉触底事件的处理函数
*
* 监听用户上拉触底事件。
* - 可以在`app.json`的`window`选项中或页面配置中设置触发距离`onReachBottomDistance`。
* - 在触发距离内滑动期间,本事件只会被触发一次。
*/
onReachBottom(): void
/** 用户点击右上角转发
*
* 监听用户点击页面内转发按钮(`<button>` 组件 `open-type="share"`)或右上角菜单“转发”按钮的行为,并自定义转发内容。
*
* **注意:只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮**
*
* 此事件需要 return 一个 Object,用于自定义转发内容
*/
onShareAppMessage(
/** 分享发起来源参数 */
options: IShareAppMessageOption
): ICustomShareContent
/** 页面滚动触发事件的处理函数
*
* 监听用户滑动页面事件。
*/
onPageScroll(
/** 页面滚动参数 */
options: IPageScrollOption
): void

/** 当前是 tab 页时,点击 tab 时触发,最低基础库: `1.9.0` */
onTabItemTap(
/** tab 点击参数 */
options: ITabItemTapOption
): void

/** 窗口尺寸改变时触发,最低基础库:`2.4.0` */
onResize(
/** 窗口尺寸参数 */
options: IResizeOption
): void
}

空判断

判断undefined

1
2
3
4
var tmp = undefined;
if(typeof(tmp) == "undefined"){
console.log("undefined");
}

typeof返回的是字符串,有六种可能:”number”、”string“、”boolean“、”object“、”function“、”undefined“

判断null

1
2
3
4
var tmp = null;
if(!tmp && typeof(tmp) != "undefined" && tmp != 0){
console.log("null");
}

判断NaN

1
2
3
4
var tmp = 0/0;
if(isNan(tmp)){
console.log("NaN");
}

NaN表示非法,如果把NaN与任何值(包括其自身)相比得到的结果均是false,所以判断某个值是否是NaN,不能使用 =====运算符。

isNaN()通常用于检测parseFloat()parseInt()的结果,判断结果是否是合法的数字。

判断undefined和null

1
2
3
4
5
6
7
8
var tmp = undefined;
if(tmp == undefined){
console.log("null or undefined");
}
var tmp = undefined;
if(tmp == null){
console.log("null or undefined");
}

说明:null == undefined

判断undefied、null与NaN

1
2
3
4
var tmp = null;
if(!tmp){
console.log("null or undefined or NaN");
}

一般不那么区分就使用这个足够

判断空对象

让一个对象一开始设为null,通过判断是否为null即可判断

判断options是否存在

if(options){//存在} else {//不存在 }即可

项目配置文件

可以在项目根目录使用 project.config.json 文件对项目进行配置。

问题

微信小程序的Js中调用同一个Js中的另一个函数会报“函数” is not defined

解决:需要在调用的时候加前缀this.

1
2
3
4
5
6
7
fun_a: function(e){
var i;
this.fun_b(i)
},
fun_b: function(e){
console.log("hello")
}

在javaScript中不用加this.,直接调用不报错

微信小程序dataset 时属性要为小写

赋值

1
2
<view data-myid="{{item.id}}" bindtap="gotoDetails">
</view>

取值

1
2
3
gotoDetails: function (e) {
var id = e.currentTarget.dataset.myid;
}

切记data-myid为小写,包含大写会得到 undefined