# 微信小程序视图层的组件、数据绑定,列表、条件渲染

TIP

从本节内容开始,开始学习微信小程序视图层的原生组件、数据绑定、列表循环、条件渲染、视图层代码拆分和模板语法,这些基础内容掌握小程序项目开发中必备的组件。

  • 小程序中的常用组件分类
  • 视图层 - 数据绑定
  • 视图层 - 列表渲染
  • 视图层 - 条件渲染
  • 视图层代码拆分和模板语法
  • 视图层 - 事件绑定

# 一、小程序中组件的分类

TIP

小程序中的组件也是由宿主环境提供的,开发者可以基于组件快速搭建出小程序的页面结构。微信官方将小程序的组件分为了 11 类

在小程序中我们将类似 HTML 的标签,即 WXML 标签 叫做 组件

点击查阅 小程序官方文档 - 组件 (opens new window)

  • ①、视图容器
  • ②、基础内容
  • ③、表单组件
  • ④、xr-frame 解决方案,AR、VR、3D 框架
  • ⑤、导航组件
  • ⑥、媒体组件
  • ⑦、map 地图组件
  • ⑧、canvas 画布组件
  • ⑨、开放能力
  • ⑩、无障碍访问
  • ⑪、导航栏组件

项目常用的组件有:视图容器、基础内容、表单组件、导航组件 也是我们要重点学习的。

# 1、视图容器组件

TIP

项目中常用的视图容器组件有:viewscroll-viewswiperswiper-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;
}

效果如下

image-20230331192151163

# 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 固定高度)

常用来实现滚动列表效果

GIF-2023-4-2-2-35-55

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;
}

实现效果如下

GIF-2023-4-2-3-30-38

# 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

更多 swiper 属性点击查阅官方文档 (opens new window)

GIF-2023-4-2-4-17-33

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>

效果如下

image-20230402042430497

# 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;
}

效果如下

GIF-2023-4-2-4-45-21

在真机上调试,效果更佳

# 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>"
/>

效果如下

image-20230402051530360

# 5、button 按钮组件

TIP

相比与 HTML 中 button 按钮,在小程序中的 button 功能更丰富

通过 button 按钮的 open-type 属性可以调用微信开放能力(打开客服会话、转发、获取用户手机号、用户信息、打开 App、获取用户头像等)

详细查阅 button 按钮官方文档 (opens new window)

<!--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>

效果如下

image-20230402060512127

注:

目前看到的样式是小程序新版的默认样式,如果不需要可在 app.json 中删除以下配置即可

{
  "style": "v2"
}

删除后效果如下

image-20230402060852286

# 6、image 图片组件

TIP

  • 支持 JPG、PNG、SVG、WEBP、GIF 等格式
  • image 组件默认宽度 320px、高度 240px

详细查阅 image 官方文档 (opens new window)

mode 属性,常用图片裁剪、缩放模式的值如下

mode 的值 说明
scaleToFill 缩放模式,不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素
aspectFit 缩放模式,保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。
aspectFill 缩放模式,保持纵横比缩放图片,只保证图片的短边能完全显示出来。也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。
widthFix 缩放模式,宽度不变,高度自动变化,保持原图宽高比不变
heightFix 缩放模式,高度不变,宽度自动变化,保持原图宽高比不变
<!-- 
    不添加图片地址时
    image 组件默认宽度320px、高度240px 
-->
<image></image>

<!-- 改变 mode 的值,来测试效果 -->
<image src="../../img/cat.jpg" mode="aspectFit" />

效果如下

image-20230402062833310

# 7、navigator 导航组件

TIP

  • navigator 页面链接
  • 类似 HTML 中的 a 标签,但又有很大的不同
  • 实际开发中会讲导航分为:声明式导航、编程式导航、导航传参,其中会涉及到 JS 逻辑层的内容 和 微信 API 相关知识,后续在统一讲解。目前先做了解即可 !

详细查阅 navigator 官方文档 (opens new window)

