# 微信小程序视图层的组件、数据绑定,列表、条件渲染
TIP
从本节内容开始,开始学习微信小程序视图层的原生组件、数据绑定、列表循环、条件渲染、视图层代码拆分和模板语法,这些基础内容掌握小程序项目开发中必备的组件。
- 小程序中的常用组件分类
- 视图层 - 数据绑定
- 视图层 - 列表渲染
- 视图层 - 条件渲染
- 视图层代码拆分和模板语法
- 视图层 - 事件绑定
# 一、小程序中组件的分类
TIP
小程序中的组件也是由宿主环境提供的,开发者可以基于组件快速搭建出小程序的页面结构。微信官方将小程序的组件分为了 11 类
在小程序中我们将类似 HTML 的标签,即 WXML 标签 叫做 组件
- ①、视图容器
- ②、基础内容
- ③、表单组件
- ④、xr-frame 解决方案,AR、VR、3D 框架
- ⑤、导航组件
- ⑥、媒体组件
- ⑦、map 地图组件
- ⑧、canvas 画布组件
- ⑨、开放能力
- ⑩、无障碍访问
- ⑪、导航栏组件
项目常用的组件有:视图容器、基础内容、表单组件、导航组件 也是我们要重点学习的。
# 1、视图容器组件
TIP
项目中常用的视图容器组件有:view
、scroll-view
、swiper
、swiper-item
# 1.1、view 视图容器
TIP
- 普通视图区域,类似于 HTML 中的 div ,是一个块级元素
- 常用来实现页面结构的布局效果
在 pages/index/index.wxml
中写入结构
<!-- pages/index/index.wxml -->
<view class="container">
<view class="con-item item-1">Arry老师</view>
<view class="con-item item-2">清心老师</view>
<view class="con-item item-3">Allen老师</view>
</view>
在 pages/index/index.wxss
中写入样式
/** pages/index/index.wxss **/
.container {
display: flex;
flex-direction: row;
/* flex-direction: column; */
justify-content: space-around;
}
.con-item {
width: 200rpx;
height: 200rpx;
background-color: skyblue;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
.item-2 {
background-color: tomato;
}
.item-3 {
background-color: green;
}
或 另一种方式布局
/**index.wxss**/
.container {
display: flex;
justify-content: space-around;
}
.container view {
width: 200rpx;
height: 200rpx;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
.container view:nth-child(1) {
background-color: tomato;
}
.container view:nth-child(2) {
background-color: skyblue;
}
.container view:nth-child(3) {
background-color: lightcoral;
}
效果如下
# 1.2、scroll-view 可滚动视图区域
TIP
可滚动视图区域。使用竖向滚动时,需要给scroll-view (opens new window)一个固定高度,通过 WXSS 设置 height。组件属性的长度单位默认为 px,2.4.0 (opens new window)起支持传入单位(rpx/px)
scroll-view 常用属性,更多属性点击查阅官方文档 (opens new window)
- scroll-x 允许横向滚动(注:横向滚动时,必须给 scroll-view 固定宽度)
- scroll-y 允许纵向滚动(注:纵向滚动时,必须给 scroll-view 固定高度)
常用来实现滚动列表效果
在 pages/list/list.wxml
中定义结构
<!--pages/list/list.wxml-->
<view class="container">
<view class="category">
<scroll-view class="nav-left" scroll-y="{{true}}">
<view class="nav-left-item nav-left-item-active">人气 TOP</view>
<view class="nav-left-item">爆款套餐</view>
<view class="nav-left-item">咖啡</view>
<view class="nav-left-item">奶茶</view>
<view class="nav-left-item">甜品小点</view>
<view class="nav-left-item">水果茶</view>
<view class="nav-left-item">烘焙轻食</view>
<view class="nav-left-item">大师咖啡</view>
<view class="nav-left-item">纯茶</view>
<view class="nav-left-item">拿铁家族</view>
<view class="nav-left-item">咖啡</view>
<view class="nav-left-item">奶茶</view>
</scroll-view>
<scroll-view class="nav-right" scroll-y="{{true}}">
<view>A</view>
<view>B</view>
<view>C</view>
<view>D</view>
<view>E</view>
</scroll-view>
</view>
</view>
在 pages/list/list.wxss
定义样式
/* pages/list/list.wxss */
page,
.container {
height: 100%;
}
.category {
height: 100%;
display: flex;
flex-direction: row;
}
.nav-left {
width: 24%;
height: 100%;
background-color: #f6f6f6;
}
.nav-right {
width: 76%;
height: 100%;
background-color: lightblue;
}
.nav-left-item {
height: 120rpx;
line-height: 120rpx;
text-align: center;
}
/* 选中左侧导航样式 */
.nav-left-item-active {
background-color: #fff;
color: #ff5762;
border-left: 8rpx solid #ff5762;
}
/* 去掉滚动条 */
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
.nav-right view {
height: 300rpx;
line-height: 300rpx;
text-align: center;
}
.nav-right view:nth-child(1) {
background-color: tomato;
}
.nav-right view:nth-child(2) {
background-color: skyblue;
}
.nav-right view:nth-child(3) {
background-color: lightpink;
}
.nav-right view:nth-child(4) {
background-color: lightgrey;
}
.nav-right view:nth-child(5) {
background-color: lightseagreen;
}
实现效果如下
# 1.3、swiper、swiper-item 滑块视图容器
TIP
- swiper 用于定义轮播图容器组件
- swiper-item 轮播图 item 组件,放置在 swiper 组件中,否则会导致未定义的行为
常用 swiper 相关属性
属性 | 描述 | 类型 | 默认值 | 必填 |
---|---|---|---|---|
indicator-dots | 是否显示面板指示点 | boolean | false | 否 |
indicator-color | 指示点颜色 | color | rgba(0, 0, 0, .3) | 否 |
indicator-active-color | 当前选中的指示点颜色 | color | #000000 | 否 |
autoplay | 是否自动切换 | boolean | false | 否 |
interval | 自动切换时间间隔 | number | 5000 | 否 |
circular | 是否采用衔接滑动 | boolean | false | 否 |
vertical | 滑动方向是否为纵向 | boolean | false | 否 |
在 pages/swiper/swiper.wxml
中定义 swiper 结构
<!--pages/swiper/swiper.wxml-->
<swiper
class="swiper-container"
indicator-dots
autoplay
current="2"
indicator-color="#fff"
indicator-active-color="grey"
circular
>
<swiper-item>
<view class="item-con">one</view>
</swiper-item>
<swiper-item>
<view class="item-con">two</view>
</swiper-item>
<swiper-item>
<view class="item-con">three</view>
</swiper-item>
<swiper-item>
<view class="item-con">four</view>
</swiper-item>
<swiper-item>
<view class="item-con">five</view>
</swiper-item>
</swiper>
在 pages/swiper/swiper.wxss
中定义样式
/* pages/swiper/swiper.wxss */
.swiper-container {
height: 300rpx;
background-color: skyblue;
}
.item-con {
height: 100%;
line-height: 300rpx;
text-align: center;
}
swiper-item:nth-child(1) .item-con {
background-color: tomato;
}
swiper-item:nth-child(2) .item-con {
background-color: skyblue;
}
swiper-item:nth-child(3) .item-con {
background-color: lightpink;
}
swiper-item:nth-child(4) .item-con {
background-color: lightseagreen;
}
swiper-item:nth-child(5) .item-con {
background-color: lightgrey;
}
# 2、icon 图标组件
详细配置 查阅官方文档 (opens new window) 即可
<!--pages/icon/icon.wxml-->
<view class="container">
<icon type="success" size="100rpx" color="#07c160" />
<icon type="success_no_circle" size="100rpx" color="red" />
<icon type="info" size="100rpx" color="#10AEFF" />
<icon type="warn" size="100rpx" color="#e7c000" />
<icon type="waiting" size="100rpx" color="#F76260" />
<icon type="search" size="100rpx" color="#B2B2B2" />
</view>
效果如下
# 3、text 文本组件
TIP
类似 HTML 中的 span 标签,是一个行内(内联)元素
内联元素不会自占一行,与其他内联元素在同一行显示,且宽高由内容撑起。
- user-select 属性实现长按选中文本内容的效果
- 注:只有 text 文本能实现长按选中文本内容的效果,其他标签无法实现
在 pages/text/text.wxml
定义页面结构
<!--pages/text/text.wxml-->
<view>
文本长按选中:
<text user-select="{{true}}">
艾编程 - 为每个互联网人提供高质量的终身学习平台
</text>
</view>
<view>
手机号长按选中:
<text user-select="{{true}}">1311234567</text>
</view>
<view>
身份证号长按选中:
<text user-select="{{true}}">110101200603072796</text>
</view>
在 pages/text/text.wxss
中定义样式
/* pages/text/text.wxss */
view {
margin-bottom: 50rpx;
}
效果如下
在真机上调试,效果更佳
# 4、rich-text 富文本组件
TIP
rich-text 支持把 HTML 字符串渲染为 WXML 结构
使用 rich-text 组件的 nodes 属性节点,将 HTML 字符串渲染为对应的 UI 效果
应用场景:后台通过富文本框编辑上传的文本内容,在前端进行结构渲染时使用。如新闻页面、文章、商品详情页等的渲染等。
<rich-text
nodes="<div style='width: 200px;height: 100px; background:skyblue;text-align:center;line-height: 100px;color:#fff;margin: 100px auto;'> <span>Arry老师</span> </div>"
/>
效果如下
# 5、button 按钮组件
TIP
相比与 HTML 中 button 按钮,在小程序中的 button 功能更丰富
通过 button 按钮的 open-type 属性可以调用微信开放能力(打开客服会话、转发、获取用户手机号、用户信息、打开 App、获取用户头像等)
<!--pages/button/button.wxml-->
<!-- 按钮的样式类型,通过 type 属性设置 -->
<view></view>
<button>默认按钮</button>
<button type="primary">绿色按钮</button>
<button type="default">白色按钮</button>
<button type="warn">红色警告按钮</button>
<!-- 镂空按钮 -->
<button type="primary" plain>绿色按钮</button>
<button type="default" plain>白色按钮</button>
<button type="warn" plain>红色警告按钮</button>
<!-- 按钮禁用 -->
<button disabled="true">按钮禁用</button>
<!-- 名称前是否带 loading 图标 -->
<button type="primary" loading="true">loading 按钮</button>
<!-- 小尺寸按钮 type="mini" -->
<button type="primary" size="mini">绿色按钮</button>
<button type="default" size="mini">白色按钮</button>
<button type="warn" size="mini">红色警告按钮</button>
<!-- open-type 微信开发能力 -->
<button open-type="contact">打开客服会话</button>
<button open-type="share">触发用户转发</button>
<button open-type="feedback">打开“意见反馈”页面</button>
效果如下
注:
目前看到的样式是小程序新版的默认样式,如果不需要可在 app.json
中删除以下配置即可
{
"style": "v2"
}
删除后效果如下
# 6、image 图片组件
mode 属性,常用图片裁剪、缩放模式的值如下
mode 的值 | 说明 |
---|---|
scaleToFill | 缩放模式,不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素 |
aspectFit | 缩放模式,保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。 |
aspectFill | 缩放模式,保持纵横比缩放图片,只保证图片的短边能完全显示出来。也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。 |
widthFix | 缩放模式,宽度不变,高度自动变化,保持原图宽高比不变 |
heightFix | 缩放模式,高度不变,宽度自动变化,保持原图宽高比不变 |
<!--
不添加图片地址时
image 组件默认宽度320px、高度240px
-->
<image></image>
<!-- 改变 mode 的值,来测试效果 -->
<image src="../../img/cat.jpg" mode="aspectFit" />
效果如下
# 7、navigator 导航组件
TIP
- navigator 页面链接
- 类似 HTML 中的 a 标签,但又有很大的不同
- 实际开发中会讲导航分为:声明式导航、编程式导航、导航传参,其中会涉及到 JS 逻辑层的内容 和 微信 API 相关知识,后续在统一讲解。目前先做了解即可 !
# 二、视图层 - 数据绑定
TIP
深入浅出微信小程序视图层的 WXML 模板语法 - 数据绑定方式,绑定内容,绑定属性,控制属性,关键字,运算 ....
# 1、小程序中数据绑定的方式
TIP
- 在 data 中定义数据
- 在 WXML 中使用数据
与 Vue 类似的语法结构
# 1.1、data 中定义数据
TIP
在页面中的 .js
文件中,把数据定义到 data 对象中。
如下
// pages/index/index.js
Page({
data: {
// 字符串类型的数据
message: "arry老师",
// 数组类型的数据
userInfo: [{ username: "arry老师" }, { sex: "man" }, { age: 18 }],
},
});
# 1.2、Mustache 语法实现数据绑定
TIP
将 data 中的数据绑定到小程序页面中进行渲染,使用 Mustache 语法(双大括号)将变量包起来。Mustache 语法也被叫做 “插值表达式”
可作用于:内容、组件属性、控制属性、关键字、运算、组合使用等
<!--pages/index/index.wxml-->
<!-- {{ 要绑定的数据名称 }} -->
<view>{{ message }}</view>
类似 Vue 模板语法中的 插值表达式 ,详细查看 Vue 的官方文档 - 模板语法 (opens new window)
# 1.3、Mustache 语法的应用场景
TIP
- 绑定内容(组件属性、控制属性、关键字)
- 运算(三元运算、算术运算、逻辑判断、字符串运算、数据路径运算)
# 2、动态绑定内容
在小程序 .js
页面逻辑中
// index.js
Page({
data: {
msg: "艾编程",
},
});
在小程序.wxml
页面中
<!--index.wxml-->
<view>{{ msg }}</view>
# 3、动态绑定属性
在小程序 .js
页面逻辑中
// index.js
Page({
data: {
id: 0,
imgUrl: "https://web.arryblog.com/applets-img/new-product-2.png",
},
});
注:
将以上 imgUrl 地址的值修改为 https://www.arryblog.com/mall-img/product/new-product-2.png
即可成功访问
在小程序.wxml
页面中
<!--index.wxml-->
<view id="item-{{ id }}"></view>
<image src="{{ imgUrl }}" mode="widthFix" />
效果如下
注:
在 Vue 中动态绑定属性会使用 v-bind
指令,详细查阅 Vue 官方文档 - Attribute 绑定 (opens new window)
小程序中 不论绑定内容还是属性都需要用到 Mustache 语法
# 4、控制属性
在小程序.wxml
页面中
<!--index.wxml-->
<!-- 如果 condition = true 显示该元素,false 不显示 -->
<view wx:if="{{condition}}">arry老师</view>
在小程序 .js
页面逻辑中
// index.js
Page({
data: {
condition: true,
},
});
# 5、关键字
TIP
true
:boolean 类型的 true,代表真值。false
: boolean 类型的 false,代表假值。
在小程序.wxml
页面中
<!--index.wxml-->
<view class="container">
<view>
爱好:
<label><checkbox checked="{{true}}" />篮球 </label>
<label><checkbox checked="{{true}}" />书法 </label>
<label><checkbox checked="{{false}}" />画画</label>
</view>
</view>
特别注意:
不要直接写 checked="false"
,其计算结果是一个字符串,转成 boolean 类型后代表真值。
# 6、运算
TIP
可以在 {{}}
内进行简单的运算。
支持的有如下几种方式
# 6.1、三元运算
在小程序.wxml
页面中
<view hidden="{{ flag ? true : false }}">arry老师</view>
在小程序 .js
页面逻辑中
// index.js
Page({
data: {
flag: false,
},
});
# 6.2、算数运算
在小程序.wxml
页面中
<!--index.wxml-->
<view> {{a + b}} + {{c}} + d </view>
在小程序 .js
页面逻辑中
// index.js
Page({
data: {
a: 1,
b: 2,
c: 3,
},
});
view
中的内容为3 + 3 + d
# 6.3、逻辑判断
在小程序.wxml
页面中
<!--index.wxml-->
<view wx:if="{{length > 5}}"> arry老师 </view>
在小程序 .js
页面逻辑中
// index.js
Page({
data: {
length: 6,
},
});
# 6.4、字符串运算
在小程序.wxml
页面中
<!--index.wxml-->
<view>{{"Hello " + username}}</view>
在小程序 .js
页面逻辑中
// index.js
Page({
data: {
username: "arry老师",
},
});
页面显示结果:“Hello arry 老师”
# 6.5、数据路径运算
在小程序.wxml
页面中
<!--index.wxml-->
<view>{{object.key}} {{array[0]}} , {{array[1]}}</view>
在小程序 .js
页面逻辑中
// index.js
Page({
data: {
object: {
key: "Hello ",
},
array: ["arry老师", "Allen老师"],
},
});
页面显示结果:“Hello arry 老师 ,Allen 老师”
# 三、视图层 - 列表渲染
TIP
在组件上使用 wx:for
控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组的当前项的
- 下标(索引)变量名默认为
index
,即当前循环的索引 - 数组当前项的变量名默认为
item
,即当前循环的每一项
# 1、wx:for
在小程序.wxml
页面中
<!--index.wxml-->
<view wx:for="{{users}}"> 索引:{{index}} ,item 项: {{item.username}} </view>
在小程序 .js
页面逻辑中
// index.js
Page({
data: {
users: [
{ username: "arry老师" },
{ username: "allen老师" },
{ username: "清心老师" },
],
});
运行结果如下
# 2、手动指定元素和下标的变量名
TIP
- 使用
wx:for-item
可以指定数组当前元素的变量名 - 使用
wx:for-index
可以指定数组当前下标的变量名
<!--index.wxml-->
<!--
默认值
wx:for-index="index"
wx:for-item="item"
-->
<view wx:for="{{users}}" wx:for-index="idx" wx:for-item="itemName">
{{idx}} - {{itemName.username}}
</view>
# 3、wx:for 也可以嵌套
下边是一个九九乘法表
<!--index.wxml-->
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="i">
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="j">
<view wx:if="{{i <= j}}"> {{i}} * {{j}} = {{i * j}} </view>
</view>
</view>
注:
以上代码,运行时控制台报警告提示:性能不好,建议 为wx:for
提供wx:key
以提高性能。(如下控制台)
优化后的代码(不再报警告信息)
<!--index.wxml-->
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="i" wx:key="*this">
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="j" wx:key="*this">
<view wx:if="{{i <= j}}"> {{i}} * {{j}} = {{i * j}} </view>
</view>
</view>
# 4、wx:key 的使用
TIP
在小程序开发中,列表的渲染建议为渲染出来的列表选项指定唯一的 key 值,从而提高渲染的性能和效率
类似于 Vue 列表渲染中的
:key
,对于 Vue 的官方文档 - 通过 key 管理状态 (opens new window)
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input (opens new window) 中的输入内容,switch (opens new window) 的选中状态),需要使用 wx:key
来指定列表中项目的唯一的标识符。
wx:key
的值以两种形式提供
- ①、字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
- ②、保留关键字
*this
代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。
当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
如不提供 wx:key
,会报一个 warning
, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。
添加 wx:key
的代码,不再报警告 (在没有 id 时,可使用 *this
或 默认的index
作为唯一的 key 值)
<view wx:for="{{users}}" wx:key="*this"> {{index}} - {{item.username}} </view>
<!-- 或 -->
<view wx:for="{{users}}" wx:key="index"> {{index}} - {{item.username}} </view>
如果有 id
时,推荐使用 id 作为唯一的 key 值
// index.js
Page({
data: {
users: [
{ id: 1001, username: "arry老师" },
{ id: 1002, username: "allen老师" },
{ id: 1003, username: "清心老师" },
],
});
<!--index.wxml-->
<!-- 这里的 key 只需要给每一项的 id 名称即可 -->
<view wx:for="{{users}}" wx:key="id">
{{ item.id }} - {{ item.username }}
</view>
# 5、block wx:for 结构块
TIP
类似 block wx:if
,也可以将 wx:for
用在<block/>
标签上,以渲染一个包含多节点的结构块。
例如:
<!--
<view wx:for="{{users}}">Hello</view>
<view wx:for="{{users}}">World</view>
-->
<!-- 以上代码执行输出,并不是我们想要的将 “Hello World” 输出 3遍 -->
<!-- block 为 包含多节点的结构块,以下输出就符合我们的预期了 -->
<block wx:for="{{users}}" wx:key="*this">
<view> Hello </view>
<view> World </view>
</block>
// index.js
Page({
data: {
users: [1, 2, 3],
},
});
# 6、注意事项
当 wx:for
的值为字符串时,会将字符串解析成字符串数组
<view wx:for="array" wx:key="*this"> {{item}} </view>
等同于
<view wx:for="{{['a','r','r','a','y']}}" wx:key="*this"> {{item}} </view>
注意:
花括号和引号之间如果有空格,将最终被解析成为字符串
<view wx:for="{{[1,2,3]}} " wx:key="*this"> {{item}} </view>
等同于
<view wx:for="{{[1,2,3] + ' '}}" wx:key="*this"> {{item}} </view>
# 7、表格布局实践应用
TIP
使用我们学过的 wx:for
循环输出一个小学生的课表(效果如下),数据动态从 courseList
中获取,设置 WXSS 样式实现表格效果,样式自定义
data 数据如下
courseList: [
{'content': ['语文', '数学', '体育']},
{'content': ['数学', '语文', '音乐']},
{'content': ['美术', '英语', '体育']},
{'content': ['数学', '语文', '英语']},
{'content': ['语文', '数学', '实践']}
]
具体实现代码如下
<!--pages/index/index.wxml-->
<view class="table">
<view wx:for="{{courseList}}" wx:key="*this" class="col">
<view class="week">星期 {{index + 1}}</view>
<view class="course" wx:for="{{item.content}}" wx:key="*this"
>{{item}}</view
>
</view>
</view>
/**index.wxss**/
.table {
width: 90%;
display: flex;
align-items: center;
justify-content: space-between;
text-align: center;
border: 1rpx solid #000;
margin: 0 auto;
margin-top: 20rpx;
}
.col {
width: 20%;
}
.week,
.course {
border: 1rpx solid #000;
padding: 0 10rpx;
}
// index.js
Page({
data: {
courseList: [
{ content: ["语文", "数学", "体育"] },
{ content: ["数学", "语文", "音乐"] },
{ content: ["美术", "英语", "体育"] },
{ content: ["数学", "语文", "英语"] },
{ content: ["语文", "数学", "实践"] },
],
},
});
# 四、视图层 - 条件渲染
TIP
按照条件渲染视图层的内容也是小程序开发中常用的指令
# 1、wx:if
在框架中,使用 wx:if=""
来判断是否需要渲染该代码块
<!--pages/index/index.wxml-->
<!-- condition: true 显示 / false 不显示 view标签 -->
<view wx:if="{{condition}}"> arry老师 </view>
// index.js
Page({
data: {
condition: true,
},
});
也可以用 wx:elif
和 wx:else
来添加一个 else 块:
<!--pages/index/index.wxml-->
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length < 3}}"> 2 </view>
<view wx:else> 3 </view>
// index.js
Page({
data: {
length: 8,
},
});
应用场景,对用户性别的条件判断展示
<view>
性别:
<text wx:if="{{ sex === 1 }}">男</text>
<text wx:elif="{{ sex === 2 }}">女</text>
<text wx:else>保密</text>
</view>
Page({
data: {
sex: 1,
},
});
# 2、block wx:if
因为 wx:if
是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个 <block/>
标签将多个组件包装起来,并在上边使用 wx:if
控制属性。
<!--pages/index/index.wxml-->
<block wx:if="{{true}}">
<view> arry老师 </view>
<view> allen老师 </view>
</block>
注意:
<block/>
并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
# 3、wx:if vs hidden
TIP
因为 wx:if
之中的模板也可能包含数据绑定,所以当 wx:if
的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。
同时 wx:if
也是惰性的,如果在初始渲染条件为 false
,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。
相比之下,hidden
就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。
一般来说,wx:if
有更高的切换消耗而 hidden
有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden
更好,如果在运行时条件不大可能改变则 wx:if
较好。
<!-- wx:if 为 true 时 显示 -->
<view wx:if="{{true}}"> 艾编程 wx:if </view>
<!-- hidden 为 false 时 显示 -->
<view hidden="{{false}}"> 艾编程 hidden </view>
# 4、总结:wx:if 与 hidden 的对比
TIP
详细对比两者的关系
# 4.1、运行方式不同
TIP
wx:if
以动态创建和移除元素的方式,控制元素的展示与隐藏- hidden 以 切换样式的方式(
display: none/block;
)控制元素的显示域隐藏
注:
通过改变 true/false
的值在控制台观察 WXML 的结构变化 和 display 值的变化
# 4.2、使用建议
TIP
- 频繁切换时,建议使用 hidden
- 控制条件复杂时,建议使用
wx:if
搭配wx:elif
、wx:else
进行展示 与 隐藏的切换
# 五、视图层代码拆分和模板语法
TIP
我们重点看两个部分的内容
- 对视图层的代码进行拆分
- 模板(template)语法
# 1、视图层代码的拆分
TIP
使用 include 标签引入,include 将目标文件整个代码引入,相当于是拷贝到 include 位置
<!--pages/index/index.wxml-->
<view> header </view>
<!-- include 将目标文件整个代码引入,相当于是拷贝到 include 位置 -->
<include src="./content" />
<view> footer </view>
在 index.wxml
文件同级目录下新建 content.wxml
<!--pages/index/content.wxml-->
<view> {{content}} </view>
// index.js
Page({
data: {
content: "这是 content 的动态数据",
},
});
注意事项:
直接使用 include 标签语法引入的方式,是有缺陷的。会造成我们整个项目工程文件的可维护性变低
如:以上代码中如需查找 content
变量,就需要层层查找,一旦项目文件多了,几乎无法查阅。
为了解决这个问题,小程序中就有了
template
模板的语法
# 2、template 模板
TIP
WXML 提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。
定义模板
新建一个
content.wxml
文件,使用 name 属性 定义一个名为 content-template 的 template 模板
<!--pages/index/content.wxml-->
<!-- 使用 name 属性 定义一个名为 content-template 的模板 -->
<template name="content-template">
<view> {{content}} </view>
</template>
使用模板
在
index.wxml
中使用 import 引入 content 模板。使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入
<!--pages/index/index.wxml-->
<!-- 通过 import 引入 content 模板 -->
<import src="./content.wxml" />
<view> header </view>
<!--
1、使用 content.wxml 模板时,用 is 属性 = "模板名称"
2、template 模板无法直接使用到 逻辑层 index.js 中的 content 变量
3、如果需要使用,就需要给 template 模板传递该变量 ,通过 data = {{逻辑层的变量名}} 传递
4、如下代码中 data="{{content}}" 相当于 data="{{content: content}}"
-->
<template is="content-template" data="{{content}}" />
<view> footer </view>
data 数据
// index.js
Page({
data: {
content: "这是 content 的动态数据",
},
});
# 3、模板的作用域
TIP
模板拥有自己的作用域,只能使用 data 传入的数据。
# 六、视图层 - 事件绑定
TIP
深入浅出小程序中的事件,事件对象、属性、事件传参、事件绑定,及综合实践
# 1、是什么是事件
TIP
事件是渲染层(视图层)到逻辑层的通讯方式。通过事件可以将用户在渲染层产生的行为,反馈到逻辑层进行业务的处理。
# 2、小程序常用的事件
TIP
微信小程序中相关事件,详情见官方文档 (opens new window)
类型 | 绑定方式 | 描述 |
---|---|---|
tap | bindtap 或 bind:tap | 手指触摸后马上离开,类似 HTML 中的 click 事件 |
input | bindinput 或 bind:input | 文本框的输入事件 |
change | bindchange 或 bind:change | 状态改变时触发 |
# 3、事件对象的属性
TIP
当事件回调函数触发的时候,会收到一个事件对象 event
它的详细属性如下
属性 | 类型 | 描述 |
---|---|---|
type | String | 事件类型 |
timeStamp | Integer | 页面打开到触发事件所经过的毫秒数 |
target | Object | 触发事件的源组件属性值的集合 |
currentTarget | Object | 事件绑定的当前组件属性值的集合 |
detail | Object | 额外的信息 |
touches | Array | 触摸事件,当前停留在屏幕中的触摸点信息的数组 |
changedTouches | Array | 触摸事件,当前变化的触摸点信息的数组 |
# 4、target 和 currentTarget 的区别
TIP
- target 是触发该事件的源头组件
- currentTarget 是当前事件所绑定的组件
<!-- 给外层的 view 绑定事件 -->
<view class="container" bindtap="handlerBtn">
<button type="primary" plain>按钮</button>
</view>
.container {
width: 100%;
height: 200rpx;
background-color: lightblue;
display: flex;
align-items: center;
}
注:
当点击内部按钮时,点击事件会以冒泡的方式向外扩散,同时还会触发外层 view 的 tap 事件处理函数。相对于外层的 view 来说,如下
e.target
指向的是触发事件的源头组件。即e.target
是内部的按钮组件e.currentTarget
指向的是当前正在触发事件的那个组件。即e.currentTarget
是当前的 view 组件
关于事件命名,我们经常会使用
handler
开头。翻译过来为:驯兽员 或 操作者如:
handleNumberChange
、handleSelect
等 。handler
# 5、bindtap 的用法
TIP
由于小程序中没有 DOM 和 BOM,因此小程序不存在 HTML 中的 onclick 鼠标点击事件,而是通过 tap 事件来响应用户的触摸行为。
通过 bindtap 为组件绑定 tap 触摸事件,如下
<!-- 事件绑定 -->
<button type="primary" bindtap="handlerBtn">点我一下</button>
在 .js
页面逻辑中定义对应的事件处理函数,事件参数通过形参 event
(大部分都会简写为 e
)来接收
Page({
// 按钮的 tap 事件处理函数
handlerBtn(e) {
// 事件参数对象 e
console.log(e);
},
});
# 6、在事件处理函数中为 data 赋值
TIP
通过调用 this.setData(dataObject)
方法,可以给页面 data 中的数据重新赋值。如下
在页面中 .js
逻辑中
Page({
// 页面的初始数据
data: {
count: 0,
},
// 点击 +1 事件
handlerCount(e) {
// console.log("点我了");
// 给页面中的 data 数据重新赋值
this.setData({
count: this.data.count + 1,
});
},
});
在 .wxml
页面中
<button type="primary" bindtap="handlerCount">点我 + 1</button>
<view> 显示count值:{{ count }} </view>
# 7、在小程序中的事件传参
TIP
在小程序中的事件传参比较特殊,不能在绑定事件的同时为事件处理函数传递参数。
<!-- 如下错误示范 -->
<button type="primary" bindtap="handlerCount(100)">点我</button>
因为,在小程序中会把 bindtap 的属性值,统一当做事件名称来处理,相当于要调用一个名称为 handlerCount(100)
的事件处理函数,而不是调用 handlerCount()
并传递参数 100
在 Vue 中的事件传参就 类似 HTMl 结构中的点击事件传参一样了。详细查阅 Vue 官方文档 - 事件处理 (opens new window)
在小程序中的事件传参语法
可以为组件提供 data-*
自定义属性传参,其中 *
代表的是参数的名称。如下
<!-- 该方法的 num 参数会被解析为:字符串类型 100 -->
<button type="primary" bindtap="handlerBtn" data-num="100">点我</button>
<!-- 该方法的 num 参数会被解析为:数字类型 100 -->
<button type="primary" bindtap="handlerBtn" data-num="{{ 100 }}">点我</button>
注:
- num 会被解析为 参数的名称
- 数字 100 会被解析为参数的值
在小程序中只能通过该方式传递参数
# 8、在小程序中获取事件传参的值
TIP
在事件处理函数中,通过 e.target.dataset.参数名称
方式,即可获取到具体参数值。
如下
handlerBtn(e){
// 获取携带的 num 参数的具体值
const num = e.target.dataset.num;
console.log(num); // 100
// dataset 是一个对象,包含了所有通过 data-* 传递过来的所有参数项
const params = e.target.dataset;
console.log(params); // {count: "1", num: 100}
// e.currentTarget 指向的是当前正在触发事件的那个组件
const params = e.currentTarget.dataset;
}
在 .wxml
页面中
<button type="primary" bindtap="handlerBtn" data-num="{{ 100 }}" data-count="1">
点我
</button>
# 9、bindinput 事件
TIP
在小程序中,通过 input 事件来响应文本框的输入事件。
通过 bindinput 可以为文本框绑定输入事件
<input type="text" bindinput="handlerInput" />
在页面的 .js
文件中定义事件处理函数
Page({
handlerInput(e) {
// e.detail.value 是变化过后,文本框最新的值
const value = e.detail.value;
console.log(value);
},
});
# 10、文本框 与 data 之间的数据同步
实现步骤如下
- ①、定义数据
Page({
data: {
info: "arry老师",
},
});
- ②、渲染页面结构
<input type="text" value="{{ info }}" bindinput="handlerInput" />
<!-- 在页面中实时展示文本框的值 -->
<view> Hello ,{{ info }} </view>
- ③、美化样式
input {
border: 1px solid gray;
margin: 20rpx;
padding: 10rpx;
border-radius: 10rpx;
font-size: 30rpx;
}
- ④、绑定 input 事件处理函数
Page({
data: {
info: "arry老师",
},
handlerInput(e) {
// 通过 e.detail.value 获取文本框最新的值
const value = e.detail.value;
// 更新 data 中的数据
this.setData({
info: value,
});
},
});
通过控制台中的 AppData 也可以实时查看到 data 中的数据变化
# 11、应用实践:加减数量组件
在 .wxml
中定页面结构
<view class="container">
<!-- 主容器 -->
<view class="stepper">
<!-- 减号 -->
<text class="{{minusStatus}}" bindtap="bindMinus">-</text>
<!-- 数值 -->
<input type="number" bindchange="bindManual" value="{{num}}" />
<!-- 加号 -->
<text class="normal" bindtap="bindPlus">+</text>
</view>
</view>
在 .wxss
中定义样式
.container {
display: flex;
justify-content: center;
align-items: center;
margin-top: 200rpx;
}
/*主容器*/
.stepper {
width: 160rpx;
height: 52rpx;
/*给主容器设一个边框*/
border: 2rpx solid #ccc;
border-radius: 6rpx;
display: flex;
}
/*加号和减号*/
.stepper text {
width: 36rpx;
line-height: 52rpx;
text-align: center;
}
/*数值*/
.stepper input {
width: 80rpx;
height: 52rpx;
text-align: center;
font-size: 24rpx;
/*给中间的input设置左右边框即可*/
border-left: 2rpx solid #ccc;
border-right: 2rpx solid #ccc;
}
/*普通样式*/
.stepper .normal {
color: black;
}
/*禁用样式*/
.stepper .disabled {
color: #ccc;
}
在 .js
文件中定义页面逻辑
Page({
data: {
// input默认是1
num: 1,
// 使用data数据对象设置样式名
minusStatus: "disabled",
},
/* 点击减号 */
bindMinus() {
var num = this.data.num;
// 如果大于1时,才可以减
if (num > 1) {
num--;
}
// 只有大于1的时候,才能normal状态,否则disable状态
var minusStatus = num <= 1 ? "disabled" : "normal";
// 将数值与状态写回
this.setData({
num: num,
minusStatus: minusStatus,
});
},
/* 点击加号 */
bindPlus() {
var num = this.data.num;
// 不作过多考虑自增1
num++;
// 只有大于1的时候,才能normal状态,否则disable状态
var minusStatus = num < 1 ? "disabled" : "normal";
// 将数值与状态写回
this.setData({
num: num,
minusStatus: minusStatus,
});
},
/* 输入框事件 */
bindManual(e) {
var num = e.detail.value;
// 将数值与状态写回
this.setData({
num: num,
});
},
});
大厂最新技术学习分享群
微信扫一扫进群,获取资料
X