微信小程序学习

先记录一些笔记

建议学习新手视频教程大概了解
根据开发指南学习,具体内容查询开发者文档

官方视频教程 新手入门
教程 | 《小程序开发指南》
开发者文档

小程序官方组件库
weui通用库
iView WeApp

app.(js/json/wxss) 全局配置
page.(js/json/wxss/wxml) 页面配置
project.config.json 开发工具配置

基本项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
├── app.js              小程序入口文件,app单例的执行逻辑
├── app.json 小程序公共配置
├── app.wxss 小程序公共样式表
├── pages 所有页面
│   ├── index
│      ├── index.js 页面逻辑
│      ├── index.json 页面配置
│      ├── index.wxml 页面结构
│      └── index.wxss 页面样式表
├── project.config.json 开发工具配置
├── sitemap.json 允许微信爬虫的页面配置
└── utils
└── util.js

WXML

类似html,后面说明一些区别:

1. 标签名字不同
2. 多了些表达式 wx:if

数据绑定

1
2
<!--pages/wxml/index.wxml-->
<text>当前时间:{{time}}</text>
1
2
3
4
5
6
// pages/wxml/index.js
Page({
data: {
time: (new Date()).toString()
},
})

逻辑语法

1
2
3
4
<!--  {{}} 中可以包含一些逻辑运算 -->
<text>{{ a === 10? "变量 a 等于10": "变量 a 不等于10"}}</text>
<view> {{a + b}} + {{c}} + d </view> // 数学运算
<view>{{"hello " + name}}</view> //字符串拼接

条件逻辑

WXML 中,使用 wx:if="" 来判断是否需要渲染该代码块。
需要包装在标签中

1
2
3
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>

block标签包装条件语句

1
2
3
4
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>

循环语句

在组件上使用 wx:for 控制属性绑定一个数组

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
<!-- 对应的js脚本文件
Page({
data: {
array: [{
message: 'foo',
}, {
message: 'bar'
}]
}
})
-->

<!-- 以下3种方式 效果相同 -->

<view wx:for="{{array}}" >
{{index}}: {{item.message}}
</view>

<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
{{idx}} : {{itemName.message}}
</view>

<block wx:for="{{array}}">
<view>{{index}}: {{item.message}}</view>
</block>

wx:key 来指定列表中项目的唯一的标识符

1
2
3
4
5
6
7
<switch wx:for="{{objectArray}}" wx:key="unique" > {{item.id}} </switch>
<button bindtap="switch"> Switch </button>
<button bindtap="addToFront"> Add to the front </button>


<switch wx:for="{{numberArray}}" wx:key="*this" > {{item}} </switch>
<button bindtap="addNumberToFront"> Add Number to the front </button>
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
Page({
data: {
objectArray: [
{id: 5, unique: 'unique_5'},
{id: 4, unique: 'unique_4'},
{id: 3, unique: 'unique_3'},
{id: 2, unique: 'unique_2'},
{id: 1, unique: 'unique_1'},
{id: 0, unique: 'unique_0'},
],
numberArray: [1, 2, 3, 4]
},
switch: function(e) {
const length = this.data.objectArray.length
for (let i = 0; i < length; ++i) {
const x = Math.floor(Math.random() * length)
const y = Math.floor(Math.random() * length)
const temp = this.data.objectArray[x]
this.data.objectArray[x] = this.data.objectArray[y]
this.data.objectArray[y] = temp
}
this.setData({
objectArray: this.data.objectArray
})
},
addToFront: function(e) {
const length = this.data.objectArray.length
this.data.objectArray = [{id: length, unique: 'unique_' + length}].concat(this.data.objectArray)
this.setData({
objectArray: this.data.objectArray
})
},
addNumberToFront: function(e){
this.data.numberArray = [ this.data.numberArray.length + 1 ].concat(this.data.numberArray)
this.setData({
numberArray: this.data.numberArray
})
}
})

模板

<template/> 定义模板
模板定义

1
2
3
4
5
6
<template name="msgItem">
<view>
<text> {{index}}: {{msg}} </text>
<text> Time: {{time}} </text>
</view>
</template>

模板使用 ,使用 is 属性,声明需要的使用的模板

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--
item: {
index: 0,
msg: 'this is a template',
time: '2016-06-18'
}
-->

<template is="msgItem" data="{{...item}}"/> // ...展开item对象

<!-- 输出
0: this is a template Time: 2016-06-18
-->

引用

WXML 提供两种文件引用方式import和include。

import 可以在该文件中使用目标文件定义的 template,( import 不具有递归的特性: C import B, B import A, C不包含A的template)

1
<import src="item.wxml"/>

include 可以将目标文件中除了<template/> <wxs/> 外的整个代码引入,相当于是拷贝到 include 位置

1
2
3
4
5
6
<!-- index.wxml -->
<include src="header.wxml"/>

<view> body </view>

<include src="footer.wxml"/>

WXSS

rpx 尺寸单位,自动适配屏幕宽度。

WXSS引用

1
@import './test_0.wxss'

样式选择器

小程序WXSS支持的选择器
-w765

权重说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
view{ // 权重为 1
color: blue
}

