一、简介
1、组件
- 视图容器
- view
- 普通视图区域
- 类似于 HTML 中的 div,是一个块级元素
- 常用来实现页面的布局效果
- scroll-view
- 可滚动的视图区域
- 常用来实现滚动列表的效果
- swiper 和 swiper-item
- 轮播图容器组件和轮播图 item 组件
- 基础内容
- 表单组件
- 导航组件
- 媒体组件
- map 地图组件
- canvas 画布组件
- 开放能力
- 无障碍访问
2、API
- 事件监听 API
- 特点:以 on 开头,用来监听某些事件的触发
- 举例:
wx.onWindowResize(function callback)
监听窗口尺寸变化的事件
- 同步 API
- 特点1:以 Sync 结尾的 API 都是同步 API
- 特点2:同步 API 的执行结果。可以通过函数返回值直接获取,如果执行出错会抛出异常
- 举例:
wx.setStorageSync('key', 'value')
向本地存储中写入内容
- 异步 API
- 特点:类似于 jQuery 中的
$.ajax(option)
函数,需要通过 success、fail、complete 接收回调的结果 - 举例:
wx.request()
发起网络数据请求,通过 success 回调函数接收数据
- 特点:类似于 jQuery 中的
二、WXML 模板语法
1、数据绑定
-
数据绑定的基本原则
-
在 data 中定义数据
-
在 WXML 中使用数据
-
把 data 中的数据绑定到页面中渲染,使用
Mustache
语法(双大括号) 将变量包起来<view>{{要绑定的数据名称}}</view>
-
-
Mustache 语法的使用场景
- 绑定内容
- 绑定属性
demo.js
Page({ data: { src: 'www.baidu.com' } })
demo.wxml
<image src="{{ src }}"></image>
- 运算(三元运算、算术运算等)
demo.js
Page({ data: { randomNum: Math.random()*10 // 生成 10 以内的随机数 } })
deom.wxml
<view>{{ randomNum >= 5 ? '随机数字大于或等于5' : '随机数小于5' }}</view>
2、事件绑定
事件是渲染层到逻辑层的通讯方式。通过事件可以将用户在渲染层的行为,反馈到逻辑层进行业务处理。
2.2.1 小程序中常用的事件
类型 | 绑定方式 | 事件描述 |
---|---|---|
tap | bindtap 或 band:tap | 手指触摸后马上离开,类似于 HTML 中的 |
input | bindinput 或 bind:input | 文本框的输入事件 |
change | bindchange 或 bind:change | 状态改变时触发 |
2.2.2 事件对象的属性列表
当事件回调触发的时候,会收到一个事件对象
event
,它的详细属性如下所示
2.2.3 target 和 currentTarget 的区别
target 是触发该事件的源头组件,而 currentTarget 则是当前事件所绑定的组件。
点击内部的按钮,点击事件以冒泡的方式向外扩散,也会触发外层 view 的 tap 事件处理函数。
此时,对于外层的 view 来说:
e.target
指向的是触发事件得到源头组件,因此,e.target
是内部的按钮组件e.currentTarget
指向的是当前正在触发事件的那个组件,因此,e.currentTarget
是当前的 view 组件
2.2.4 bindtap 的语法格式
在小程序中,不存在 HTML 中的 onclick 鼠标点击事件,而是通过 tap 事件来响应用户的触摸行为
-
通过
bindtap
,可以为组件绑定 tap 触摸事件<button type="primary" bindtap="btnHandle">按钮</button>
-
在页面的
.js
文件中定义对应的事件处理函数,事件参数通过形参event
(一般简写成 e)来接受Page({ // 定义按钮的事件处理函数 btnHandle(e) { // 事件对象 event console.log(e); // 获取自定义属性(参数) console.log(e.target.dataset.val); } })
2.2.5 在事件处理函数中为 data 中的数据赋值
通过调用
this.setData(dataObject)
方法,可以给页面 data 中的数据重新赋值
Page({
data: {
count: 0
}
// 修改 count 的值
changeCount() {
this.setData({
count: this.data.count + 1
})
}
})
2.2.6 事件传参
可以为组件提供
data-*
自定义属性传参,其中 * 代表的是参数的名字
如果不使用 Mustache 语法(双大括号),参数 2 会被解析为字符串格式
<button type="primary" bindtap="btnHandle" data-val="{{2}}">事件传参</button>
- val 会被解析为参数的名字
- 数值 2 会被解析为参数的值
在事件处理函数中,通过
event.target.dataset.参数名
来获取到具体参数的值
btnHandle(e) {
// 获取自定义属性(参数)val
console.log(e.target.dataset.val);
}
2.2.7 bindinput 的语法格式
在小程序中,通过
input
事件来响应文本框的输入事件
-
通过
bindinput
,可以为文本框绑定输入事件<input bindinput="inputHandle" type="text"/>
-
在页面的
.js
文件中定义对应的事件处理函数inputHandle(e) { // 通过 e.detail.value 获取文本框最新的值,是变化过后的值 console.log(e.detail.value); }
2.2.8 实现文本框和 data 之间的数据同步(双向数据绑定)
实现思路:
- 先将文本框的内容和 data 的 msg 进行动态绑定
- 监听文本框输入事件,
通过 this.setData()
将文本框的最新值(event.detail.value
)赋给msg
具体步骤:
-
定义数据
Page({ data: { msg: '你好' } })
-
渲染结构
<input value="{{msg}}" bindinput="inputHandle" type="text"/>
-
绑定 input 事件处理函数
inputHandle(e) { this.setData({ // 通过 e.detail.value 获取文本框最新的值 msg: e.detail.value }) }
3、条件渲染
2.3.1 wx:if
在小程序中,使用
wx:if="{{ condition }}"
来判断是否需要渲染该代码块
<view wx:if="{{ condition }}">True</view>
也可以使用
wx:elif
和wx:else
来添加判断
<view wx:if="{{ type === 1 }}">男</view>
<view wx:elif="{{ type === 2 }}">女</view>
<view wx:else>保密</view>
2.3.2 结合 <block>
标签使用 wx:if
如果要一次性控制多个组件的展示与隐藏,可以使用一个
<block></block>
标签将多个组件包装起来,并在<block>
标签上使用wx:if
控制属性。<block>
标签只起包裹作用,不参与页面渲染,相当于vue
中的<template>
<block wx:if="{{true}}">
<view>view1</view>
<view>view2</view>
</block>
2.3.3 hidden
在小程序中,直接使用
hidden="{{ condition }}"
也能控制元素的现实与隐藏
<view hidden="{{ condition }}">条件为 true 隐藏,条件为 false 显示</view>
2.3.4 wx:if 与 hidden 的对比
- 运行方式不同
wx:if
以动态创建和移除元素的方式,控制元素的展示与隐藏hidden
以切换样式的方式(display: none/block;
),控制元素的现实与隐藏
- 使用建议
- 频繁切换是,建议使用
hidden
- 控制条件复杂时,建议使用
wx:if
搭配wx:elif
、wx:else
进行展示与隐藏的切换
- 频繁切换是,建议使用
4、列表渲染
2.4.1 wx:for
通过 wx:for 可以根据指定的数组,循环渲染重复的组件结构
默认情况下,当前循环项的索引用
index
表示;当前循环项用item
表示
wx:key="index"
绑定key值。不加 {{}}
<view wx:for="{{arr}}" wx:key="index">
当前项的索引:{{index}}, 当前项:{{item}}
</view>
三、WXSS 模板样式
1、WXSS 和 CSS 的关系
WXSS 具有 CSS 大部分特征,同时,WXSS 还对 CSS 进行了扩充以及修改,以适应微信小程序的开发,与 CSS 相比,WXSS 扩展的特征有:
-
rpx 尺寸单位
rpx(responsive pixel)是微信小程序独有的,用来解决屏幕适配的尺寸单位
实现原理:
鉴于不同设备屏幕的大小不同,为了实现屏幕的自动适配,rpx 把所有设备的屏幕在宽度上等分为 750 份(即:当前屏幕的总宽度为 750 rpx)
- 在较小的设备上,1 rpx 所代表的宽度较小
- 在较大的设备上,1 rpx 所代表的宽度较大
小程序在不同设备上运行的时候,会自动把 rpx 的样式单位换算成对应的像素单位来渲染,从而实现屏幕适配。
官方建议:开发微信小程序时,设计师可以使用 iPhone6 作为视觉稿的标准(1px 正好等于 2rpx)。
开发举例:在 iPhone6 上如果要绘制宽 100px,高 20px 的盒子,换算成 rpx 单位,宽高分别为 200rpx 和 40rpx。
-
@import 样式导入
@import 后跟需要导入的外联样式表的相对路径,用 ; 表示语句结束。
/** common.wxss **/ body { margin: 0; padding: 0; }
@import "common.wxss"; .box1 { padding: 10px; }
2、全局样式和局部样式
- 在
app.wxss
文件中定义的样式为全局样式,作用于每一个页面。 - 在页面的
.wxss
文件中定义的样式为局部样式,只作用于当前页面。
注意:
- 当局部样式和全局样式冲突时,根据就近原则,局部样式会覆盖全局样式。
- 当局部样式的权重大于或等于全局样式的权重时,才会覆盖全局的样式。
四、全局配置
1、 全局配置文件及常用的配置项
小程序根目录下的 app.json
文件时小程序的全局配置文件。常用的配置项如下:
- pages
- 记录当前小程序所有页面的存放路径
- window
- 全局配置小程序窗口的外观
- tabBar
- 设置小程序底部的 tabBar 效果
- style
- 是否启用新版的组件样式
2、 window
4.2.1 小程序窗口的组成部分
4.2.2 window 节点常用的配置项
设置导航栏的标题
设置步骤:app.json -> window -> navigationBarTitleText
设置导航栏的背景色
设置步骤:app.json -> window -> navigationBarBackgroundColor
设置导航栏的标题颜色
设置步骤:app.json -> window -> navigationBarTextStyle
全局开启下拉刷新功能
概念:下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为。
设置步骤:app.json -> window -> 把 enablePullDownRefresh
的值设置为 true(在 app.json 中开启下拉刷新会作用于每一个页面)
设置下拉刷新时窗口的背景色
当全局开启下拉刷新功能之后,默认窗口背景颜色为白色。
设置步骤:app.json -> window -> 为 backgroundColor
指定 16 进制的颜色值(如:#efefef)
设置下拉刷新时 loading 的样式
当全局开启下拉刷新功能之后,默认窗口的 loading 样式为白色。
设置步骤:app.json -> window -> 为 backgroundTextStyle
指定 dark 值。(可选值只有 dark 和 light)
设置上拉触底的距离
概念:上拉触底是移动端的专有名词,指的是通过手指在屏幕上的上拉滑动操作,从而加载更多数据的行为。
设置步骤:app.json -> window -> 把 onReachBottomDistance
设置新的数值
示例
"window": {
"navigationBarBackgroundColor": "#2b4b6b",
"navigationBarTitleText": "第一个小程序",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"backgroundColor": "#999",
"backgroundTextStyle": "dark",
"onReachBottomDistance": 50
}
3、 tabBar
4.3.1 什么是 tabBar
tabBar 是移动端应用常见的页面效果,用于实现多页面的快速切换。小程序通常将其分为:
- 底部 tabBar
- 顶部 tabBar
注意:
- tabBar 中只能配置最少 2 个、最多 5 个 tab 页签
- 当渲染顶部 tabBar 时,不显示 icon,只显示文本
4.3.2 tabBar 的六个组成部分
- backgroundColor:tabBar 的背景色
- selectedIconPath:选中时的图片路径
- borderStyle:tabBar 上边框的颜色
- iconPath:未选中时的图片路径
- selectedColor:tab 上的文字选中时的颜色
- color:tab 上文字的默认(未选中)颜色
4.3.3 tabBar 节点的配置项
4.3.4 每个 tab 项的配置选项
示例
"pages": [
"pages/demo01/demo01",
"pages/list/list",
],
"tabBar": {
"selectedColor": "#C00000", // tab 上的文字选中时的颜色
"list": [
{
"pagePath": "pages/demo01/demo01", // 页面路径,必须在 pages 中预先定义
"text": "demo01" // tab 上显示的文字
"iconPath": "图片路径", // 未选中时的图标路径
"selectedIconPath": "图片路径" // 选中时的图标路径
},
{
"pagePath": "pages/list/list",
"text": "demo02"
"iconPath": "图片路径",
"selectedIconPath": "图片路径"
}
]
}
五、网络数据请求
1、小程序中网络数据请求的限制
出于安全性方面的考虑,小程序官方对数据接口的请求做出了如下两个限制:
- 只能请求 HTTPS 类型的接口
- 必须将接口的域名添加到信任列表中
2、配置 request 合法域名
配置步骤:
登录微信小程序管理后台 -> 开发 -> 开发设置 -> 服务器域名 -> 修改 request 合法域名
注意事项
- 域名只支持 https 协议
- 域名不能使用 IP 地址或 localhost
- 域名必须经过 ICP 备案
- 服务器域名一个月最多可申请 50 次修改
3、发起 GET 请求
调用微信小程序提供的
wx.request()
方法,可以发起 GET 数据请求
wx.request({
// 请求的接口地址,必须基于 https 协议
url: 'https://www.escook.cn/api/get',
// 请求方式
method: 'GET',
// 带给服务器的数据
data: {
name: 'andy',
age: 18
},
// 请求成功后的回调
success: res => {
console.log(res);
}
})
4、发起 POST 请求
调用微信小程序提供的
wx.request()
方法,可以发起 POST 数据请求(配置 method 为 POST)
wx.request({
// 请求的接口地址,必须基于 https 协议
url: 'https://www.escook.cn/api/get',
// 请求方式
method: 'POST',
// 带给服务器的数据
data: {
name: 'jack',
age: 20
},
// 请求成功后的回调
success: res => {
console.log(res);
}
})
5、在页面刚加载时请求数据
在页面刚加载的时候,自动请求一些初始化的数据。此时需要在页面的 onLoad 事件中调用获取数据的函数
/**
* 生命周期函数--监听页面加载(页面一加载就触发这个函数)
*/
onLoad(options) {
this.getInfo()
},
// 发送 get 请求
getInfo() {
wx.request({
url: 'https://www.escook.cn/api/get',
method: 'GET',
data: {
name: 'andy',
age: 18
},
success: res => {
console.log(res);
}
})
},
6、跳过 request 合法域名校验
在开发过程中,如果后端程序员仅仅提供了 http 协议的接口、暂时没有提供 https 协议的接口,此时,为了不耽误开发的进度,我们可以在微信开发者工具中,临时开启【不校验请求域名、web-view(业务域名)、TLS 版本及 HTTPS 证书】选项,跳过 request 合法域名的校验
注意:跳过 request 合法域名校验的选项,仅限在开发与调试阶段使用!
7、关于跨域和 Ajax 的说明
跨域问题只存在于基于浏览器的 Web 开发中。由于小程序的宿主环境不是浏览器,而是微信客户端,所以小程序中不存在跨域的问题。
Ajax 技术的核心是依赖于浏览器中的
XMLHttpRequest
这个对象,由于小程序的宿主环境是微信客户端,所以小程序不能叫做“发起 Ajax 请求”,而是叫做“发起网络数据请求”。
六、页面导航
6.1 什么是页面导航
页面导航指的是页面之间的相互跳转。例如,浏览器中实现页面导航的方式有如下两种:
<a>
链接location.href
6.2 小程序中实现页面导航的两种方式
6.2.1 声明式导航
在页面上声明一个
<navigator>
导航组件通过点击
<navigator>
组件实现页面跳转
-
导航到 tabBar 页面
tabBar 页面指的是被配置为 tabBar 的页面。在使用
<navigator>
组件跳转到指定的 tabBar 页面时,需要指定url
属性和open-type
属性,其中:-
url
表示要跳转的页面的地址,必须以 / 开头 -
open-type
表示跳转得到方式,必须为switchTab
<navigator url="/pages/message/message" open-type="switchTab">消息页面</navigator>
-
-
导航到非 tabBar 页面
非 tabBar 页面指的是没有被配置为 tabBar 的页面。在使用
<navigator>
组件跳转到普通的非 tabBar 页面时,需要指定url
属性和open-type
属性,其中:-
url
表示要跳转的页面的地址,必须以 / 开头 -
open-type
表示跳转得到方式,必须为navigate
<navigator url="/pages/index/index" open-type="navigate">普通页面</navigator>
注意:为了简便在导航到非 tabBar 页面时,
open-type="navigate"
属性可以省略 -
-
后退导航
如果要后退到上一页面或多级页面,则需要指定
open-type
属性和delta
属性,其中:-
open-type
的值必须是 navigateBack,表示要进行后退导航 -
delta
的值必须是数字,表示要后退的层级<navigator open-type="navigateBack" delta="1">后退导航</navigator>
注意:为了简便,如果只是后退到上一页面,则可以省略
delta
属性,因为其默认值就是 1 -
6.2.2 编程式导航
调用小程序的导航 API,实现页面的跳转
-
导航到 tabBar 页面
调用
wx.switchTab(Object object)
方法,可以跳转到 tabBar 页面。其中 Object 参数对象的属性列表如下:// 页面结构 <button bindtap="goMessage">编程式导航</button> // 通过编程式导航,跳转到 message 页面 goMessage() { wx.switchTab({ url: '/pages/message/message', }) }
-
导航到非 tabBar 页面
调用
wx.navigateTo(Object object)
方法,可以跳转到非 tabBar 页面。其中 Object 参数对象的属性列表如下:// 页面结构 <button bindtap="goInfo">编程式导航</button> // 通过编程式导航,跳转到 info 页面 goMessage() { wx.navigateTo({ url: '/pages/info/info', }) }
-
后退导航
调用
wx.navigateBack(Object object)
方法,可以返回上一页面或多级页面。其中 Object 参数对象可选的属性列表如下:// 页面结构 <button bindtap="goBack">编程式导航</button> // 通过编程式导航,跳转到 info 页面 goMessage() { wx.navigateBack() }
注意:为了简便,如果只是后退到上一页面,则可以省略
delta
属性,因为其默认值就是 1
6.3 导航传参
6.3.1 声明式导航传参
navigate 组件的 url 属性用来指定将要跳转到的页面的路径。同时,路径的后面还可以携带参数:
- 参数与路径之间使用 ? 分隔
- 参数键与参数值用 = 相连
- 不同参数用 & 分隔
<navigator url="/pages/message/message?name=zs&age=20">消息页面</navigator>
6.3.2 编程式导航传参
调用 wx.navigateTo(Object object)
方法跳转页面时,也可以携带参数
// 页面结构
<button bindtap="goInfo">编程式导航</button>
// 通过编程式导航,跳转到 info 页面
goMessage() {
wx.navigateTo({
url: '/pages/info/info?name=zs&age=20',
})
}
6.3.3 在 onLoad 中接收导航参数
通过声明式导航传参或编程式导航传参所携带的参数,可以直接在 onLoad 事件中直接获取到
/**
* 生命周期函数--监听页面加载(页面一加载就触发这个函数)
*/
onLoad(options) {
// options 就是导航传递过来的参数对象
console.log(options)
}
七、页面事件
7.1 下拉刷新事件
7.1.1 什么是下拉刷新
下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为。
7.1.2 启用下拉刷新
启用下拉刷新的两种方式:
-
全局开启下拉刷新
在
app.json
的 window 节点中,把enablePullDownRefresh
的值设置为 true"window": { "enablePullDownRefresh": true, }
-
局部开启下拉刷新
在页面的
.json
配置文件中,把enablePullDownRefresh
的值设置为 true{ "usingComponents": {}, "enablePullDownRefresh": true }
在实际开发中,推荐使用第二种方式,为需要的页面单独开启下拉刷新的效果
7.1.3 监听页面的下拉刷新事件
在页面的
.js
文件中,通过onPullDownRefresh()
函数即可监听当前页面的下拉刷新事件
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
console.log('触发了下拉刷新事件');
}
7.1.4 停止下拉刷新的效果
当处理完下拉刷新后,下拉刷新的 loading 效果会一直显示,不会主动消失,所以需要手动隐藏 loading 效果。此时,调用 wx.stopPullDownRefresh() 可以停止当前页面的下拉刷新
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
console.log('触发了下拉刷新事件');
// 当下拉刷新操作成功后,调用此函数,关闭下拉刷新效果
wx.stopPullDownRefresh()
}
八、生命周期
8.1 什么是生命周期
生命周期(LIfe Cycle)是指一个对象从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。
我们可以把每个小程序运行的过程,也概括为生命周期:
- 小程序的启动,表示生命周期的开始
- 小程序的关闭,表示生命周期的结束
- 中间小程序运行的过程,就是小程序的生命周期
8.2 生命周期的分类
在小程序中,生命周期分为两类,分别是:
- 应用生命周期
- 特指小程序从启动 -> 运行 -> 销毁的过程
- 页面生命周期
- 特指小程序中,每个页面的加载 -> 渲染 -> 销毁的过程
其中,页面的生命周期范围较小,应用程序的生命周期范围较大,如图所示:
8.3 什么是生命周期函数
-
生命周期函数
是由小程序框架提供的内置函数,会伴随着生命周期,自动按次序执行。
-
生命周期函数的作用
允许程序员在特定的时间点,执行某些特定的操作。例如,页面刚加载的时候,可以在 onLoad 生命周期函数中初始化页面的数据。
注意:生命周期强调的是时间段,生命周期函数强调的是时间点。
8.4 生命周期函数的分类
在小程序中,生命周期函数分为两类,分别是:
- 应用的生命周期函数
- 特指小程序从启动 -> 运行 -> 销毁期间依次调用的那些函数
- 页面的生命周期函数
- 特指小程序中,每个页面的加载 -> 渲染 -> 销毁期间依次调用的那些函数
8.5 应用的生命周期函数
小程序的应用生命周期函数需要在
app.js
中进行声明
// app.js
App({
/**
* 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
*/
onLaunch: function () {},
/**
* 当小程序启动,或从后台进入前台显示,会触发 onShow
*/
onShow: function (options) {},
/**
* 当小程序从前台进入后台,会触发 onHide
*/
onHide: function () {},
/**
* 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
*/
onError: function (msg) {}
})
8.6 页面的生命周期函数
小程序的页面生命周期函数需要在页面的
.js
中进行声明
// pages/home/home.js
Page({
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {},
/**
* 生命周期函数--监听页面显示
*/
onShow() {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {}
})
-
onLoad(Object query)
页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。
参数:
名称 类型 说明 query Object 打开当前页面路径中的参数 -
onShow()
页面显示/切入前台时触发。
-
onReady()
页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
注意:对界面内容进行设置的 API 如 wx.setNavigationBarTitle(修改页面标题),请在
onReady
之后进行。详见生命周期 -
onHide()
页面隐藏/切入后台时触发。 如 wx.navigateTo 或底部
tab
切换到其他页面,小程序切入后台等。 -
onUnload()
页面卸载时触发。如 wx.redirectTo 或 wx.navigateBack 到其他页面时。
九、wxs 脚本
9.1 概念
9.1.1 什么是 wxs
WXS(WeiXin Script) 是小程序独有的一套脚本语言,结合
wxml
,可以构建出页面的结构
9.1.2 wxs 的应用场景
wxml
中无法调用在页面的.js
中定义的函数,但是,wxml
中可以调用wxs
中定义的函数。因此,小程序中wxs
的典型应用场景就是“过滤器”
9.1.3 wxs 和 JavaScript 的关系
虽然 wxs 的语法类似于 JavaScript,但是 WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致
9.2 基本语法
9.2.1 内嵌 wxs 脚本
wxs 代码可以编写在 wxml 文件中的 <wxs>
标签内,就像 JavaScript 代码可以编写在 html 文件中的 <script>
标签内一样。
wxml 文件中的每个 <wxs></wxs>
标签,必须提供 module 属性,用来指定当前 wxs 的模块名称,方便在 wxml 中访问模块中的成员
<view>{{ m1.toUpper(username) }}</view>
<wxs module='m1'>
// 将文本转为大写形式 zs -> ZS
// 向外暴露一个名叫 toUpper 的方法
module.export.toUpper = function(str) {
// 将结果返回出去
return str.toUpperCase()
}
</wxs>
9.2.2 定义外联的 wxs 脚本
wxs 代码还可以编写在以 .wxs
为后缀名的文件内,就像 JavaScript 代码可以编写在以 .js
为后缀名的文件中一样。
// tools.wxs 文件
// 将文本转为小写形式 ZS -> zs
function toLower(str) {
return str.toLowerCase()
}
// 将 toLower 共享出去,供外界使用
module.export = {
toLower: toLower
}
9.2.3 使用外联的 wxs 脚本
在 wxml 中引入外联的 wxs 脚本时,必须为 <wxs>
标签添加 module 和 src 属性,其中:
- module 用来指定模块的名称
- src 用来指定要引入的脚本的路径,且必须是相对路径
<!-- 调用 tools 模块中的方法 -->
<view>{{tools.toLower(username)}}</view>
<!-- 引入外联的 tools.wxs 脚本,并命名为 tools -->
<wxs src="../../utils/tools.wxs" module="tools"></wxs>
9.3 wxs 的特点
-
与 JavaScript 不同
为了降低 wxs 的学习成本,wxs 语言在设计时借鉴了大量 JavaScript 的语法,但是在本质上,wxs 和 JavaScript 是完全不同的两种语言!
-
不能作为组件的事件回调
wxs 典型的应用场景就是“过滤器”,经常配合
Mustache
语法进行使用,例如:<view>{{tools.toLower(username)}}</view>
但是,在 wxs 中定义的函数不能作为组件的事件回调函数。例如,下面的用法是错误的:
<button bindtap="tools.toLower"></button>
-
隔离性
隔离性指的是 wxs 的运行环境和其他 JavaScript 代码是隔离的。体现在如下两方面:
- wxs 不能调用 js 中定义的函数
- wxs 不能调用小程序提供的 API
-
性能好
- 在 IOS 设备上,小程序内的 wxs 会比 JavaScript 代码快 2~20 倍
- 在 Android 设备上,二者的运行效率无差异
十、自定义组件
10.1 组件的创建与使用
10.1.1 创建组件
- 在项目的根目录中,鼠标右键,创建 components -> test 文件夹
- 在新建的 components -> test 文件夹上,鼠标右键,点击“新建Component”
- 键入组件的名称之后会漕河,会自动生成组件对应的 4 个为恩建,后缀名分别为
.js
、.json
、.wxml
和.wxss
注意:为了保证目录结构的清晰,建议把不同的组件,存放到单独的目录中,例如:
10.1.2 引用组件
组件的引用方式分为“局部引用”和“全局引用”
-
局部引用
- 组件只能在当前被引用的页面内使用。
- 在页面的 .json 配置文件中引用组件的方式,叫做“局部引用”
// 在页面的 .json 文件中,引入组件 { "usingComponents": { "my-test": "/components/test/test" } } // 在页面的 .wxml 文件中,使用组件 <my-test></my-test>
-
全局引用
- 组件可以在每个小程序页面中使用
- 在 app.json 全局配置文件中引用组件的方式,叫做“全局引用”
// 在页面的 app.json 文件中,引入组件 { "pages": [] "window": {} "usingComponents": { "my-test": "/components/test/test" } } // 在页面的 .wxml 文件中,使用组件 <my-test></my-test>
10.1.3 组件和页面
从表面来看,组件和页面都是由
.js
、.json
、.wxml
和.wxss
这四个文件组成的。但是,组件和页面的.js
与.json
文件有明显的不同:
-
组件的
.json
文件中需要声明"component": true
属性{ "component": true }
-
组件的
.js
文件中调用的是Component()
函数// components/test/test.js Component({ /** * 组件的属性列表 */ properties: {}, /** * 组件的初始数据 */ data: {}, /** * 组件的方法列表 */ methods: {} })
-
组件的事件处理函数需要定义到
methods
节点中
10.2 样式
10.2.1 组件样式隔离
默认情况下,自定义组件的样式只对当前组件生效,不会影响到组件之外的 UI 结构。
好处:
- 防止外界的样式影响组件内部的样式
- 防止组件的样式破坏外界的样式
10.2.2 组件样式隔离的注意点
- app.wxss 中的全局样式对组件无效
- 只有 class 选择器会有样式隔离效果,id 选择器、属性选择器、标签选择器不受样式隔离的影响
建议:在组件和引用组件的页面中建议使用 class 选择器,不要使用id 选择器、属性选择器、标签选择器!
10.2.3 修改组件的样式隔离选项
默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题。但有时,我们希望在外界能够控制组件内部的样式,此时,可以通过 styleIsolation
修改组件的样式隔离选项,用法如下:
// 在组件的 .js 文件中新增如下配置
Component({
options: {
styleIsolation: 'isolated'
}
})
// 或在组件的 .json 文件中新增如下配置
{
styleIsolation: 'isolated'
}
styleIsolation
的可选值:
10.3 数据、方法和属性
10.3.1 data 数据
在小程序中,用于组件模板渲染的私有数据,需要定义到
data
节点中,示例如下:
Component({
/**
* 组件的初始数据
*/
data: {
count: 0
}
})
10.3.2 methods 方法
在小程序中,事件处理函数和自定义方法需要定义到
methods
节点中,示例如下:
Component({
/**
* 组件的方法列表
*/
methods: {
// 事件处理函数
add() {
this.setData({
count: this.data.count + 1
})
this._showCount() // 通过 this 直接调用自定义方法
},
// 自定义方法建议以 '_' 开头,以区分事件处理函数和自定义方法
_showCount() {
wx.showToast({
title: 'count的值是:' + this.data.count,
icon: 'none'
})
}
}
})
10.3.3 properties 属性
在小程序中,properties 是组件的对外属性,用来接收外界传递到组件中的数据,示例如下:
Component({
/**
* 组件的属性列表
*/
properties: {
// 第一种方式:简化定义属性的方式
max: Number
// 第二种方式:完整定义属性的方式【当需要指定属性默认值时,建议使用此方式】
max: {
type: Number, // 属性值的数据类型
value: 10 // 属性默认值【如果外界没有传递 max 的值,默认等于 10】
}
},
})
<my-test1 max="10"></my-test1>
注意:上述代码中的
max
可以通过this.properties.max
访问到。
10.3.4 data 和 properties 的区别
在小程序中,
properties
属性和data
数据的用法相同,它们都是可读可写的,只不过:
data
更倾向于存储组件的私有数据properties
更倾向于存储外界传递到组件的数据
Component({
methods:{
showInfo() {
console.log(this.data) // 输出结果: {count: 0, max: 10}
console.log(this.properties) // 输出结果: {count: 0, max: 10}
// 结果为 true,证明 data 数据和properties 属性在本质上是一样的、都是可读可写的
console.log(this.data === this.properties)
}
}
})
10.3.5 使用 setData 修改 properties 的值
由于
data
数据和properties
属性在本质上没有任何区别,因此properties
属性的值也可以用于页面渲染,或使用setData
为properties
中的属性重新赋值,示例代码如下:
Component({
properties: {
// 定义属性
max: Number
}
methods:{
addCount() {
// 使用 setData 修改属性的值
this.setData({
max: this.properties.max + 1
})
}
}
})
10.4 数据监听器
10.4.1 什么是数据监听器
数据监听器用于监听和响应任何属性和数据字段的变化,从而执行特定的操作。它的作用类似于
Vue
中的watch
侦听器。在小程序组件中,数据监听器的基本语法格式如下:
Component({
observers: {
// 可以在一个监听器中监听多个字段,中间用逗号隔开。新值的顺序就是字段声明时的顺序
'字段A, 字段B': function(字段A的新值, 字段B的新值) {
// do something
}
}
})
10.4.2 监听对象属性的变化
Component({
observers: {
// 可以在一个监听器中监听多个字段,中间用逗号隔开。新值的顺序就是字段声明时的顺序
'对象.属性A, 对象.属性B': function(属性A的新值, 属性B的新值) {
// do something
}
}
})
触发上述代码中的监听器有三种情况:
-
为属性 A 赋值
使用
setData
设置this.data.对象.属性A
时触发 -
为属性 B 赋值
使用
setData
设置this.data.对象.属性B
时触发 -
直接为对象赋值
使用
setData
设置this.data.对象
时触发
10.4.3 案列
需求:一个颜色可由 R、G、B 三个部分组成,在页面中创建一个
view
,其背景颜色动态绑定为属性fullColor
、创建三个按钮,每个按钮分别控制对象rgb
中的属性 r、g、b,使用监听器监听这三个属性的变化,动态计算出fullColor
的值,从而达到修改背景颜色的效果。
Component({
/**
* 组件的初始数据
*/
data: {
rgb: { // rgb 的颜色值对象
r: 0,
g: 0,
b: 0
},
fullColor: '0, 0, 0' // 根据 rgb 对象的三个属性,动态计算 fullColor 的值
}
})
步骤:
-
在页面中定义元素并绑定数据
<view class="box" style="background-color: rgb({{fullColor}});">rgb的值是:{{rgb.r}},{{rgb.g}},{{rgb.b}}</view> <view> <text class="color" bindtap="addR">R</text> <text class="color" bindtap="addG">G</text> <text class="color" bindtap="addB">B</text> </view>
-
在 wxss 中美化样式
/* components/test2/test2.wxss */ .box { width: 100%; height: 300rpx; text-align: center; line-height: 300rpx; color: #fff; } .color { display: inline-block; width: 100rpx; height: 100rpx; text-align: center; line-height: 100rpx; } .color:nth-child(1) { background-color: red; } .color:nth-child(2) { background-color: green; } .color:nth-child(3) { background-color: blue; }
-
定义 button 的事件处理函数
methods: { addR() { // 让属性自增加 5,并且判断自增过后是否大于 255,如果是,则永远等于 255 this.setData({ // 修改 rgb 对象上 r 属性的值 'rgb.r': this.data.rgb.r + 5 > 255 ? 255 : this.data.rgb.r + 5 }) }, addG() { this.setData({ // 修改 rgb 对象上 g 属性的值 'rgb.g': this.data.rgb.g + 5 > 255 ? 255 : this.data.rgb.g + 5 }) }, addB() { this.setData({ // 修改 rgb 对象上 b 属性的值 'rgb.b': this.data.rgb.b + 5 > 255 ? 255 : this.data.rgb.b + 5 }) } }
-
监听对象中指定属性的变化
// 监听器 observers: { // 函数中的参数为新值 // 要监听的属性用单引号包裹 // 监听多个属性用逗号隔开 'rgb.r, rgb.g, rgb.b': function(r, g, b) { this.setData({ // 为 data 中的 fullColor 重新赋值 fullColor: `${r}, ${g}, ${b}` }) } }
如果某个对象中需要被监听的属性太多,为了方便,可以使用通配符 ** 来监听对象中所有属性的变化,示例代码如下:
// 监听器 observers: { // 监听对象中所有属性的变化,使用通配符 ** // 只要任何一个属性发生变化都会触发监听器 'rgb.**': function(obj) { this.setData({ fullColor: `${obj.r}, ${obj.g}, ${obj.b}` }) } }
10.5 组件的生命周期
10.5.1 组件全部的生命周期函数
小程序组件可用的全部生命周期如下表所示:
生命周期 | 参数 | 描述 | 最低版本 |
---|---|---|---|
created | 无 | 在组件实例刚刚被创建时执行 | 1.6.3 |
attached | 无 | 在组件实例进入页面节点树时执行 | 1.6.3 |
ready | 无 | 在组件在视图层布局完成后执行 | 1.6.3 |
moved | 无 | 在组件实例被移动到节点树另一个位置时执行 | 1.6.3 |
detached | 无 | 在组件实例被从页面节点树移除时执行 | 1.6.3 |
error | Object Error | 每当组件方法抛出错误时执行 | 2.4.1 |
其中,最重要的生命周期是
created
attached
detached
,包含一个组件实例生命流程的最主要时间点。
- 组件实例刚刚被创建好时,
created
生命周期被触发。此时,组件数据this.data
就是在Component
构造器中定义的数据data
。 此时还不能调用setData
。 通常情况下,这个生命周期只应该用于给组件this
添加一些自定义属性字段。 - 在组件完全初始化完毕、进入页面节点树后,
attached
生命周期被触发。此时,this.data
已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行(例如发请求获取初始数据)。 - 在组件离开页面节点树后,
detached
生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则detached
会被触发。
10.5.2 定义生命周期函数
生命周期方法可以直接定义在 Component
构造器的第一级参数中。
自小程序基础库版本 2.2.3 起,组件的的生命周期也可以在 lifetimes
字段内进行声明(这是推荐的方式,其优先级最高)。
代码示例
Component({
// 推荐用法
lifetimes: {
attached: function() {
// 在组件实例进入页面节点树时执行
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
},
}
// 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容
attached: function() {
// 在组件实例进入页面节点树时执行
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
},
// ...
})
10.6 组件所在页面的生命周期
10.6.1 什么是组件坐在页面的生命周期
有时,自定义组件的行为依赖于页面状态的变化,此时就需要用到组件所在页面的生命周期,在 pageLifetimes
定义段中定义。其中可用的生命周期包括:
生命周期 | 参数 | 描述 | 最低版本 |
---|---|---|---|
show | 无 | 组件所在的页面被展示时执行 | 2.2.3 |
hide | 无 | 组件所在的页面被隐藏时执行 | 2.2.3 |
resize | Object Size | 组件所在的页面尺寸变化时执行 | 2.4.0 |
代码示例
Component({
pageLifetimes: {
show: function() {
// 页面被展示
},
hide: function() {
// 页面被隐藏
},
resize: function(size) {
// 页面尺寸变化
}
}
})
10.6.2 生成随机的 RGB 颜色值
在页面刚被展示的时候,生成随机的 RGB 颜色值
// pageLifetimes 节点可以访问组件所在页面的生命周期函数(只能访问三个)
pageLifetimes: {
// 页面被展示
show: function() {
// 生成随机颜色
this.setData({
rgb: {
r: Math.floor((Math.random()*256)),
g: Math.floor((Math.random()*256)),
b: Math.floor((Math.random()*256))
}
})
}
}