# 二、视图层 - 数据绑定

TIP

深入浅出微信小程序视图层的 WXML 模板语法 - 数据绑定方式,绑定内容,绑定属性,控制属性,关键字,运算 ....

详细查阅 小程序官方文档 - WXML 语法参考 (opens new window)

# 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" />

效果如下

image-20230403172947712

注:

在 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 类型后代表真值。

image-20230403174132629

# 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: "清心老师" },
        ],
    });

运行结果如下

image-20230403184132436

# 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以提高性能。(如下控制台)

image-20230403184926806

优化后的代码(不再报警告信息)

<!--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],
  },
});

image-20230215012756380

# 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>

image-20230215010855189

注意:

花括号和引号之间如果有空格,将最终被解析成为字符串

<view wx:for="{{[1,2,3]}} " wx:key="*this"> {{item}} </view>

等同于

<view wx:for="{{[1,2,3] + ' '}}" wx:key="*this"> {{item}} </view>

image-20230215011135195

# 7、表格布局实践应用

TIP

使用我们学过的 wx:for 循环输出一个小学生的课表(效果如下),数据动态从 courseList 中获取,设置 WXSS 样式实现表格效果,样式自定义

image-20230215131951383

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: ["语文", "数学", "实践"] },
    ],
  },
});

image-20230215132638096

# 四、视图层 - 条件渲染

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:elifwx: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;)控制元素的显示域隐藏

image-20230404210300785

注:

通过改变 true/false 的值在控制台观察 WXML 的结构变化 和 display 值的变化

# 4.2、使用建议

TIP

  • 频繁切换时,建议使用 hidden
  • 控制条件复杂时,建议使用 wx:if 搭配 wx:elifwx: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 的动态数据",
  },
});

image-20230215162314353

注意事项:

直接使用 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 的动态数据",
  },
});

image-20230215180406116

# 3、模板的作用域

TIP

模板拥有自己的作用域,只能使用 data 传入的数据。

# 六、视图层 - 事件绑定

TIP

深入浅出小程序中的事件,事件对象、属性、事件传参、事件绑定,及综合实践

# 1、是什么是事件

TIP

事件是渲染层(视图层)到逻辑层的通讯方式。通过事件可以将用户在渲染层产生的行为,反馈到逻辑层进行业务的处理。

image-20230405164607049

# 2、小程序常用的事件

TIP

微信小程序中相关事件,详情见官方文档 (opens new window)

类型 绑定方式 描述
tap bindtapbind:tap 手指触摸后马上离开,类似 HTML 中的 click 事件
input bindinputbind:input 文本框的输入事件
change bindchangebind:change 状态改变时触发

# 3、事件对象的属性

TIP

当事件回调函数触发的时候,会收到一个事件对象 event

它的详细属性如下

属性 类型 描述
type String 事件类型
timeStamp Integer 页面打开到触发事件所经过的毫秒数
target Object 触发事件的源组件属性值的集合
currentTarget Object 事件绑定的当前组件属性值的集合
detail Object 额外的信息
touches Array 触摸事件,当前停留在屏幕中的触摸点信息的数组
changedTouches Array 触摸事件,当前变化的触摸点信息的数组

# 4、target 和 currentTarget 的区别

TIP

  • target 是触发该事件的源头组件
  • currentTarget 是当前事件所绑定的组件

image-20230406012715427

<!-- 给外层的 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开头。翻译过来为:驯兽员 或 操作者

如:handleNumberChangehandleSelect 等 。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);
  },
});

image-20230406021640310

# 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);
  },
});

image-20230407105457608

# 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 中的数据变化

image-20230407112334943

# 11、应用实践:加减数量组件

GIF-2023-4-7-12-52-30

.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,
    });
  },
});
上次更新时间: 7/19/2024, 3:10:45 AM

大厂最新技术学习分享群

大厂最新技术学习分享群

微信扫一扫进群,获取资料

X