.ele{ // 权重为 10
color: red
}

#ele{ // 权重为 100
color: pink
}

view#ele{ // 权重为 1 + 100 = 101,优先级最高,元素颜色为orange
color: orange
}

view.ele{ // 权重为 1 + 10 = 11
color: green
}

js

模块化

可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports 或者 exports 才能对外暴露接口。

1
2
3
4
5
6
7
8
9
10
11
12
// common.js

// exports和方法写在一起,内部方法名可以忽略(不建议,建议统一在底部exports方法,便于查看)
module.exports.sayHello = function(name) {
console.log(`Hello ${name} !`)
}

function sayGoodbye(name) {
console.log(`Goodbye ${name} !`)
}

exports.sayGoodbye = sayGoodbye

模块的引用

1
2
3
4
5
6
7
8
9
var common = require('common.js')
Page({
helloMINA: function() {
common.sayHello('MINA')
},
goodbyeMINA: function() {
common.sayGoodbye('MINA')
}
})

脚本执行顺序,按照App.json 里面定义的页面顺序。内部按照定义或者require的模块顺序执行

作用域

定义在js内部的为局部变量

1
2
3
// a.js
// 定义局部变量
var localValue = 'a'

定义全局变量

1
2
3
4
// app.js
App({
globalData: 1
})

在页面js通过 getApp 方法获取到全局唯一的 App 实例

1
2
3
4
const appInstance = getApp()
console.log(appInstance.globalData)
// 修改 global 变量
appInstance.globalData++ // 执行后 globalData 数值为 2

程序与页面

小程序造器App()

1
2
3
4
5
6
7
App({
onLaunch: function(options) {},
onShow: function(options) {},
onHide: function() {},
onError: function(msg) {},
globalData: 'I am global data'
})

onLaunch,onShow,中option 参数:
-w742

页面构造器Page()

1
2
3
4
5
6
7
8
9
10
11
12
Page({
data: { text: "This is page data." },
onLoad: function(options) { },
onReady: function() { },
onShow: function() { },
onHide: function() { },
onUnload: function() { },
onPullDownRefresh: function() { },
onReachBottom: function() { },
onShareAppMessage: function () { }, // 点击右上角分享
onPageScroll: function() { }
})

onLoad: function(options) { } 获取传递参数

1
2
3
4
5
6
7
8
9
10
11
// pages/list/list.js
// 列表页使用navigateTo跳转到详情页
wx.navigateTo({ url: 'pages/detail/detail?id=1&other=abc' })

// pages/detail/detail.js
Page({
onLoad: function(option) {
console.log(option.id)
console.log(option.other)
}
})

如果有特殊字符,如中文等字符 注意UrlEncode

this.setData 更新渲染数据,可以跟callback回调,渲染成功后执行

1
2
3
4
5
6
7
8
9
10
Page({
clickMe: function() {
this.setData(
{ msg: "Hello World" },
function(){
// 在这次setData对界面渲染完毕后触发
}
)
}
})

页面用户行为

  • onPullDownRefresh
    监听用户下拉刷新事件,需要在app.json的window选项中或页面配置page.json中设置enablePullDownRefresh为true。当处理完数据刷新后,wx.stopPullDownRefresh可以停止当前页面的下拉刷新。
1
2
3
4
5
6
7
8
9
10
11
//page.json
{"enablePullDownRefresh": true }

//page.js
Page({
onPullDownRefresh: function() {
// 用户触发了下拉刷新操作
// 拉取新数据重新渲染界面
// wx.stopPullDownRefresh() // 可以停止当前页面的下拉刷新。
}
})
  • onReachBottom
    监听用户上拉触底事件。可以在app.json的window选项中或页面配置page.json中设置触发距离onReachBottomDistance。在触发距离内滑动期间,本事件只会被触发一次。
1
2
3
4
5
6
7
8
9
10
11
//page.json
// 界面的下方距离页面底部距离小于onReachBottomDistance像素时触发onReachBottom回调
{"onReachBottomDistance": 100 }


//page.js
Page({
onReachBottom: function() {
// 当界面的下方距离页面底部距离小于100像素时触发回调
}
})
  • onPageScroll
    监听用户滑动页面事件,参数为 Object,包含 scrollTop 字段,表示页面在垂直方向已滚动的距离(单位px)。

  • onShareAppMessage
    只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮,在用户点击转发按钮的时候会调用,此事件需要return一个Object,包含title和path两个字段,用于自定义转发内容

    1
    2
    3
    4
    5
    6
    onShareAppMessage: function () {
    return {
    title: '自定义转发标题',
    path: '/page/user?id=123'
    }
    }

页面跳转和路由
[ pageA, pageB, pageC ]页面栈,最大层级为10层。

1
2
3
wx.navigateTo({ url: 'pageD' })
wx.navigateBack() //退出最上层页面
wx.redirectTo({ url: 'pageE' }) //最上面的页面替换

tabBar

1
2
3
4
5
6
7
8
9
10
11
12
13
// app.json
{
"tabBar": {
"list": [
{ "text": "Tab1", "pagePath": "pageA" },
{ "text": "Tab1", "pagePath": "pageF" },
{ "text": "Tab1", "pagePath": "pageG" }
]
}
}

