第一章 小程序介绍与开发环境
一. Hello World
// app.json
"pages": [
"pages/index/index"
]
// index/index.wxml
<text>Hello World</text>
二. 小程序介绍
1.小程序技术发展史
(1).移动web在微信的WebView中显示,微信有相关JS API ,但只对团队内部使用
(2). 网页开发工具包 JS-SDK,对外开放,缺点:体验不好,资源加载白屏/缺少操纵反馈。
(3). 小程序应运而生。
2.小程序与普通网页开发的区别
(1).网页开发中渲染线程与脚本线程是互斥的。小程序逻辑层与渲染层是分开的,可以并行的。
(2).小程序逻辑层没有一个完整浏览器对象,因而缺少相关的DOM API和BOM API。同时JSCore 的环境同 NodeJS 环境不尽相同。
(3).网页开发者需要面对的环境是各式各样的浏览器,小程序要面对的是两大操作系统 iOS 和 Android 的微信客户端,以及用于辅助开发的小程序开发者工具。
小程序运行环境
三. 小程序开发准备
在微信公众平台注册一个小程序,设置”—“开发设置” 就可以看到小程序的 AppID。
安装开发者工具
在开发者工具中创建项目 填入AppID 开始项目。
第二章 小程序代码组成
一. JSON 配置
1.在小程序中,JSON扮演的静态配置的角色。
2.小程序是无法在运行过程中去动态更新JSON 配置文件从而发生对应的变化的。
JSON 语法:
1.JSON的Key必须包裹在一个双引号中
2.JSON的值数据格式:
1.数字 包括浮点数和整数
2.字符串
3.Bool: true or false
4.数组:包裹在[]中
5.对象:包裹在{}中
6.null
其他任何格式都会触发报错
3.JSON 文件中无法使用注释,试图添加注释将会引发报错。
二.WXML模板
1.基本介绍
1.全称:WeiXin Markup Language
2.基本语法:
<!-- 在此处注释 —>
<text 属性名=“属性值”></text>
2.数据绑定
1.WXML中的属性是大小写敏感的
2.数据绑定中 变量也是大小写敏感的
3.属性值可以绑定,有所不同的是,属性值必须被包裹在引号中
<!-- 数据绑定 -->
// index.wxml
<view>当前时间:{{time}}</view>
<!-- 绑定属性值 -->
<view data-test="{{test}}">hello world</view>
<view>{{var1}}</view>
<view>{{var2}}</view>
// index.js
data: {
time: (new Date()).toString(),
var2: undefined,
var3: null
}
3.逻辑语法
<!-- 逻辑运算 -->
// index.js
data: {
a: 10,
b: 2,
c: 4,
d: 6,
name: 'world',
arr: ['hei',2,3]
}
// index.wxml
<view>{{a === 10?"变量a等于10":"变量a不等于10"}}</view> //变量a等于10
<view>{{a + b}} + {{c}} + {{d}}</view> // 12 + 4 + 6
<view>{{'hello ' + name }}</view> // hello world
<view>hello {{name}}</view> // hello world
<view>{{arr}}</view> // hei,2,3
<view>{{1}}</view> // 1
<view>{{'hhahahha'}}</view> // hhahahha
4. 条件逻辑
<!-- 条件逻辑 -->
// index.js
arr: ['hei',2,3]
// index.wxml
<view wx:if="{{arr.length > 5}}">1</view>
<view wx:elif="{{length > 3}}">2</view>
<view wx:else>3</view>
5. 列表渲染
// index.js
data: {
list: [
{
message: 'foo',
},
{
message: 'bar',
}
],
objArr: [
{ id: 5, unique: 'unique5' },
{ id: 4, unique: 'unique4' },
{ id: 3, unique: 'unique3' },
{ id: 2, unique: 'unique2' },
{ id: 1, unique: 'unique1' },
{ id: 0, unique: 'unique0' },
],
numArr: [1, 2, 3, 4]
}switch: function(e){
const length = this.data.objArr.length
for(var i = 0;i < length; i++) {
const x = Math.floor(Math.random() * length);
const y = Math.floor(Math.random() * length);
const temp = this.data.objArr[x];
this.data.objArr[x] = this.data.objArr[y];
this.data.objArr[y] = temp;
}
this.setData({
objArr: this.data.objArr
})
},
addToFront: function(e){
const length = this.data.objArr.length
this.data.objArr = [{id: length, unique: 'unique_' + length}].concat(this.data.objArr);
this.setData({
objArr: this.data.objArr
})
},
addNumToFront: function(e){
this.data.numArr = [this.data.numArr.length + 1].concat(this.data.numArr)
this.setData({
numArr: this.data.numArr
})
}
// index.wxml
<view wx:for='{{list}}'>
{{index + 1}}: {{item.message}}
</view>
<!-- 列表渲染 指定当前元素的变量名 及其对应的下标 -->
<view wx:for="{{list}}" wx:for-index='idx' wx:for-item='itemName'>
{{idx + 1}}: {{itemName.message}}
</view>
<!-- 列表渲染 指定唯一标识符 -->
<switch wx:for="{{objArr}}" wx:key="unique">{{item.id}}</switch>
<button bindtap="switch">switch</button>
<button bindtap="addToFront">addToFront</button>
<switch wx:for="{{numArr}}" wx:key="*this">{{item}}</switch>
<button bindtap="addNumToFront">addNumToFront</button>
使用wx:key指定列表渲染中的唯一标识符,key值两个条件:
(1)字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
(2) 保留关键字 this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字
好处: 当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件 保持自身的状态,并且提高列表渲染时的效率。
4. 模版
模板中渲染的所有数据是传入对象的属性的值
<!-- 模板 -->
<template name="msg">
<view>
<text>{{id}}: {{unique}}</text>
</view>
</template>
<view wx:for="{{objArr}}" wx:for-item="item" wx:for-index="index">
<template is="msg" data="{{...item}}"></template>
</view>
<template name="even">
<view>even</view>
</template>
<block wx:for="{{[1, 2, 3, 4, 5]}}" wx:for-item="item">
<template is="{{item%2 == 0?'even': ''}}" data="{{...item}}"></template>
</block>
5. 引用
// b.wxml
<template name='B'>
<text>B template</text>
</template>
// 同目录下index.wxml
<import src='b.wxml' />
<template is="B"/>
// header.wxml
<view>header</view>
// footer.wxml
<view>footer</view>
<!-- 引用 include -->
<include src='header.wxml'/>
<view>body</view>
<include src='footer.wxml'/>
6. 共同属性
共同属性三. javascript 脚本组成
1.小程序javascript组成
屏幕快照 2019-02-22 下午8.56.42.png注意:
(1)没有BOM与DOM,无法使用Jquery、zepto等浏览器类库
(2) Native 模块和NPM包管理的机制,无法加载原生库与大部分NPM包
2.小程序的执行环境
1.小程序目前可以运行在三大平台:
(1).iOS平台,包括iOS9、iOS10、iOS11
(2).Android平台
(3).小程序IDE
原因:三大平台实现的 ECMAScript 的标准有所不同。
2.iOS9和iOS10 所使用的运行环境并没有完全的兼容到 ECMAScript 6 标准。
小程序解决方案:小程序IDE在‘右上角详情’中提供转码工具,勾选 ES6 转 ES5 后,可实现转码。
3.模块化
1.小程序中将所有javascript文件作为一个模块处理,通过module.exports 或者 exports 对外暴露接口。
2.引用模块的方法时,可采用require的方式
var aBy2 = require('./a.js');
var result = aBy2(5);
console.log(result);
3.小程序脚本执行顺序:
(1)入口文件app.js
(2)app.js中引入的模块
(3)app.js中pages属性定义的js文件顺序:
"pages": [
"pages/wxml/index",
"pages/index/index"
],
4.作用域
(1)不同文件声明的变量以及方法只在本文件有效
(2)可以通过getApp()全局方法设置全局性的属性以及方法
第三章 理解小程序宿主环境
不同版本的微信客户端提供的宿主环境版本不同,所以小程序需要做兼容!!
一.渲染层和逻辑层
小程序的运行环境分成渲染层和逻辑层,这是其运行环境的基本工作模式。
1.渲染“Hello World”页面
hello world案例:
wxml:
<view>{{ msg }}</view>
js:
Page({
onLoad: function () {
this.setData({ msg: 'Hello World' })
}
})
说明:1.渲染层和数据相关。
2.逻辑层负责产生、处理数据。
3.逻辑层通过 Page 实例的 setData 方法传递数据到渲染层。
所以接下类讲述第三点描述的通讯模型。
2.通信模型
小程序的逻辑层与渲染层由2个线程管理,渲染层使用webview线程渲染,逻辑层采用JsCore线程运行脚本。一个小程序有多个界面,所以存在多个webview线程。下图为通讯模型:
小程序通讯模型
图中Native指‘微信客户端’,渲染层与逻辑通通过微信客户端做中转进行通讯。
3.数据驱动
数据驱动:将数据与状态相关联,当数据更新时,自动更新视图。
(1)Hello World 数据渲染示意图:
数据渲染
(2)更改数据后,视图更新流程示意图:
数据更新
4.双线程下的界面渲染
(1)wxml转化为对应的js对象
(2)逻辑层数据更新后,通过setData方法把数据传递到渲染层,
(3)对比前后js对象差异,更新修改的部分。
界面渲染过程
二.程序与页面
1.程序
注意: ‘小程序’指的是产品层面的程序,‘程序’指的是代码层面的程序实例。
程序构造器App():
// app.js
App({
onLaunch: function (options) { },
onShow: function (options) { },
onHide: function () { },
onError: function (msg) { },
globalData: 'I am global data'
})
App构造器的参数
注意: App的生命周期是由微信客户端根据用户操作主动触发的。为了避免程序上的混乱,我们不应该从其他代码里主动调用App实例的生命周期函数。
上述方法中onLaunch、onShow带参数:
参数内容
以下场景支持返回referrerInfo.appId:
支持返回referrerInfo.appId
构造器中的其他数据为全局性的数据,其他页面可通过getApp()方法获取到。
2. 页面:
1.文件构成与路径:
页面分三部分: wxml、wxss负责界面;json负责配置;js负责逻辑。
页面路径在小程序根目录app.json中声明,第一个声明的是首页。
{
"pages":[
"pages/index/page", // 第一项默认为首页
"pages/other/other"
]
}
2.页面构造器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() { }
})
其中,data为数据、前四个方法为声明周期函数、后面四个为页面的用户行为。
Page构造器的参数
当使用wx.navigateTo切换到其它页面、底部Tab切换时触发onHide方法;
当使用wx.redirectTo或者wx.navigateBack返回其他页面时,页面被销毁调用onUnload方法。
Page构造器传参实例:
// 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)
}
})
注意:页面URL上的value如果涉及特殊字符(例如:&字符、?字符、中文字符等,详情参考URI的RFC3986说明 ),需要采用UrlEncode后再拼接到页面URL上。
4.页面的数据:
(1)data参数是页面第一次渲染时从逻辑层传递到渲染层的数据。
<!-- page.wxml -->
<view>{{text}}</view>
<view>{{array[0].msg}}</view>
// page.js
Page({
data: {
text: 'init data',
array: [{msg: '1'}, {msg: '2'}]
}
})
(2)使用setData函数传递数据,传递过程中渲染层、逻辑层在两个线程中运行,故该过程是异步的。setData方法中有callback回调,在渲染完成后触发。
// page.js
Page({
onLoad: function(){
this.setData({
text: 'change data'
}, function(){
// 在这次setData对界面渲染完毕后触发
})
}
})
(3)在通过setData方法重设字段时,不必把所有字段重设,只需把改变的值重设,原则是每次只设置需要改变的最小单位数据。
注意:
必须通过this.setData设置数据
每次设置的数据不应超过1024kb
不要把任意一项value设置为undefined,
- 页面的用户行为:
(1) 监听用户下拉刷新事件 onPullDownRefresh
需在app.json的window选项中或者页面配置page.json中设置enablePullDownRefresh为true,
处理完数据刷新后,可使用wx.stopPullDownRefresh停止页面下拉刷新。
(2)监听用户上拉触底 onReachBottom
可在app.json的window选项中或者页面配置page.json中设置触发距离onReachBottomDistance,在触发距离内滑动,该事件只触发一次。
(3)监听用户滑动页面事件 onPageScroll
参数为object,包含scrollTop字段,表示页面在垂直方向已经滚动的距离(单位:px)
(4) 用户转发 onShareAppMessage:
定义此事件后,右上角显示转发按钮,该事件需要返回一个object,包含 title、path
// page.js
Page({
onShareAppMessage: function () {
return {
title: '自定义转发标题',
path: '/page/user?id=123'
}
}
})
-
页面跳转和路由
(1) 页面栈:例子:在多次使用(少于等于10)wx.navigateTo后,页面层级会有多层,行程的页面层级为页面栈。如下图:
页面栈
为方便,使用[ pageA, pageB, pageC ]表述页面栈,其中pageA在最下面,pageC在最上面。
下面使用以下方法表述页面栈的变化:
wx.navigateTo({ url: 'pageD' }) 页面栈变为 [ pageA, pageB, pageC, pageD ];
wx.navigateBack() 页面栈变为 [ pageA, pageB, pageC ];
wx.redirectTo({ url: 'pageE' }) 页面栈变为 [ pageA, pageB, pageE ];
当页面栈到达10层没法再新增的时候,往往就是使用redirectTo这个API进行页面跳转。
小程序Tabbar 声明:
// app.json
{
"tabBar": {
"list": [
{ "text": "Tab1", "pagePath": "pageA" },
{ "text": "Tab1", "pagePath": "pageF" },
{ "text": "Tab1", "pagePath": "pageG" }
]
}
}
接着上面的页面栈变化:
wx.switchTab({ url: 'pageF' }) 页面栈变为 [pageF ];(其中除pageA外,其它页面被销毁)。
wx. reLaunch({ url: 'pageH' })重启小程序,页面栈为 [ pageH ]
注意:wx.navigateTo和wx.redirectTo只能打开非TabBar页面,wx.switchTab只能打开Tabbar页面。
(2) 页面路由触发方式及页面生命周期函数的对应关系。
页面触发方式与声明周期函数
(3) Tab 切换对应的生命周期(以 A、B 页面为 Tabbar 页面,C 是从 A 页面打开的页面,D 页面是从 C 页面打开的页面为例) 注意Tabbar页面初始化之后不会被销毁。
页面路由触发方式及页面生命周期函数的对应关系
三.组件
1.小程序页面WXML的基本组成单位是:组件。
-
小程序的宿主环境提供了一系列基础组件。
3.组件样式:
(1) 通常包含开始标签和结束标签,该标签的属性用来描述该组件。
(2) 所有组件名和属性都是小写,多个单词会以英文横杠 "-" 进行连接。
(3) 所有组件的公共样式和事件绑定:
组件公共样式、事件绑定
(4) 组件都拥有各自自定义的私有属性,可以对该组件的功能或者样式进行修饰。以image为例:
<image mode="scaleToFill" src="img.png"></image>
(5) 基础组件地址:
https://mp.weixin.qq.com/debug/wxadoc/dev/component/
四.API
1.定义:
以wx.navigate为例,wx是小程序宿主环境的全局对象。
所有小程序的API(构造器Page/App除外)都挂载在wx对象下。
API通指wx对象下的方法。
2.API按照功能分为网络、媒体、文件、数据缓存、位置、设备、界面、界面节点信息以及开放接口。
3.API一般调用的约定:
(1) wx.on开头的API是监听某个事件发生的API接口,接受一个callback回调,该事件触发时,回调。
(2) 多数API为异步接口、接受一个object参数
(3) API的Object参数一般由success、fail、complete三个回调来接收接口调用结果.
(4) wx.get开头的API:获取宿主环境数据
(5). wx.set*开头的API:写入宿主环境数据
wx.request({
url: 'test.php',
data: {},
header: { 'content-type': 'application/json' },
success: function(res) {
// 收到https服务成功后返回
console.log(res.data)
},
fail: function() {
// 发生网络错误等情况触发
},
complete: function() {
// 成功或者失败后触发
}
})
详细了解API文档地址: https://mp.weixin.qq.com/debug/wxadoc/dev/api/
五.事件:
1.定义:
用户在渲染层的行为反馈或者组件的部分状态反馈(播放的进度条)。
渲染层传递给逻辑层的事件。
事件触发过程
2.事件类型与事件对象:
1.组件的部分状态反馈的事件可参考https://mp.weixin.qq.com/debug/wxadoc/dev/component/
2.常见的事件类型:
常见事件类型
以上事件都是冒泡事件,除此之外都不是。
3.事件触发后的事件对象属性:
事件对象属性
4.事件对象属性中target和currentTarget详情:
target和currentTarget
两者区别:currentTarget为当前事件所绑定的组件,而target则是触发该事件的源头组件
<!-- page.wxml -->
<view id="outer" catchtap="handleTap">
<view id="inner">点击我</view>
</view>
// page.js
Page({
handleTap: function(evt) {
// 当点击inner节点时
// evt.target 是inner view组件
// evt.currentTarget 是绑定了handleTap的outer view组件
// evt.type == “tap”
// evt.timeStamp == 1542
// evt.detail == {x: 270, y: 63}
// evt.touches == [{identifier: 0, pageX: 270, pageY: 63, clientX: 270, clientY: 63}]
// evt.changedTouches == [{identifier: 0, pageX: 270, pageY: 63, clientX: 270, clientY: 63}]
}
})
5.事件对象属性中touch和changedTouches详情:
touch和changedTouches
3.事件绑定与冒泡捕获:
1.写法:
key="value"
自基础库版本1.5.0起,key 以bind或catch开头,接:,如: bind:tap、catch:tap、
capture-bind:tap、capture-catch-tap
value为字符串
2.功用:
bind和capture-bind的含义分别代表事件的冒泡阶段和捕获阶段;
触发的顺序:
捕获与冒泡触发顺序
描述: 捕获为从外到内,冒泡为从内到外,先捕获后冒泡
<view id="outer" bind:tap="handleTap4" capture-bind:tap="handleTap1">
outer view
<view id="inner" bind:tap="handleTap3" capture-bind:tap="handleTap2">
inner view
</view>
</view>
// 点击 inner view 事件触发顺序为: handleTap1、handleTap2、handleTap3、handleTap4
3.bind事件绑定不会阻止冒泡事件向上冒泡,catch事件绑定可以阻止冒泡事件向上冒泡
<view id="outer" bind:tap="handleTap4" capture-catch:tap="handleTap1">
outer view
<view id="inner" bind:tap="handleTap3" capture-bind:tap="handleTap2">
inner view
</view>
</view>
只会触发handleTap1,catch事件阻止了tap事件冒泡
六. 兼容:
1.影响微信小程序的实现的几个因素:
手机品牌/操作系统/微信版本/小程序基础库版本号
2.可通过 wx.getSystemInfo 或者 wx.getSystemInfoSync 获取小程序使用环境信息
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 // 用户字体大小设置
}
*/
3.判断API是否兼容:
if (wx.openBluetoothAdapter) {
wx.openBluetoothAdapter()
} else {
// 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
wx.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
})
}
4.wx.canIUse
作用:用于判断接口或者组件在当前宿主环境是否可用
参数格式为: API.method.param.options或者component.attribute.option
(1).API 代表 API 名字
(2).method 代表调用方式,有效值为return, success, object, callback
(3).param 代表参数或者返回值
(4).options 代表参数的可选值
// 判断接口及其参数在宿主环境是否可用
wx.canIUse('openBluetoothAdapter')
wx.canIUse('getSystemInfoSync.return.screenWidth')
wx.canIUse('getSystemInfo.success.screenWidth')
wx.canIUse('showToast.object.image')
wx.canIUse('onCompassChange.callback.direction')
wx.canIUse('request.object.method.GET')
component 代表组件名字
attribute 代表组件属性
option 代表组件属性的可选值
// 判断组件及其属性在宿主环境是否可用
wx.canIUse('contact-button')
wx.canIUse('text.selectable')
wx.canIUse('button.open-type.contact')
5.兼容办法
做向前兼容,保证小程序在旧版微信中的运行
在小程序管理后台设置“基础库最低版本设置”。例子:
当小程序只支持1.5.0版本以上的宿主环境时,运行低版本时,微信会提示版本过低之类的。
网友评论