8.2 WXML模版
developers.weixin.qq.com/miniprogram…
WXML(WeiXin Markup Language)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构
developers.weixin.qq.com/miniprogram…
8.2.1 数据绑定
WXML 中的动态数据均来自对应 Page 的 data。
// app.json
{
pages: [
"pages/test/test",
....
]
}
- 简单绑定
-
组件属性(需要在双引号之内)
-
控制属性(需要在双引号之内)
-
关键字(需要在双引号之内)
true:boolean 类型的 true,代表真值。
false: boolean 类型的 false,代表假值。
8.2.2 列表渲染
8.2.3 条件渲染
8.2.4 模版
WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。
8.2.5 引用
WXML 提供两种文件引用方式import和include。
以上代码参照 pages/test/test.wxml 以及 pages/test/test.js
<!--pages/test/test.wxml-->
<!-- <text>pages/test/test.wxml</text>
<view style="height: 300px;font-size:40px;font-weight: bold">1</view>
<view style="height: 300px;font-size:40px;font-weight: bold">2</view>
<view style="height: 300px;font-size:40px;font-weight: bold">3</view>
<view style="height: 300px;font-size:40px;font-weight: bold">4</view>
<view style="height: 300px;font-size:40px;font-weight: bold">5</view>
<view style="height: 300px;font-size:40px;font-weight: bold">6</view>
<view style="height: 300px;font-size:40px;font-weight: bold">7</view>
<view style="height: 300px;font-size:40px;font-weight: bold">8</view>
<view style="height: 300px;font-size:40px;font-weight: bold">9</view> -->
<include src="footer.wxml"/>
<!--
如果属性值是变量,boolean类型,number类型,对象、数组、null、undefined,
在vue中使用绑定属性解决问题
在小程序中使用{{}} 解决问题
-->
<view>{{ message }}</view>
<view class="item-100"></view>
<view class="item-{{ id }}"></view>
<view wx:if="{{condition}}">真</view>
<checkbox value="篮球" checked="{{false}}"/>篮球
<view>{{ condition ? '真真' : '假假'}}</view>
<view wx:for="{{['a', 'b', 'c']}}" wx:key="*this">{{ item }}</view>
<view class="{{ { a: 1, b: 2 } }}"></view>
<!--
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item
* 使用 wx:for-item 可以指定数组当前元素的变量名,
* 使用 wx:for-index 可以指定数组当前下标的变量名:
需要使用 wx:key 来指定列表中项目的唯一的标识符。
wx:key 的值以两种形式提供
* 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
* 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。
* 不可以使用索引值作为key值
-->
<view wx:for="{{ list }}" wx:key="*this">{{ item }}</view>
<view wx:for="{{ arr }}" wx:key="id">{{ item.unique }}</view>
<view wx:for="{{ arr }}" wx:key = "id" wx:for-item="itm">{{ itm.unique }}</view>
<!--
条件渲染
vue v-if v-show
v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换。
总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。
minpro wx:if hidden
因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。
同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。
相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。
一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好。
vue v-if v-else-if v-else
minpro wx:if wx:elif wx:else
-->
<view>
<text> 1: 第一条消息 </text>
<text> Time: 2023/12/26 10:17 </text>
</view>
<!-- 模板 -->
<template name="msgItem">
<view>
<text> {{ index }}: {{ msg }} </text>
<text> Time: {{ time }} </text>
</view>
</template>
<!--
使用模板时,遇到对象 不要写 {}
如果数据源是对象类型的变量,使用...展开对象
-->
<template is="msgItem" data="{{ index: 0, msg: 'this is a template', time: '2016-09-15' }}"></template>
<template wx:for="{{ timeArr }}" wx:key="index" is="msgItem" data="{{ ...item }}"></template>
<!--
import引入模板
作用域问题C import B,B import A,在C中可以使用B定义的template,在B中可以使用A定义的template,但是C不能使用A定义的template
-->
<import src="item.wxml"/>
<template wx:for="{{ timeArr }}" wx:key="index" is="importItem" data="{{ ...item }}"></template>
<!-- include 使用模板 -->
<include src="footer.wxml"/>
// pages/test/test.js
Page({
/**
* 页面的初始数据
*/
data: {
message: "hello world",
id: 999,
condition: false,
list: ['aa', 'bb', 'cc', 'dd'],
arr: [
{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'},
],
timeArr: [
{ index: 1, msg: '第1条消息', time: '2023/12/16 10:24' },
{ index: 2, msg: '第2条消息', time: '2023/12/16 10:25' },
{ index: 3, msg: '第3条消息', time: '2023/12/16 10:26' }
]
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
<!-- pages/test/footer.wxml -->
<view>这里是公共的底部组件</view>
<!-- pages/test/item.wxml -->
<template name="importItem">
<view>
<text> {{ index }}: {{ msg }} </text>
<text> Time: {{ time }} </text>
</view>
</template>
8.3 WXSS 样式
WXSS 具有 CSS 大部分的特性,小程序在 WXSS 也做了一些扩充和修改
- 新增了尺寸单位。在写
CSS样式时,开发者需要考虑到手机设备的屏幕会有不同的宽度和设备像素比,采用一些技巧来换算一些像素单位。WXSS在底层支持新的尺寸单位rpx,开发者可以免去换算的烦恼,只要交给小程序底层来换算即可,由于换算采用的浮点数运算,所以运算结果会和预期结果有一点点偏差。 - 提供了全局的样式和局部样式。和前边
app.json,page.json的概念相同,你可以写一个app.wxss作为全局样式,会作用于当前小程序的所有页面,局部页面样式page.wxss仅对当前页面生效。 - 此外
WXSS仅支持部分CSS选择器
developers.weixin.qq.com/miniprogram…
- rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为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 |
| iPhone6 Plus | 1rpx = 0.552px | 1px = 1.81rpx |
建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
假如设计是给我们的设计稿,打开以后,发现 设计稿的宽度是 750px,那么我们在wxss中写宽度和高度时,可以直接写量取的数据,单位 rpx
如果设计师给的设计稿为375px,假设量取的宽度为100px,那么建议将wxss的单位写为 200rpx
注意: 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况。
8.4 js逻辑交互
8.4.1 什么是事件
- 事件是视图层到逻辑层的通讯方式。
- 事件可以将用户的行为反馈到逻辑层进行处理。
- 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。
- 事件对象可以携带额外信息,如 id, dataset, touches。
切记,自定义的事件是需要写到 js中的 选项中的
8.4.2 如何给事件传递参数
传递参数时,id具有特殊性,其余数据通过 data-params 属性传值
8.4.3 冒泡
biantap并不会阻止事件冒泡
catchtap 会阻止冒泡
<!--pages/test/test.wxml-->
<!-- <text>pages/test/test.wxml</text>
<view style="height: 300px;font-size:40px;font-weight: bold">1</view>
<view style="height: 300px;font-size:40px;font-weight: bold">2</view>
<view style="height: 300px;font-size:40px;font-weight: bold">3</view>
<view style="height: 300px;font-size:40px;font-weight: bold">4</view>
<view style="height: 300px;font-size:40px;font-weight: bold">5</view>
<view style="height: 300px;font-size:40px;font-weight: bold">6</view>
<view style="height: 300px;font-size:40px;font-weight: bold">7</view>
<view style="height: 300px;font-size:40px;font-weight: bold">8</view>
<view style="height: 300px;font-size:40px;font-weight: bold">9</view> -->
<include src="footer.wxml"/>
<!--
如果属性值是变量,boolean类型,number类型,对象、数组、null、undefined,
在vue中使用绑定属性解决问题
在小程序中使用{{}} 解决问题
-->
<view>{{ message }}</view>
<view class="item-100"></view>
<view class="item-{{ id }}"></view>
<view wx:if="{{condition}}">真</view>
<checkbox value="篮球" checked="{{false}}"/>篮球
<view>{{ condition ? '真真' : '假假'}}</view>
<view wx:for="{{['a', 'b', 'c']}}" wx:key="*this">{{ item }}</view>
<view class="{{ { a: 1, b: 2 } }}"></view>
<!--
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item
* 使用 wx:for-item 可以指定数组当前元素的变量名,
* 使用 wx:for-index 可以指定数组当前下标的变量名:
需要使用 wx:key 来指定列表中项目的唯一的标识符。
wx:key 的值以两种形式提供
* 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
* 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。
* 不可以使用索引值作为key值
-->
<view wx:for="{{ list }}" wx:key="*this">{{ item }}</view>
<view wx:for="{{ arr }}" wx:key="id">{{ item.unique }}</view>
<view wx:for="{{ arr }}" wx:key = "id" wx:for-item="itm">{{ itm.unique }}</view>
<!--
条件渲染
vue v-if v-show
v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换。
总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。
minpro wx:if hidden
因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。
同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。
相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。
一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好。
vue v-if v-else-if v-else
minpro wx:if wx:elif wx:else
-->
<view>
<text> 1: 第一条消息 </text>
<text> Time: 2023/12/26 10:17 </text>
</view>
<!-- 模板 -->
<template name="msgItem">
<view>
<text> {{ index }}: {{ msg }} </text>
<text> Time: {{ time }} </text>
</view>
</template>
<!--
使用模板时,遇到对象 不要写 {}
如果数据源是对象类型的变量,使用...展开对象
-->
<template is="msgItem" data="{{ index: 0, msg: 'this is a template', time: '2016-09-15' }}"></template>
<template wx:for="{{ timeArr }}" wx:key="index" is="msgItem" data="{{ ...item }}"></template>
<!--
import引入模板
作用域问题C import B,B import A,在C中可以使用B定义的template,在B中可以使用A定义的template,但是C不能使用A定义的template
-->
<import src="item.wxml"/>
<template wx:for="{{ timeArr }}" wx:key="index" is="importItem" data="{{ ...item }}"></template>
<!-- include 使用模板 -->
<include src="footer.wxml"/>
<!--
js逻辑运行
on ===> bind
click ===> tap (300ms延迟故事,点击穿透的故事:tap事件代替click事件、touch事件代替click事件、引入第三方 fastclick 事件)
meta viewport
minpro绑定事件 不要给事件添加(),会自带默认参数为event
如果遇到需要传递参数,使用dataset的方式
proid ===》 data-proid="1234"
自定义的事件,在js文件中可以作为 页面Page函数的参数对象的一个属性使用
事件冒泡 catch事件代替bind事件
-->
<view>{{ msg }}</view>
<button bind:tap="changeMsg">改变msg</button>
<button bind:tap="changeParamMsg" data-str="haha">改变msg,传递参数</button>
<view style="width: 200px;height: 200px;background-color: #ff6666;" bind:tap="clickContainer">
<view style="width: 100px;height: 100px;background-color: #00ff00;" bind:tap="clickBox"></view>
<view style="width: 100px;height: 100px;background-color: #0000ff;" catch:tap="clickBlue"></view>
</view>
<!-- wxs语法简单使用 -->
<!-- <script></script> -->
<wxs module="m1">
var msg = "hello world";
var num = 10000;
// module.exports.message = msg;
// module.exports.num = num;
module.exports = {
message: msg,
num: num
};
</wxs>
<view> {{m1.message}} </view>
<view> {{m1.num}} </view>
// pages/test/test.js
Page({
/**
* 页面的初始数据
*/
data: {
message: "hello world",
id: 999,
condition: false,
list: ['aa', 'bb', 'cc', 'dd'],
arr: [
{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'},
],
timeArr: [
{ index: 1, msg: '第1条消息', time: '2023/12/16 10:24' },
{ index: 2, msg: '第2条消息', time: '2023/12/16 10:25' },
{ index: 3, msg: '第3条消息', time: '2023/12/16 10:26' }
],
msg: 'hello world'
},
changeMsg () {
// vue this.msg = "hello minpro"
// react this.setState({ msg: 'hello minpro' })
this.setData({ msg: 'hello minpro' })
},
changeParamMsg (event) {
// 只要我们将事件处理程序直接绑定到目标元素,那么目标元素事件执行时,target 和 currentTarget 均指向的是该目标元素。
// 如果事件处理程序并未绑定在目标元素,而是在其祖先元素上时,那么target则指向的是该目标元素,而currentTarget指向的是当前绑定事件的祖先元素。
console.log(event)
this.setData({
msg: event.target.dataset.str
})
},
clickContainer () {
console.log('container')
},
clickBox () {
console.log('box')
},
clickBlue () {
console.log('blue')
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
9.小程序的宿主环境
9.1 渲染层和逻辑层
WXML 模板和 WXSS 样式工作在渲染层
JS 脚本工作在逻辑层
9.1.1 注册小程序
每个小程序都需要在 app.js 中调用 App 方法注册小程序实例,绑定生命周期回调函数、错误监听和页面不存在监听函数等
developers.weixin.qq.com/miniprogram…
注册小程序。接受一个 Object 参数,其指定小程序的生命周期回调等。
App() 必须在 app.js 中调用,必须调用且只能调用一次。不然会出现无法预期的后果。
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
|---|---|---|---|---|---|
| onLaunch | function | 否 | 生命周期回调——监听小程序初始化。 | ||
| onShow | function | 否 | 生命周期回调——监听小程序启动或切前台。 | ||
| onHide | function | 否 | 生命周期回调——监听小程序切后台。 | ||
| onError | function | 否 | 错误监听函数。 | ||
| onPageNotFound | function | 否 | 页面不存在监听函数。 | 1.9.90 | |
| onUnhandledRejection | function | 否 | 未处理的 Promise 拒绝事件监听函数。 | 2.10.0 | |
| onThemeChange | function | 否 | 监听系统主题变化 | 2.11.0 | |
| 其他 | any | 否 | 开发者可以添加任意的函数或数据变量到 Object 参数中,用 this 可以访问 |
// app.js
// app.js
App({
onLaunch() {
console.log('onLaunch')
// 展示本地存储能力
const logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
// 登录
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
}
})
},
// 其他
globalData: {
userInfo: null
},
onShow () {
console.log('onShow')
},
onHide () {
console.log('onHide')
},
onError () {
console.log('onError')
}
})
9.1.2 注册页面
对于小程序中的每个页面,都需要在页面对应的 js 文件中进行注册,指定页面的初始数据、生命周期回调、事件处理函数等。
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| data | Object | 页面的初始数据 | ||
| options | Object | 页面的组件选项,同 Component 构造器 中的 options ,需要基础库版本 2.10.1 | ||
| behaviors | String Array | 类似于 mixins 和traits的组件间代码复用机制,参见 behaviors,需要基础库版本 2.9.2 | ||
| onLoad | function | 生命周期回调—监听页面加载 | ||
| onShow | function | 生命周期回调—监听页面显示 | ||
| onReady | function | 生命周期回调—监听页面初次渲染完成 | ||
| onHide | function | 生命周期回调—监听页面隐藏 | ||
| onUnload | function | 生命周期回调—监听页面卸载 | ||
| onPullDownRefresh | function | 监听用户下拉动作 | ||
| onReachBottom | function | 页面上拉触底事件的处理函数 | ||
| onShareAppMessage | function | 用户点击右上角转发 | ||
| onShareTimeline | function | 用户点击右上角转发到朋友圈 | ||
| onAddToFavorites | function | 用户点击右上角收藏 | ||
| onPageScroll | function | 页面滚动触发事件的处理函数 | ||
| onResize | function | 页面尺寸改变时触发,详见 响应显示区域变化 | ||
| onTabItemTap | function | 当前是 tab 页时,点击 tab 时触发 | ||
| onSaveExitState | function | 页面销毁前保留状态回调 | ||
| 其他 | any | 开发者可以添加任意的函数或数据到 Object 参数中,在页面的函数中用 this 可以访问。这部分属性会在页面实例创建时进行一次深拷贝。 |
9.1.3页面路由
-
编程式导航路由
-
wx.switchTab({}) 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
-
wx.reLaunch({}) 关闭所有页面,打开到应用内的某个页面
-
wx.redirectTo({}) 关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面
-
wx.navigateTo({}) 保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面。使用 wx.navigateBack 可以返回到原页面。小程序中页面栈最多十层。
-
wx.navigateBack({}) 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages 获取当前的页面栈,决定需要返回几层
-
-
声明式导航路由
<navigator url="" open-type=""></navigator>
下面为open-type的属性值
| 合法值 | 说明 | 最低版本 |
|---|---|---|
| navigate | 对应 wx.navigateTo 或 wx.navigateToMiniProgram 的功能 | |
| redirect | 对应 wx.redirectTo 的功能 | |
| switchTab | 对应 wx.switchTab 的功能 | |
| reLaunch | 对应 wx.reLaunch 的功能 | 1.1.0 |
| navigateBack | 对应 wx.navigateBack 的功能 | 1.1.0 |
| exit | 退出小程序,target="miniProgram"时生效 | 2.1.0 |
9.1.4 模块化
可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。
- 方式1:使用commonjs规范,模块只有通过
module.exports或者exports才能对外暴露接口。
// pages/test/common.js
function sayHello(name) {
console.log(`Hello ${name} !`)
}
function sayGoodbye(name) {
console.log(`Goodbye ${name} !`)
}
module.exports.sayHello = sayHello
exports.sayGoodbye = sayGoodbye
// pages/test/test.js
var common = require('common.js')
Page({
...,
onLoad () {
common.sayHello('千锋教育')
common.sayGoodbye('吴大勋')
},
...
})
- 方式2:使用es6模块化规范
// pages/test/es6md.js
export function sayHello(name) {
console.log(`Hello ${name} !`)
}
export function sayGoodbye(name) {
console.log(`Goodbye ${name} !`)
}
// pages/test/test.js
import { sayHello, sayGoodbye } from './es6md'
Page({
...,
onLoad () {
sayHello('千锋教育 - 太原')
sayGoodbye('吴大勋 - HTML5')
},
...
})
9.2 组件
developers.weixin.qq.com/miniprogram…
搭建小程序页面时,参照组件篇章
通过给 app.json的pages选项添加pages/com/com测试小程序的常用组件
9.3 API
developers.weixin.qq.com/miniprogram…
通过小程序调用微信的功能时,参照API章节
通过给 app.json的pages选项添加pages/api/api测试小程序的常用组件
10小程序的自定义组件
developers.weixin.qq.com/miniprogram…
pages/com/components/child