// page.js
wx.switchTab({ url: 'pageF' })

wx.switchTab 触发切换tabBar,原来的页面栈会被清空只保留底层
wx.navigateTowx.redirectTo只能打开非TabBar页面,wx.switchTab只能打开Tabbar页面。

页面路由触发方式及页面生命周期函数的对应关系
-w796

组件

页面组件,标签引用组件
https://mp.weixin.qq.com/debug/wxadoc/dev/component/

API

wx.request({})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
wx.request({
url: 'test.php',
method: 'POST',
data: {},
header: { 'content-type': 'application/json' },
success: function (res) {
// 收到https服务成功后返回
if (res.statusCode === 200) {
console.log(res.data)// 服务器回包内容
}
},
fail: function () {
// 发生网络错误等情况触发
},
complete: function () {
// 成功或者失败后触发
}
})

事件

事件绑定,冒泡,捕获

冒泡:指事件从 子组件,传递到父组件(里->外)
捕获:捕获阶段位于冒泡阶段之前,且在捕获阶段中,事件到达节点的顺序与冒泡阶段恰好相反(外->里)

1
2
3
4
5
bindTap     //事件绑定,不中断冒泡 
catchTap //事件绑定,中断冒泡

capture-bindTap //捕获事件,不中断捕获、冒泡
capture-catchTap //捕获事件,中断捕获、冒泡
1
2
3
4
5
6
<view id="outer" bind:touchstart="handleTap1" capture-bind:touchstart="handleTap2">
outer view
<view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
inner view
</view>
</view>

bindtap 事件参数,event target
data tap事件传递参数
xml没有大写,如果要驼峰方式需要用data-user-name => event.target.dataset.userName

1
2
3
4
5
6
// wxml
<text data-userid='123131' data-user-name='patty' bindtap='tapMessage' class="user-motto" id='userid-{{userid}}' >{{motto}}</text>

// js
console.log("data - userid -" + event.target.dataset.userid)
console.log("data - userName - " + event.target.dataset.userName)

兼容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
wx.getSystemInfoSync()
/*
{
brand: "iPhone", // 手机品牌
model: "iPhone 6", // 手机型号
platform: "ios", // 客户端平台
system: "iOS 9.3.4", // 操作系统版本
version: "6.5.23", // 微信版本号
SDKVersion: "1.7.0", // 小程序基础库版本
language: "zh_CN", // 微信设置的语言
pixelRatio: 2, // 设备像素比
screenWidth: 667, // 屏幕宽度
screenHeight: 375, // 屏幕高度
windowWidth: 667, // 可使用窗口宽度
windowHeight: 375, // 可使用窗口高度
fontSizeSetting: 16 // 用户字体大小设置
}
*/

交互反馈

hover响应

1
2
3
<button hover-class="hover"> 点击button </button>

<button loading="{{loading}}" bindtap="tap">操作</button>

Toast

1
2
3
4
5
6
wx.showToast({ // 显示Toast
title: '已发送',
icon: 'success',
duration: 1500
})
// wx.hideToast() // 隐藏Toast

提示弹框

1
2
3
4
5
6
7
8
9
10
11
12
13
wx.showModal({
title: '标题',
content: '告知当前状态,信息和解决方法',
confirmText: '主操作',
cancelText: '次要操作',
success: function(res) {
if (res.confirm) {
console.log('用户点击主操作')
} else if (res.cancel) {
console.log('用户点击次要操作')
}
}
})

发起HTTPS网络通信

微信登录

-w313

####获取微信用户数据
1、使用 button 组件,并将 open-type 指定为 getUserInfo 类型,获取用户基本信息。
详情参考文档:
https://developers.weixin.qq.com/miniprogram/dev/component/button.html

2、使用 open-data 展示用户基本信息。
详情参考文档:
https://developers.weixin.qq.com/miniprogram/dev/component/open-data.html

本地数据缓存

小程序本地数据持久化,小程序沙盒:一个微信号、一个小程序 独立存储

小程序提供了读写本地数据缓存的接口,通过wx.getStorage/wx.getStorageSync读取本地缓存,通过wx.setStorage/wx.setStorageSync写数据到缓存,其中Sync后缀的接口表示是同步接口[9],执行完毕之后会立马返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
wx.getStorage({
key: 'key1',
success: function(res) {
// 异步接口在success回调才能拿到返回值
var value1 = res.data
},
fail: function() {
console.log('读取key1发生错误')
}
})

try{
// 同步接口立即返回值
var value2 = wx.getStorageSync('key2')
}catch (e) {
console.log('读取key2发生错误')
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 异步接口在success/fail回调才知道写入成功与否
wx.setStorage({
key:"key",
data:"value1"
success: function() {
console.log('写入value1成功')
},
fail: function() {
console.log('写入value1发生错误')
}
})

try{
// 同步接口立即写入
wx.setStorageSync('key', 'value2')
console.log('写入value2成功')
}catch (e) {
console.log('写入value2发生错误')
}

自定义组件component

轮播