微信小程序-组件开发

微信小程序提供了视图容器组件、基础内容组件、表单组件、操作反馈组件、导航组件、媒体组件、地图组件、画布组件等8类组件,包括了大部分UI需要用到的组件。

组件的通用属性

1.id属性

id属性为字符串类型(String),是组件的唯一标示,在同一页面中id属性必须保持唯一,不能重复。

id 用于定位:仅用于获取组件实例或CSS锚点,不要用于样式选择器(性能差)

2.class属性

class 属性为字符串类型(String),通过class属性来设置组件的样式类。该属性的值为样式类名称,该样式类的CSS样式定义在对应的WXSS文件中。如果与动态数据绑定相结合,组件的class也将具有动态变换的能力。

3.style属性

style属性为字符串类型(String),通过style属性可设置组件的内联样式。style属性值中可设置CSS的各种属性。如果与动态数据绑定相结合,组件的style也将具有动态变换的能力。

样式优先用 class:静态样式用 class,动态样式用 style

4. hidden 属性

hidden属性是一个逻辑值(Boolean),用来决定该组件是否显示。默认情况下,hidden属性的值为false,即组件为显示状态(不隐藏)。

隐藏用 hidden:频繁切换用 hidden,初始不显示用 wx:if

5.data-*属性

data-*属性可为任何类型,可用来为组件设置任意的自定义属性值。当组件上绑定的事件触发时,这些自定义属性将作为参数发送给事件处理函数,在事件处理函数中可通过传人参数对象的currentTarget.dataset方式来获取自定义属性的值。

html 复制代码
<!-- 传递多个数据 -->
<button 
  data-user-id="12345"
  data-user-name="张三"
  data-user-age="25"
  bindtap="handleUserInfo"
>
  传递用户信息
</button>

<!-- 支持驼峰命名(会自动转小写) -->
<view data-userName="李四" data-userAge="30" bindtap="handleData">
  测试驼峰命名
</view>
javascript 复制代码
Page({
  handleUserInfo(event) {
    // 通过 currentTarget.dataset 获取
    const userId = event.currentTarget.dataset.userId;    // "12345"
    const userName = event.currentTarget.dataset.userName; // "张三"
    const userAge = event.currentTarget.dataset.userAge;   // "25"
    
    console.log(userId, userName, userAge);
  },
  
  handleData(event) {
    // 驼峰命名会自动转小写
    console.log(event.currentTarget.dataset.username);    // "李四"
    console.log(event.currentTarget.dataset.userage);     // "30"
  }
});

注意事项:

  • 属性名中不能有大写字母,如需使用驼峰,实际会转为小写

  • 建议全部使用小写加短横线:data-user-name

数据传递用 data-*:简单数据传递优先使用 dataset

冒泡数据用 mark:需要多层级数据时使用 mark

6. bind* / catch* 事件绑定

用来为组件定义事件,bind和 catch的区别是,bind事件绑定不会阻止冒泡事件向上冒泡,catch事件绑定可以阻止冒泡事件向上冒泡。

html 复制代码
<!-- bind:事件会冒泡 -->
<view bindtap="parentTap">
  <button bindtap="childTap">冒泡事件</button>
</view>

<!-- catch:阻止冒泡 -->
<view bindtap="parentTap">
  <button catchtap="childTap">阻止冒泡</button>
</view>

<!-- 支持事件类型后缀 -->
<input bindinput="onInput" bindfocus="onFocus" bindblur="onBlur" />
<scroll-view bindscroll="onScroll" />

7. mut-bind:* 互斥绑定

同一节点上多个互斥绑定事件,只会触发其中一个。

html 复制代码
<!-- 点击时只会触发一个(通常是最外层的或特定规则) -->
<view mut-bind:tap="handleTap1" mut-bind:tap="handleTap2">
  互斥绑定测试
</view>

8. capture-bind:* / capture-catch:* 捕获阶段事件

执行顺序: 捕获阶段(从外到内)→ 目标阶段 → 冒泡阶段(从内到外

html 复制代码
<!-- 捕获阶段绑定(先于冒泡阶段执行) -->
<view capture-bind:tap="captureTap">
  <view bindtap="bubbleTap">点击测试</view>
</view>

<!-- 捕获阶段阻止事件传递 -->
<view capture-catch:tap="captureStopTap">
  <view bindtap="bubbleTap">不会触发bubbleTap</view>
</view>

9. mark:* 标记数据

与 data-* 类似,但 mark 数据会在冒泡时合并传递。

html 复制代码
<view mark:parentId="p001" bindtap="parentTap">
  <button mark:childId="c001" bindtap="childTap">点击</button>
</view>
javascript 复制代码
Page({
  childTap(event) {
    // 可以同时获取父组件和子组件的 mark 数据
    console.log(event.mark.parentId); // "p001"
    console.log(event.mark.childId);  // "c001"
  },
  
  parentTap(event) {
    console.log(event.mark.parentId); // "p001"
    console.log(event.mark.childId);  // undefined(从子组件冒泡过来才有)
  }
});

mark 与 dataset 的区别:

  • dataset:只包含当前组件的 data-* 数据

  • mark:包含冒泡路径上所有组件的 mark:* 数据(会合并)

视图容器组件

视图容器组件包括view、scroll-view、swiper及swiper-item,主要用于控制视图样式与内容展现。

用于布局和结构组织的基础组件,它们负责承载其他组件、控制布局方式以及实现滚动、轮播等高级交互功能。

组件名 主要功能 适用场景
<view> 基础容器,类似div 普通布局、结构划分
<block> 逻辑容器,不渲染 批量渲染、条件判断
<scroll-view> 可滚动视图 长列表、横向滚动、滚动动画
<swiper> 轮播容器 图片轮播、卡片滑动、引导页
<movable-view> 可移动容器 拖拽排序、悬浮窗
<cover-view> 覆盖原生组件 地图、视频上叠加文本/按钮
<page-container> 页面级容器 弹窗、抽屉、半屏效果
<share-element> 共享元素动画 页面转场动画

1. <view> - 基础容器

最基础的视图容器,功能类似于 HTML 的 <div>,用于布局、分组和样式控制。

特殊属性:

  • hover-class:点击时的样式类(默认 none)

  • hover-stop-propagation:阻止点击状态向父节点传递

  • hover-start-time:点击后多久出现点击态(单位ms)

  • hover-stay-time:点击态保持时间(单位ms)

html 复制代码
<view 
  hover-class="view-hover" 
  hover-start-time="50" 
  hover-stay-time="400"
  hover-stop-propagation="{{true}}"
>
  带点击反馈的视图
</view>

2. <block> - 逻辑容器

不渲染 任何节点的包装器,只用于逻辑控制(条件、循环)。不支持 class、style、id 等样式属性,因为它不会被渲染到DOM中。

html 复制代码
<!-- 条件渲染 -->
<block ="{{hasPermission}}">
  <view>管理员菜单1</view>
  <view>管理员菜单2</view>
</block>

<!-- 列表渲染 -->
<block ="{{userList}}" ="id">
  <view class="user-item">{{item.name}}</view>
  <view class="user-desc">{{item.desc}}</view>
</block>

<!-- 混合使用 -->
<block ="{{showSection}}">
  <block ="{{items}}" ="index">
    <view>{{item}}</view>
  </block>
</block>

3. <scroll-view> - 可滚动视图

实现区域滚动,支持横向、纵向滚动,常用于长列表、横向滑动菜单。

html 复制代码
<!-- 纵向滚动 -->
<scroll-view 
  scroll-y="{{true}}"
  style="height: 500rpx;"
  bindscrolltolower="loadMore"
  lower-threshold="50"
>
  <view ="{{100}}" ="index">列表项 {{index}}</view>
</scroll-view>

<!-- 横向滚动 -->
<scroll-view 
  scroll-x="{{true}}"
  class="horizontal-scroll"
  bindscroll="onScroll"
>
  <view class="scroll-item" ="{{banners}}" ="id">
    <image src="{{item.image}}" mode="widthFix" />
  </view>
</scroll-view>

<!-- 滚动到指定位置 -->
<scroll-view 
  scroll-y="{{true}}"
  scroll-into-view="item-{{currentIndex}}"
  scroll-with-animation="{{true}}"
>
  <view id="item-0">项目0</view>
  <view id="item-1">项目1</view>
  <view id="item-2">项目2</view>
</scroll-view>

常用属性:

属性 类型 说明
scroll-x Boolean 允许横向滚动
scroll-y Boolean 允许纵向滚动
scroll-top Number 设置竖向滚动条位置
scroll-left Number 设置横向滚动条位置
scroll-into-view String 滚动到指定id的元素
scroll-with-animation Boolean 滚动时使用动画
bindscrolltolower EventHandle 滚动到底部/右边触发
lower-threshold Number 距底部多远触发(默认50)
bindscroll EventHandle 滚动时触发

4. <swiper> - 轮播容器

实现触摸滑动轮播效果,常用于首页Banner、卡片轮播。

html 复制代码
<!-- 基础轮播 -->
<swiper 
  indicator-dots="{{true}}"
  autoplay="{{true}}"
  interval="3000"
  duration="500"
  circular="{{true}}"
  bindchange="onSwiperChange"
>
  <swiper-item ="{{banners}}" ="id">
    <image src="{{item.image}}" mode="aspectFill" class="banner-img" />
  </swiper-item>
</swiper>

<!-- 垂直轮播 -->
<swiper 
  vertical="{{true}}"
  indicator-dots="{{true}}"
  autoplay="{{true}}"
  style="height: 200rpx;"
>
  <swiper-item>
    <view class="notice-item">公告1:系统升级通知</view>
  </swiper-item>
  <swiper-item>
    <view class="notice-item">公告2:活动即将开始</view>
  </swiper-item>
</swiper>

<!-- 卡片式轮播(设置间距和边距) -->
<swiper 
  next-margin="30rpx"
  previous-margin="30rpx"
  display-multiple-items="1"
>
  <swiper-item ="{{cards}}" ="id">
    <view class="card">{{item.title}}</view>
  </swiper-item>
</swiper>

常用属性:

属性 类型 说明
indicator-dots Boolean 是否显示面板指示点
autoplay Boolean 是否自动切换
interval Number 自动切换时间间隔(ms)
duration Number 滑动动画时长(ms)
circular Boolean 是否循环播放
vertical Boolean 滑动方向是否为纵向
current Number 当前所在滑块的索引
bindchange EventHandle current改变时触发

5. <movable-view> - 可移动容器

需要配合 <movable-area> 使用,实现可拖拽移动的组件。

html 复制代码
<!-- 基础拖拽 -->
<movable-area style="width: 600rpx; height: 600rpx; background: #f0f0f0;">
  <movable-view 
    style="width: 100rpx; height: 100rpx; background: #07c160;"
    direction="all"
    bindchange="onMovableChange"
  >
    拖拽
  </movable-view>
</movable-area>

<!-- 限制拖拽范围 -->
<movable-area style="width: 100%; height: 400rpx;">
  <movable-view 
    x="{{x}}" 
    y="{{y}}"
    direction="all"
    out-of-bounds="{{false}}"
    bindchange="onChange"
  >
    <view class="drag-item">可拖动</view>
  </movable-view>
</movable-area>

常用属性:

  • direction:移动方向(all/vertical/horizontal/none)

  • x/y:初始位置

  • out-of-bounds:是否超出边界

  • damping:阻尼系数(超出边界后)

6. <cover-view> - 覆盖原生组件

覆盖在原生组件(如 <video>、<map>、<canvas>)之上的视图。

html 复制代码
<!-- 视频上叠加控件 -->
<video src="{{videoUrl}}" controls>
  <cover-view class="video-controls">
    <cover-view class="play-btn" bindtap="togglePlay">
      {{isPlaying ? '暂停' : '播放'}}
    </cover-view>
    <cover-view class="time">{{currentTime}}/{{duration}}</cover-view>
  </cover-view>
</video>

<!-- 地图上叠加信息 -->
<map latitude="{{latitude}}" longitude="{{longitude}}">
  <cover-view class="map-marker">
    <cover-image src="/images/marker.png" />
    <cover-view>当前位置</cover-view>
  </cover-view>
</map>

注意事项:

  • 仅支持部分组件:button、image、text、view

  • 不支持 overflow、position: fixed 等CSS属性

  • 建议使用 cover-image 显示图片

7. <page-container> - 页面级容器

实现弹窗、抽屉、半屏等效果,类似于半透明遮罩层。

html 复制代码
<!-- 底部弹出 -->
<button bindtap="showPopup">显示弹窗</button>

<page-container 
  show="{{showPopup}}"
  position="bottom"
  round="{{true}}"
  bindafterleave="onPopupClose"
>
  <view class="popup-content">
    <view class="popup-header">标题</view>
    <view class="popup-body">弹窗内容</view>
    <button bindtap="closePopup">关闭</button>
  </view>
</page-container>
javascript 复制代码
Page({
  data: {
    showPopup: false
  },
  showPopup() {
    this.setData({ showPopup: true });
  },
  closePopup() {
    this.setData({ showPopup: false });
  }
});

常用属性:

  • show:是否显示

  • position:弹出位置(top/bottom/left/right/center)

  • round:是否显示圆角

  • overlay:是否显示遮罩层

  • close-on-slide-down:是否允许下滑关闭

基础内容组件

基础内容组件包括icon、text和progress,用于在视图界面中展现图标、文本内容及进度条等信息。用于展示最基础UI内容的组件,包括文本、图标、进度条等。它们是构建用户界面的最小单元,几乎所有页面都会用到。

组件名 主要功能 适用场景
<text> 文本展示 普通文本、内联文本、长按复制
<icon> 图标展示 状态图标、提示图标、简单图标
<progress> 进度条 加载进度、下载进度、完成率展示
<rich-text> 富文本渲染 渲染HTML、文章内容、格式化文本
<view> 基础容器 布局容器(已在视图容器中介绍)

1. <text> - 文本组件

最基础的文本展示组件,支持长按复制、嵌套等特性。

属性 类型 说明
selectable Boolean 是否支持长按选中复制(默认false)
space String 显示连续空格(ensp/emsp/nbsp)
decode Boolean 是否解码HTML实体(&nbsp;等)
user-select Boolean 是否支持用户选择(基础库2.12.1+)
html 复制代码
<!-- 长按复制文本 -->
<text selectable="{{true}}" user-select="{{true}}">
  这段文字可以被长按复制
</text>

<!-- 显示连续空格 -->
<text space="ensp">文  本(中文空格)</text>
<text space="emsp">文  本(em空格)</text>
<text space="nbsp">文  本(不断行空格)</text>

<!-- 解码HTML实体 -->
<text decode="{{true}}">&lt; &gt; &amp; &nbsp; &ensp; &emsp;</text>
<!-- 输出: < > &      -->

文本嵌套

html 复制代码
<!-- text支持嵌套,内层会继承外层样式 -->
<text class="outer">
  外层文本
  <text class="inner" selectable>内层可复制文本</text>
  继续外层文本
</text>

<!-- 实现文本截断效果 -->
<text class="ellipsis">
  很长很长很长很长很长很长很长很长的文本会被截断
</text>


/* 文本截断样式 */
.ellipsis {
  display: block;
  width: 300rpx;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

2. <icon> - 图标组件

提供内置的图标库,无需图片资源即可使用常见图标。

可用图标类型

type值 说明 默认颜色
success 成功图标(圆形打勾) #07c160
success_no_circle 成功图标(无圆形) #07c160
info 信息图标(圆形i) #1989fa
warn 警告图标(三角感叹号) #fa2c19
waiting 等待图标(圆形时钟) #ff976a
cancel 取消图标(圆形叉) #999
download 下载图标 #666
search 搜索图标(放大镜) #666
clear 清除图标(叉) #ccc

属性说明

属性 类型 默认值 说明
type String - 图标类型(必填)
size Number/String 23 图标大小(单位px)
color String 各类型默认色 图标颜色
html 复制代码
<!-- 自定义大小和颜色 -->
<icon type="success" size="32" color="#ff6600" />
<icon type="info" size="20" color="#00b26a" />

<!-- 结合文本使用 -->
<view class="status-item">
  <icon type="success" size="16" />
  <text>操作成功</text>
</view>

3. <progress> - 进度条组件

展示操作进度,支持百分比显示、动画、圆角等。

属性 类型 默认值 说明
percent Number 0 进度百分比(0-100)
show-info Boolean false 是否显示百分比文字
stroke-width Number 6 进度条高度(单位px)
color String #09BB07 进度条颜色
backgroundColor String #EBEBEB 背景条颜色
active Boolean false 是否从左到右动画
active-mode String backwards 动画模式(backwards/forwards)
border-radius Number 0 圆角大小(单位px)
duration Number 30 动画持续时间(ms)
html 复制代码
<!-- 简单进度条 -->
<progress percent="30" />

<!-- 带文字百分比 -->
<progress percent="50" show-info stroke-width="8" />

<!-- 自定义颜色 -->
<progress 
  percent="75" 
  color="#1989fa" 
  backgroundColor="#e5e5e5" 
  stroke-width="10"
  border-radius="5"
/>

<!-- 带动画效果 -->
<progress 
  percent="{{progressPercent}}" 
  active="{{true}}" 
  active-mode="forwards"
  duration="1000"
  show-info
/>
javascript 复制代码
Page({
  data: {
    progressPercent: 0
  },
  
  onLoad() {
    // 模拟进度更新
    let progress = 0;
    const timer = setInterval(() => {
      progress += 10;
      this.setData({ progressPercent: progress });
      
      if (progress >= 100) {
        clearInterval(timer);
      }
    }, 500);
  }
});

4. <rich-text> - 富文本组件

用于渲染HTML字符串,支持常见的HTML标签和样式。

html 复制代码
<!-- 基础HTML渲染 -->
<rich-text nodes="<h1>标题</h1><p>这是一个段落</p>" />

<!-- 通过数据绑定 -->
<rich-text nodes="{{htmlContent}}" />
javascript 复制代码
Page({
  data: {
    htmlContent: `
      <div style="padding: 20rpx;">
        <h2 style="color: #333;">文章标题</h2>
        <p style="font-size: 28rpx; line-height: 1.6;">
          这是一段<span style="color: red;">高亮文字</span>。
        </p>
        <img src="https://example.com/image.jpg" style="width: 100%;" />
        <ul>
          <li>列表项1</li>
          <li>列表项2</li>
        </ul>
      </div>
    `
  }
});

nodes 支持两种格式:字符串 和 数组。

表单组件

用于收集用户输入信息的重要组件,涵盖文本输入、选择器、开关、按钮等交互元素,是构建用户交互界面的核心。

组件名 主要功能 适用场景
<button> 按钮 表单提交、页面跳转、授权触发
<input> 输入框 文本、数字、密码、身份证输入
<textarea> 多行输入框 评论、反馈、文章编辑
<checkbox> 多选框 多项选择、协议确认
<radio> 单选框 性别选择、选项切换
<switch> 开关 状态切换、设置项
<slider> 滑动选择器 音量、亮度、数值范围选择
<picker> 选择器 日期、时间、地区、自定义选择
<picker-view> 嵌入式选择器 三级联动、自定义滚动选择
<form> 表单容器 批量收集表单数据

1. <button> - 按钮组件

最核心的交互组件,支持多种类型、尺寸和状态。

属性 类型 默认值 说明
type String default 按钮类型(default/primary/warn)
size String default 按钮大小(default/mini)
plain Boolean false 是否镂空
disabled Boolean false 是否禁用
loading Boolean false 是否显示加载图标
form-type String - 表单类型(submit/reset)
open-type String - 开放能力(contact/share/getPhoneNumber等)
hover-class String button-hover 点击态样式
hover-stop-propagation Boolean false 阻止点击态冒泡
hover-start-time Number 20 点击后多久出现点击态
hover-stay-time Number 70 点击态保持时长

开放能力(open-type)

html 复制代码
<!-- 获取用户信息 -->
<button open-type="getUserInfo" bindgetuserinfo="onGetUserInfo">
  获取用户信息
</button>

<!-- 获取手机号 -->
<button open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">
  获取手机号
</button>

<!-- 分享 -->
<button open-type="share">分享给朋友</button>

<!-- 客服 -->
<button open-type="contact" bindcontact="onContact">
  联系客服
</button>

<!-- 打开APP -->
<button open-type="launchApp">打开APP</button>

<!-- 反馈 -->
<button open-type="feedback">意见反馈</button>
javascript 复制代码
Page({
  onGetUserInfo(e) {
    console.log(e.detail.userInfo);
  },
  
  onGetPhoneNumber(e) {
    console.log(e.detail.code);
    console.log(e.detail.errMsg);
  }
});

2. <input> - 输入框

最常用的文本输入组件,支持多种输入类型。

属性 类型 说明
value String 输入框内容
type String 类型(text/number/idcard/digit)
password Boolean 是否密码类型
placeholder String 占位符
placeholder-style String 占位符样式
placeholder-class String 占位符样式类
disabled Boolean 是否禁用
maxlength Number 最大长度(0不限制)
cursor-spacing Number 光标与键盘距离
focus Boolean 自动获取焦点
confirm-type String 键盘右下角按钮类型
confirm-hold Boolean 点击确认按钮是否保持键盘
adjust-position Boolean 键盘弹起时是否自动上推页面
hold-keyboard Boolean 聚焦时点击页面不收起键盘
auto-fill String 自动填充内容

3. <textarea> - 多行输入框

用于输入多行文本,如评论、反馈等场景。

html 复制代码
<textarea 
  placeholder="请输入评论内容"
  maxlength="200"
  auto-height="{{true}}"
  show-confirm-bar="{{false}}"
  bindinput="onTextareaInput"
/>

<!-- 固定高度文本域 -->
<textarea 
  style="height: 200rpx;"
  maxlength="500"
  placeholder-style="color: #999;"
/>

常用属性:

  • auto-height:是否自动增高

  • show-confirm-bar:是否显示键盘上方完成栏

  • disable-default-padding:是否移除默认内边距

4. <checkbox> - 多选框

用于多选场景,需配合 <checkbox-group> 使用。

html 复制代码
<!-- 单个多选框 -->
<checkbox-group bindchange="onCheckboxChange">
  <label>
    <checkbox value="agree" checked="{{isAgreed}}"/>
    同意用户协议
  </label>
</checkbox-group>

<!-- 多选组 -->
<checkbox-group bindchange="onHobbyChange">
  <label ="{{hobbies}}" ="value">
    <checkbox value="{{item.value}}" checked="{{item.checked}}"/>
    {{item.name}}
  </label>
</checkbox-group>
javascript 复制代码
Page({
  data: {
    hobbies: [
      { name: '阅读', value: 'read', checked: false },
      { name: '音乐', value: 'music', checked: true },
      { name: '运动', value: 'sport', checked: false }
    ]
  },
  
  onHobbyChange(e) {
    console.log('选中的值:', e.detail.value);
    // e.detail.value: ['music', 'read']
  }
});

5. <radio> - 单选框

用于单选场景,需配合 <radio-group> 使用。

html 复制代码
<radio-group bindchange="onGenderChange">
  <label>
    <radio value="male" checked="{{gender === 'male'}}"/>
    男
  </label>
  <label>
    <radio value="female" checked="{{gender === 'female'}}"/>
    女
  </label>
</radio-group>
javascript 复制代码
Page({
  data: {
    gender: 'male'
  },
  
  onGenderChange(e) {
    this.setData({
      gender: e.detail.value
    });
  }
});

6. <switch> - 开关

用于切换状态,类似设置项。

html 复制代码
<switch checked="{{isEnabled}}" bindchange="onSwitchChange" />

<!-- 自定义颜色 -->
<switch 
  checked="{{isVibrate}}"
  color="#07c160"
  type="switch"
  bindchange="onVibrateChange"
/>

<!-- 复选框样式 -->
<switch type="checkbox" checked="{{isSelected}}" />
javascript 复制代码
Page({
  data: {
    isEnabled: true,
    isVibrate: false
  },
  
  onSwitchChange(e) {
    this.setData({
      isEnabled: e.detail.value
    });
  }
});

7. <slider> - 滑动选择器

通过滑动选择数值,适合音量、亮度调节。

html 复制代码
<!-- 基础滑块 -->
<slider value="{{50}}" bindchange="onSliderChange" />

<!-- 带边界值 -->
<slider 
  min="0" 
  max="100" 
  step="1"
  value="{{volume}}"
  show-value
  bindchanging="onVolumeChanging"
  bindchange="onVolumeChange"
/>

<!-- 自定义样式 -->
<slider 
  block-size="20"
  block-color="#07c160"
  active-color="#07c160"
  backgroundColor="#e5e5e5"
/>
javascript 复制代码
Page({
  data: {
    volume: 50
  },
  
  onVolumeChanging(e) {
    // 拖动过程中实时触发
    console.log('当前值:', e.detail.value);
  },
  
  onVolumeChange(e) {
    // 拖动结束触发
    this.setData({
      volume: e.detail.value
    });
  }
});

8. <picker> - 选择器

从底部弹起的滚动选择器,支持多种模式

html 复制代码
普通选择器
<picker 
  mode="selector"
  range="{{selectorItems}}"
  value="{{selectorIndex}}"
  bindchange="onSelectorChange"
>
  <view>当前选择:{{selectorItems[selectorIndex]}}</view>
</picker>


多列选择器
<picker 
  mode="multiSelector"
  range="{{multiArray}}"
  value="{{multiIndex}}"
  bindchange="onMultiChange"
  bindcolumnchange="onColumnChange"
>
  <view>当前选择:{{multiArray[0][multiIndex[0]]}} - {{multiArray[1][multiIndex[1]]}}</view>
</picker>


时间选择器
<picker 
  mode="time"
  value="{{time}}"
  start="09:00"
  end="18:00"
  bindchange="onTimeChange"
>
  <view>当前时间:{{time}}</view>
</picker>

<picker 
  mode="date"
  value="{{date}}"
  start="2020-01-01"
  end="2030-12-31"
  bindchange="onDateChange"
>
  <view>当前日期:{{date}}</view>
</picker>


地区选择器
<picker 
  mode="region"
  value="{{region}}"
  bindchange="onRegionChange"
>
  <view>当前地区:{{region[0]}} {{region[1]}} {{region[2]}}</view>
</picker>
javascript 复制代码
Page({
  data: {
    selectorItems: ['选项1', '选项2', '选项3'],
    selectorIndex: 0,
    time: '12:00',
    date: '2024-01-15',
    region: ['广东省', '广州市', '海珠区']
  },
  
  onSelectorChange(e) {
    this.setData({
      selectorIndex: e.detail.value
    });
  },
  
  onTimeChange(e) {
    this.setData({
      time: e.detail.value
    });
  },
  
  onDateChange(e) {
    this.setData({
      date: e.detail.value
    });
  },
  
  onRegionChange(e) {
    this.setData({
      region: e.detail.value
    });
  }
});

9. <form> - 表单容器

用于统一收集表单组件的值,简化数据获取。

html 复制代码
<form bindsubmit="onFormSubmit" bindreset="onFormReset">
  <input name="username" placeholder="用户名" />
  <input name="password" password placeholder="密码" />
  
  <radio-group name="gender">
    <radio value="male">男</radio>
    <radio value="female">女</radio>
  </radio-group>
  
  <switch name="agree" value="{{true}}" />
  
  <button form-type="submit">提交</button>
  <button form-type="reset">重置</button>
</form>
javascript 复制代码
Page({
  onFormSubmit(e) {
    const formData = e.detail.value;
    console.log(formData);
    // { username: '张三', password: '123', gender: 'male', agree: true }
    
    // 提交到服务器
    wx.request({
      url: 'https://api.example.com/submit',
      data: formData,
      success: (res) => {
        wx.showToast({ title: '提交成功' });
      }
    });
  },
  
  onFormReset(e) {
    console.log('表单已重置');
  }
});

互动操作组件

用于增强用户与应用之间的交互体验,涵盖手势识别、弹窗菜单、消息提示等场景。

组件/API 类型 主要功能 适用场景
手势组件 Skyline组件 拖拽、缩放、长按等手势识别 半屏弹窗、图片缩放、拖拽排序
<button> 表单组件 触发交互、开放能力 分享、获取手机号、客服
ActionSheet 扩展组件/API 底部弹出操作菜单 多选项选择、操作面板
Modal API 模态对话框 重要确认、信息输入
Toast/Loading API 轻提示/加载提示 操作反馈、加载状态

互动组件选型建议

场景 推荐方案
半屏弹窗+拖拽关闭 Skyline手势组件
图片缩放/双指操作 <scale-gesture-handler>
底部操作菜单 wx.showActionSheet
重要确认弹窗 wx.showModal
简单操作反馈 wx.showToast
异步加载状态 wx.showLoading
微信分享/获取手机号 Button open-type

1. 手势组件(Skyline渲染引擎)

手势组件是Skyline渲染引擎内置的虚组件,直接在UI线程响应,避免了传统JS线程通信的延迟,性能更好。

组件名称 触发时机
<tap-gesture-handler> 点击时触发
<double-tap-gesture-handler> 双击时触发
<long-press-gesture-handler> 长按时触发
<pan-gesture-handler> 拖拽移动时触发
<vertical-drag-gesture-handler> 纵向滑动时触发
<horizontal-drag-gesture-handler> 横向滑动时触发
<scale-gesture-handler> 多指缩放时触发
<force-press-gesture-handler> iPhone重按时触发
html 复制代码
<!-- 拖拽手势 -->
<pan-gesture-handler worklet:ongesture="handlePan">
  <view class="drag-box">拖拽我</view>
</pan-gesture-handler>

<!-- 纵向拖拽(与scroll-view协同) -->
<vertical-drag-gesture-handler 
  worklet:ongesture="handleVerticalDrag"
  native-view="scroll-view"
>
  <scroll-view scroll-y style="height: 500rpx;">
    <view ="{{100}}" ="index">列表项 {{index}}</view>
  </scroll-view>
</vertical-drag-gesture-handler>

手势回调中的state字段表示当前手势状态:

状态值 常量名 说明
0 POSSIBLE 手势未识别
1 BEGIN 手势已识别
2 ACTIVE 连续手势活跃中
3 END 手势正常结束
4 CANCELLED 手势被取消

2. 操作菜单(ActionSheet)

底部弹出的操作菜单,用于提供多个操作选项。

javascript 复制代码
Page({
  showActionSheet() {
    wx.showActionSheet({
      alertText: "请选择操作",      // 提示文案
      itemList: ["编辑", "删除", "分享"],
      itemColor: "#000000",
      success: (res) => {
        console.log("点击了第" + res.tapIndex + "个按钮");
        if (res.tapIndex === 0) {
          // 编辑操作
        } else if (res.tapIndex === 1) {
          // 删除操作
        }
      },
      fail: (error) => {
        console.log("调用失败", error);
      }
    });
  }
});

3. 模态对话框(Modal)

用于需要用户确认的重要操作。

属性 类型 说明
title String 对话框标题
content String 对话框内容
showCancel Boolean 是否显示取消按钮
cancelText String 取消按钮文字
confirmText String 确认按钮文字
editable Boolean 是否显示输入框
placeholderText String 输入框提示文字
javascript 复制代码
Page({
  showConfirm() {
    wx.showModal({
      title: "温馨提示",
      content: "是否确认删除该条记录?",
      showCancel: true,
      cancelText: "取消",
      cancelColor: "#999",
      confirmText: "确认",
      confirmColor: "#fa2c19",
      success: (res) => {
        if (res.confirm) {
          // 用户点击确认
          console.log("确认删除");
        } else if (res.cancel) {
          // 用户点击取消
          console.log("取消删除");
        }
      }
    });
  },
  
  // 带输入框的模态框(基础库2.17.1+)
  showPrompt() {
    wx.showModal({
      title: "输入昵称",
      editable: true,
      placeholderText: "请输入新昵称",
      success: (res) => {
        if (res.confirm && res.content) {
          console.log("输入内容:", res.content);
        }
      }
    });
  }
});

4. 轻提示(Toast)

自动消失的提示信息,适合操作结果反馈。

javascript 复制代码
Page({
  showSuccess() {
    wx.showToast({
      title: "保存成功",
      icon: "success",   // success/error/loading/none
      duration: 2000,
      mask: true        // 防止背景被点击
    });
  },
  
  showError() {
    wx.showToast({
      title: "操作失败",
      icon: "error",
      duration: 2000
    });
  },
  
  // 手动隐藏
  hideToast() {
    wx.hideToast();
  }
});

5. 加载提示(Loading)

需要手动关闭的加载提示,适合异步操作。

javascript 复制代码
Page({
  async loadData() {
    // 显示加载中
    wx.showLoading({
      title: "加载中...",
      mask: true
    });
    
    try {
      // 模拟异步请求
      const res = await this.fetchData();
      // 处理数据...
    } catch (error) {
      wx.showToast({ title: "加载失败", icon: "error" });
    } finally {
      // 手动关闭loading
      wx.hideLoading();
    }
  }
});

6. Button开放能力

Button组件的open-type属性可调用微信开放能力

html 复制代码
<!-- 获取用户信息 -->
<button open-type="getUserInfo" bindgetuserinfo="onGetUserInfo">
  获取用户信息
</button>

<!-- 获取手机号(需后端配合) -->
<button open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">
  获取手机号
</button>

<!-- 分享给朋友 -->
<button open-type="share">分享</button>

<!-- 打开客服会话 -->
<button open-type="contact" bindcontact="onContact">联系客服</button>

<!-- 打开意见反馈 -->
<button open-type="feedback">意见反馈</button>

<!-- 打开APP(需APP关联) -->
<button open-type="launchApp" app-parameter="key=value">打开APP</button>
javascript 复制代码
Page({
  onGetUserInfo(e) {
    console.log(e.detail.userInfo);
  },
  
  onGetPhoneNumber(e) {
    // 新版接口返回code,需后端换取手机号
    console.log(e.detail.code);
  },
  
  onShareAppMessage() {
    return {
      title: '分享标题',
      path: '/pages/index/index'
    };
  }
});

页面导航组件

核心指的就是 <navigator> 组件。它主要负责在页面之间构建链接,实现用户点击后的页面跳转。简单来说,它类似于 HTML 中的 <a> 标签,但功能更丰富,能适应小程序中不同类型页面的跳转规则

1. 关键属性速查

属性名 类型 说明 最低版本
url String 当前小程序内的跳转链接,路径后可以带参数-1-5。 1.0.0
open-type String 跳转方式,这是最关键的属性,决定了跳转时的页面栈行为-5。 1.0.0
delta Number 当 open-type="navigateBack" 时有效,表示回退的页面层数,默认为 1-5。 1.0.0
hover-class String 指定点击时的样式类,默认值为 navigator-hover。可以设置为 none 来去掉点击态效果-5。 1.0.0

2. open-type 跳转方式详解

这是 <navigator> 的核心,它决定了跳转时页面栈的变化,具体有5种模式-5-6。

open-type 值 功能说明 对应API 页面栈表现
navigate 保留当前页面,跳转到应用内的某个页面。这是默认值 -5。 wx.navigateTo 新页面入栈,页面栈加1。
redirect 关闭当前页面,跳转到应用内的某个页面。 wx.redirectTo 当前页面出栈,新页面入栈,页面栈大小不变。
switchTab 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。 wx.switchTab 清除非 tabBar 页面,切换到指定的 tabBar 页面。
reLaunch 关闭所有页面,打开到应用内的某个页面。 wx.reLaunch 清空所有页面,新页面入栈。
navigateBack 关闭当前页面,返回上一页面或多级页面。 wx.navigateBack 页面出栈,直到回到目标页面。

3. 使用注意事项

  1. 路径写法 :url 属性中的路径建议使用以 / 开头的绝对路径,这样最不容易出错。

  2. 页面栈限制 :小程序页面栈最多支持 10 层。使用 navigate 跳转时需注意,避免页面层级过深导致无法再打开新页面。

  3. 样式定制 :<navigator> 组件自带点击态效果(灰色背景)。你可以通过修改 .navigator-hover 这个类名来全局自定义样式,或者给某个导航单独设置 hover-class 属性

<navigator> 组件是微信小程序页面跳转的基础,掌握它对于构建流畅的用户交互至关重要。

  • 判断目标页面类型 :在写代码前,先问自己"要跳转的页面是不是 Tab 页?"。如果是,open-type 必须用 switchTab;如果不是,大多数情况用默认的 navigate 即可。

  • 传递简单参数 :直接在 url 后用 ? 拼接,格式为 ?key1=value1&key2=value2。参数可以在目标页面的 onLoad(options) 中通过 options 参数接收-1-9。

  • 后退操作 :小程序自带返回箭头,如需自定义返回按钮,使用 open-type="navigateBack"。

除了声明式的 <navigator> 组件,小程序也支持通过 wx.navigateTo、wx.redirectTo 等 API 进行编程式导航。

媒体组件

用于在应用中展示图片、音视频、地图等富媒体内容

组件名 主要功能 适用场景
<image> 图片展示 头像、商品图、轮播图、背景图
<video> 视频播放 短视频、课程视频、直播回放
<camera> 相机组件 拍照、扫码、人脸识别
<live-player> 直播播放 实时直播、体育赛事
<live-pusher> 直播推流 主播开播、视频会议
<audio> 音频播放(旧版) 背景音乐、语音播放
<map> 地图组件 位置展示、导航、轨迹记录

1. <image> - 图片组件

最常用的媒体组件,支持多种图片模式和懒加载。

属性 类型 默认值 说明
src String - 图片资源地址
mode String scaleToFill 图片裁剪、缩放的模式
lazy-load Boolean false 是否懒加载
show-menu-by-longpress Boolean false 长按是否显示菜单
webp Boolean false 是否支持WebP格式
binderror EventHandle - 图片加载失败触发
bindload EventHandle - 图片加载完成触发

mode 模式详解

mode 值 说明 适用场景
scaleToFill 拉伸填充,不保持比例 图标、背景图
aspectFit 保持比例,完整显示 商品图、相册
aspectFill 保持比例,裁剪溢出 头像、封面图
widthFix 宽度固定,高度自适应 文章配图、详情图
heightFix 高度固定,宽度自适应 横向滚动图
top/center/bottom 裁剪模式 特定位置对齐
html 复制代码
<!-- 头像:保持比例,居中裁剪 -->
<image class="avatar" mode="aspectFill" src="{{avatar}}" />

<!-- 商品图:完整显示,可能留白 -->
<image class="product" mode="aspectFit" src="{{productImage}}" />

<!-- 文章图:宽度固定,高度自适应 -->
<image mode="widthFix" src="{{articleImage}}" />
javascript 复制代码
.avatar {
  width: 80rpx;
  height: 80rpx;
  border-radius: 50%;
}

.product {
  width: 100%;
  height: 300rpx;
  background-color: #f5f5f5;
}

2. <video> - 视频组件

功能强大的视频播放器,支持多种格式和播放控制。

属性 类型 默认值 说明
src String - 视频地址
controls Boolean true 显示默认播放控件
autoplay Boolean false 自动播放
loop Boolean false 循环播放
muted Boolean false 静音播放
initial-time Number 0 初始播放位置(秒)
duration Number - 视频时长(秒)
poster String - 视频封面图
object-fit String contain 填充模式(contain/fill/cover)
show-center-play-btn Boolean true 显示中央播放按钮
enable-progress-gesture Boolean true 开启手势进度调节
show-fullscreen-btn Boolean true 显示全屏按钮
show-play-btn Boolean true 显示播放按钮

3. <camera> - 相机组件

用于调用设备摄像头,实现拍照、扫码等功能。

属性 类型 默认值 说明
device-position String back 前置/后置摄像头(front/back)
flash String off 闪光灯状态(auto/on/off/torch)
frame-size String medium 帧数据尺寸(small/medium/large)
scan-area Array - 扫码识别区域
binderror EventHandle - 用户不允许授权或错误
html 复制代码
<camera 
  id="myCamera"
  device-position="{{devicePosition}}"
  flash="{{flashMode}}"
  binderror="onError"
  style="width: 100%; height: 600rpx;"
/>

<view class="camera-controls">
  <button bindtap="toggleCamera">切换摄像头</button>
  <button bindtap="toggleFlash">闪光灯: {{flashMode}}</button>
  <button bindtap="takePhoto">拍照</button>
  <button bindtap="scanCode">扫码</button>
</view>

<image ="{{photoPath}}" src="{{photoPath}}" mode="widthFix" />
javascript 复制代码
Page({
  data: {
    devicePosition: 'back',
    flashMode: 'off',
    photoPath: ''
  },
  
  onReady() {
    this.cameraContext = wx.createCameraContext();
  },
  
  toggleCamera() {
    this.setData({
      devicePosition: this.data.devicePosition === 'back' ? 'front' : 'back'
    });
  },
  
  toggleFlash() {
    const modes = ['off', 'on', 'torch'];
    const currentIndex = modes.indexOf(this.data.flashMode);
    const nextMode = modes[(currentIndex + 1) % modes.length];
    this.setData({ flashMode: nextMode });
  },
  
  takePhoto() {
    this.cameraContext.takePhoto({
      quality: 'high',
      success: (res) => {
        this.setData({ photoPath: res.tempImagePath });
        wx.saveImageToPhotosAlbum({
          filePath: res.tempImagePath,
          success: () => {
            wx.showToast({ title: '保存成功' });
          }
        });
      }
    });
  },
  
  scanCode() {
    this.cameraContext.startScanCode({
      success: (res) => {
        console.log('扫码结果:', res.result);
        wx.showModal({
          title: '扫描结果',
          content: res.result
        });
      }
    });
  }
});

4. <live-player> - 直播播放组件

用于播放实时直播流,支持RTMP、HLS等协议。

使用 live-player 需要申请相应的类目权限。

html 复制代码
<live-player 
  src="{{liveUrl}}"
  mode="live"
  autoplay="{{true}}"
  muted="{{false}}"
  bindstatechange="onStateChange"
  binderror="onError"
  style="width: 100%; height: 400rpx;"
/>
javascript 复制代码
Page({
  data: {
    liveUrl: 'rtmp://example.com/live/stream'
  },
  
  onStateChange(e) {
    console.log('播放状态:', e.detail.code);
    // 状态码: 2001-2004 开始播放/播放中/暂停/停止
  },
  
  onError(e) {
    console.error('播放错误:', e.detail);
  }
});

地图组件

展示地图、标记点、路线等功能。

属性 类型 说明
latitude Number 中心纬度
longitude Number 中心经度
scale Number 缩放级别(3-20)
markers Array 标记点数组
polyline Array 路线数组
circles Array 圆数组
controls Array 控件数组
show-location Boolean 显示带有方向的当前定位点
enable-zoom Boolean 是否支持缩放
enable-scroll Boolean 是否支持拖动
html 复制代码
<map 
  id="myMap"
  latitude="{{latitude}}"
  longitude="{{longitude}}"
  scale="{{scale}}"
  markers="{{markers}}"
  polyline="{{polyline}}"
  show-location
  bindmarkertap="onMarkerTap"
  bindregionchange="onRegionChange"
/>

<view class="map-controls">
  <button bindtap="getUserLocation">定位当前位置</button>
  <button bindtap="addMarker">添加标记</button>
</view>
javascript 复制代码
Page({
  data: {
    latitude: 39.9042,   // 北京天安门
    longitude: 116.4074,
    scale: 14,
    markers: [{
      id: 1,
      latitude: 39.9042,
      longitude: 116.4074,
      title: '天安门',
      iconPath: '/images/marker.png',
      width: 30,
      height: 30,
      callout: {
        content: '天安门广场',
        color: '#333',
        fontSize: 14
      }
    }],
    polyline: [{
      points: [
        { latitude: 39.9042, longitude: 116.4074 },
        { latitude: 39.9142, longitude: 116.4174 }
      ],
      color: '#07c160',
      width: 4,
      dottedLine: false
    }]
  },
  
  onReady() {
    this.mapContext = wx.createMapContext('myMap');
  },
  
  getUserLocation() {
    wx.getLocation({
      type: 'gcj02',
      success: (res) => {
        this.setData({
          latitude: res.latitude,
          longitude: res.longitude
        });
        this.mapContext.moveToLocation();
      }
    });
  },
  
  addMarker() {
    const newMarker = {
      id: Date.now(),
      latitude: this.data.latitude + 0.001,
      longitude: this.data.longitude + 0.001,
      title: '新标记',
      iconPath: '/images/marker.png'
    };
    this.setData({
      markers: [...this.data.markers, newMarker]
    });
  },
  
  onMarkerTap(e) {
    const markerId = e.detail.markerId;
    wx.showModal({
      title: '标记点',
      content: `点击了标记点ID: ${markerId}`
    });
  },
  
  onRegionChange(e) {
    if (e.type === 'end') {
      console.log('地图移动结束');
    }
  }
});

地图性能

  • 标记点过多时使用聚合

  • 及时调用 moveToLocation 定位

  • 避免频繁更新 markers

画布组件

用于自由绘制图形的矩形区域,它提供了强大的绘图能力,可以满足从基础图形绘制到复杂动画、图像处理等多种场景需求

与其他大部分组件不同,<canvas> 组件本身只是一个空白的画布容器,具体的绘制工作需要通过调用相应的 API 来完成

  1. 两种版本,从 "旧" 到 "新"

微信小程序的 Canvas 主要经历了两个版本的演进,强烈建议新项目直接使用新版 2D Canvas。

  • 旧版 Canvas(已过时):

    • 通过 canvas-id 来标识,使用 wx.createCanvasContext(canvasId) 获取绘图上下文。

    • 所有绘图操作都是异步的,最后需要调用 .draw() 方法才会真正将内容渲染到画布上,逻辑较复杂,性能也相对较差。

  • 新版 Canvas 2D(推荐):

    • 基础库 2.7.0 开始支持,通过 type="2d" 来声明。

    • 它的 API 接口与 Web 标准对齐,通过 Canvas.getContext('2d') 获取的上下文与 Web 开发一致,绘图操作是同步的,性能更好,开发体验也更流畅。

2. 关键属性速查

在新版 Canvas 2D 中,常用属性如下:

属性名 类型 说明
type String 必须 。定义画布类型,2D 绘图应设为 "2d"
id String 必须。组件的唯一标识,用于后续获取 Canvas 实例
disable-scroll Boolean 当在 canvas 中移动且有绑定手势事件时,禁止屏幕滚动
bindtouchstart EventHandle 手指触摸动作开始
bindtouchmove EventHandle 手指触摸后移动
bindtouchend EventHandle 手指触摸动作结束
html 复制代码
<!-- canvasDemo.wxml -->
<canvas id="myCanvas" type="2d" style="width: 300px; height: 150px; border: 1px solid #ccc;"></canvas>

在页面的 onReady 生命周期中,获取节点和渲染上下文,然后进行绘制。

javascript 复制代码
// canvasDemo.js
Page({
  onReady() {
    // 1. 通过 SelectorQuery 获取 Canvas 节点
    const query = wx.createSelectorQuery();
    query.select('#myCanvas')
      .fields({ node: true, size: true })
      .exec((res) => {
        // 2. 获取 Canvas 对象和 2D 渲染上下文
        const canvas = res[0].node;
        const ctx = canvas.getContext('2d');
        
        // 3. 处理设备像素比(DPI),防止图片在高分屏上模糊
        const dpr = wx.getSystemInfoSync().pixelRatio;
        canvas.width = res[0].width * dpr;
        canvas.height = res[0].height * dpr;
        ctx.scale(dpr, dpr);
        
        // 4. 开始绘图:绘制一个红色正方形
        ctx.fillStyle = 'red';
        ctx.fillRect(10, 10, 50, 50);
      });
  }
});

代码解析:

  • wx.createSelectorQuery():用于查询页面节点。

  • canvas.getContext('2d'):获取 2D 绘图上下文,这是所有绘图操作的基础。

  • canvas.width / canvas.height:设置画布的实际像素尺寸。这与 CSS 定义的渲染尺寸(300*150px)不同,乘以 dpr 是为了保证绘图在高清屏上不会模糊。

  • ctx.fillRect(x, y, width, height):绘制一个实心矩形。

Canvas 可以用来实现很多复杂的功能:

  • 图表绘制 :利用路径(beginPath, lineTo)和贝塞尔曲线(bezierCurveTo)等方法,可以绘制各种统计图表,如平滑的折线图。

  • 图像处理:在 canvas 上加载并绘制图片,并为其添加文字或图形水印,保护图片版权。

  • 动画与游戏 :通过 canvas.requestAnimationFrame 方法可以创建流畅的逐帧动画,是实现小程序游戏的基础。

使用 wxml2canvas-2d 生成海报

在实际开发中,我们经常遇到将一段复杂的UI(如包含图片、文字、头像的卡片)一键生成海报并保存或分享的需求。手动用 Canvas API 去计算每个元素的位置非常繁琐。

这时,就可以使用像 wxml2canvas-2d 这样的第三方库,它允许你像写普通页面一样,通过 WXML 和 CSS 来描述海报的样式,然后由它自动帮你绘制到 Canvas 上,极大地提高了开发效。

基本使用思路如下:

  • 安装和引入 :通过 npm 安装 wxml2canvas-2d 并在页面的 usingComponents 中注册。

  • 编写 "海报模板":在 WXML 中,用一个特定类名的容器包裹起你想生成海报的内容,并像平时写样式一样,用 WXSS 定义好每个元素的样式。

  • 调用方法生成图片 :在 JS 中,通过 selectComponent 获取组件实例,调用其 draw() 和 toTempFilePath() 方法,就能得到一张海报图片。

相关推荐
FX1317887KP_3 小时前
从KET到PET口语进阶:词汇量翻倍后,口语如何跟上?
小程序·ket口语·pet口语·fce备考·剑桥英语
m0_587383004 小时前
工业场景设备维修维护实战技巧 全流程标准化落地与常见问题排查指南
java·spring·小程序·架构·需求分析
m0_587383001 天前
深圳 24 小时自助健身房系统软件开发实战指南与案例解析
java·spring boot·小程序·架构·需求分析
2501_915909061 天前
iOS应用从开发到上架App Store的完整发布流程与步骤指南
android·ios·小程序·https·uni-app·webview
扶风ff1 天前
练题簿在线免费刷题:会员线下组卷、Word 试卷与成绩导入,课堂检测更方便
java·开发语言·算法·小程序·word
镭封1 天前
做电视剧解说视频用什么软件?配音、文案处理一次解决
人工智能·小程序·音视频·语音识别·媒体
闹小艾1 天前
制作题库练题小程序如何选择搭建平台?3大主流平台优缺点,新手选型不踩坑
小程序
小码哥0681 天前
陪诊小程序系统开发:开启医疗陪护新时代
小程序·陪诊·陪护·陪诊陪护·医院陪诊系统·陪诊系统·陪诊管理
扶风ff1 天前
练题簿在线免费刷题:创建个人任务,把刷题、听题和模考目标放到每天的学习中
开发语言·javascript·学习·小程序