uni-app 入门学习教程,从入门到精通,uni-app组件 —— 知识点详解与实战案例(4)

📚 uni-app组件 ------ 知识点详解与实战案例


✅ 本章学习目标

  1. 掌握 uni-app 常用容器组件:scroll-viewswiper
  2. 熟悉基础组件:rich-textprogress
  3. 掌握表单组件:buttoninputpickersliderradiocheckboxswitchtextareaform
  4. 了解媒体组件:cameravideomap
  5. 能独立构建典型页面:注册页、个人中心页
  6. 理解组件通信、数据绑定、事件处理机制

🧱 一、容器组件


1. scroll-view 滚动视图

可滚动的视图容器,支持横向/纵向滚动、下拉刷新、上拉加载等。

🔧 语法结构:

html 复制代码
<scroll-view 
  scroll-y="true"     <!-- 纵向滚动 -->
  scroll-x="false"    <!-- 横向滚动 -->
  scroll-top="0"      <!-- 滚动顶部距离 -->
  scroll-left="0"     <!-- 滚动左侧距离 -->
  @scroll="handleScroll"   <!-- 滚动事件 -->
  @scrolltoupper="onUpper" <!-- 滚动到顶部 -->
  @scrolltolower="onLower" <!-- 滚动到底部 -->
>
  <!-- 内容 -->
</scroll-view>

📌 属性说明:

属性名 类型 默认值 说明
scroll-y Boolean false 是否允许纵向滚动
scroll-x Boolean false 是否允许横向滚动
scroll-top Number 0 设置竖向滚动条位置
scroll-left Number 0 设置横向滚动条位置
enable-back-to-top Boolean false 是否支持点击顶部回到顶部

💡 案例:纵向滚动列表 + 上拉加载更多

vue 复制代码
<template>
  <view class="container">
    <scroll-view 
      scroll-y="true" 
      @scrolltolower="loadMore"
      :scroll-top="scrollTop"
      style="height: 100vh; padding: 20rpx;"
    >
      <view v-for="(item, index) in dataList" :key="index" class="item">
        {{ item }}
      </view>
      <view v-if="loading" class="loading">正在加载...</view>
      <view v-if="noMore" class="no-more">没有更多了</view>
    </scroll-view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      dataList: ['第1项', '第2项', '第3项'],
      loading: false,
      noMore: false,
      scrollTop: 0
    }
  },
  methods: {
    loadMore() {
      if (this.loading || this.noMore) return;
      this.loading = true;
      setTimeout(() => {
        const newData = [];
        for (let i = 0; i < 5; i++) {
          newData.push(`新增第${this.dataList.length + 1 + i}项`);
        }
        this.dataList = [...this.dataList, ...newData];
        this.loading = false;
        if (this.dataList.length >= 20) {
          this.noMore = true;
        }
      }, 1000);
    }
  }
}
</script>

<style scoped>
.item {
  height: 80rpx;
  line-height: 80rpx;
  background: #f0f0f0;
  margin: 10rpx 0;
  padding: 0 20rpx;
  border-radius: 10rpx;
}
.loading, .no-more {
  text-align: center;
  padding: 20rpx;
  color: #999;
}
</style>

2. swiper 轮播图

用于实现轮播图效果,支持自动播放、指示器、循环播放等。

🔧 语法结构:

html 复制代码
<swiper 
  autoplay="true"       <!-- 自动播放 -->
  interval="3000"       <!-- 自动切换时间间隔 -->
  duration="500"        <!-- 滑动动画时长 -->
  circular="true"       <!-- 循环播放 -->
  indicator-dots="true" <!-- 显示指示点 -->
  @change="handleChange" <!-- 切换事件 -->
>
  <swiper-item v-for="(item, index) in images" :key="index">
    <image :src="item" mode="aspectFill" style="width: 100%; height: 100%;" />
  </swiper-item>
</swiper>

📌 属性说明:

属性名 类型 默认值 说明
autoplay Boolean false 是否自动播放
interval Number 5000 自动切换时间间隔(ms)
duration Number 500 滑动动画时长(ms)
circular Boolean false 是否循环播放
indicator-dots Boolean false 是否显示面板指示点
indicator-color String rgba(0,0,0,.3) 指示点颜色
indicator-active-color String #007aff 当前选中指示点颜色

💡 案例:带指示点+自动播放轮播图

vue 复制代码
<template>
  <view class="swiper-container">
    <swiper 
      autoplay="true" 
      interval="3000" 
      duration="500" 
      circular="true" 
      indicator-dots="true"
      indicator-color="rgba(255,255,255,0.5)"
      indicator-active-color="#fff"
      @change="onSwiperChange"
    >
      <swiper-item v-for="(img, index) in bannerImages" :key="index">
        <image :src="img" mode="aspectFill" style="width: 100%; height: 100%;" />
      </swiper-item>
    </swiper>
    <text class="current-index">当前第{{ currentIndex + 1 }}张</text>
  </view>
</template>

<script>
export default {
  data() {
    return {
      bannerImages: [
        'https://via.placeholder.com/750x300?text=Banner1',
        'https://via.placeholder.com/750x300?text=Banner2',
        'https://via.placeholder.com/750x300?text=Banner3'
      ],
      currentIndex: 0
    }
  },
  methods: {
    onSwiperChange(e) {
      this.currentIndex = e.detail.current;
    }
  }
}
</script>

<style scoped>
.swiper-container {
  position: relative;
}
.current-index {
  position: absolute;
  bottom: 20rpx;
  right: 20rpx;
  background: rgba(0,0,0,0.6);
  color: white;
  padding: 10rpx 20rpx;
  border-radius: 20rpx;
  font-size: 28rpx;
}
</style>

🧩 二、基础组件


1. rich-text 富文本渲染

用于渲染 HTML 格式的富文本内容(不支持所有标签和样式)。

🔧 语法结构:

html 复制代码
<rich-text :nodes="richTextContent"></rich-text>

📌 注意事项:

  • 仅支持部分 HTML 标签:<div>, <p>, <span>, <img>, <a>, <strong>, <em> 等。
  • 不支持 CSS 样式、JS 脚本、iframe 等。
  • 图片需使用 mode="widthFix" 或设置宽高。

💡 案例:渲染含图片和加粗文字的富文本

vue 复制代码
<template>
  <view class="rich-container">
    <rich-text :nodes="content"></rich-text>
  </view>
</template>

<script>
export default {
  data() {
    return {
      content: [
        {
          name: 'div',
          attrs: { class: 'title' },
          children: [{ type: 'text', text: '这是标题' }]
        },
        {
          name: 'p',
          children: [
            { type: 'text', text: '这是一段' },
            { name: 'strong', children: [{ type: 'text', text: '加粗' }] },
            { type: 'text', text: '的文字。' }
          ]
        },
        {
          name: 'img',
          attrs: {
            src: 'https://via.placeholder.com/300x200?text=Image',
            mode: 'widthFix',
            style: 'width: 100%; margin: 20rpx 0;'
          }
        }
      ]
    }
  }
}
</script>

<style scoped>
.title {
  font-size: 36rpx;
  font-weight: bold;
  margin-bottom: 20rpx;
}
</style>

2. progress 进度条

显示任务进度或加载状态。

🔧 语法结构:

html 复制代码
<progress 
  percent="50"        <!-- 百分比 -->
  show-info="true"    <!-- 是否显示百分比文字 -->
  stroke-width="6"    <!-- 进度条宽度 -->
  activeColor="#007AFF" <!-- 活动部分颜色 -->
  backgroundColor="#E0E0E0" <!-- 背景颜色 -->
/>

📌 属性说明:

属性名 类型 默认值 说明
percent Number 0 百分比(0~100)
show-info Boolean false 是否在进度条右侧显示百分比
stroke-width Number 6 进度条宽度(单位 rpx)
activeColor String #09BB07 已完成部分颜色
backgroundColor String #EBEBEB 未完成部分颜色
active Boolean false 是否从左到右动画

💡 案例:动态进度条 + 动画效果

vue 复制代码
<template>
  <view class="progress-container">
    <progress 
      :percent="progressValue" 
      show-info 
      stroke-width="8" 
      activeColor="#FF6B6B" 
      backgroundColor="#ddd"
      :active="true"
    />
    <button @click="startProgress">开始加载</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      progressValue: 0
    }
  },
  methods: {
    startProgress() {
      let timer = setInterval(() => {
        if (this.progressValue >= 100) {
          clearInterval(timer);
          return;
        }
        this.progressValue += 5;
      }, 200);
    }
  }
}
</script>

<style scoped>
.progress-container {
  padding: 40rpx;
}
</style>

📝 三、表单组件


1. button 按钮

触发操作按钮,支持多种样式和事件。

🔧 语法结构:

html 复制代码
<button 
  type="primary"     <!-- 主按钮 -->
  size="default"     <!-- 大小 -->
  plain="false"      <!-- 是否镂空 -->
  disabled="false"   <!-- 是否禁用 -->
  @click="handleClick"
  form-type="submit" <!-- 表单提交类型 -->
>
  点击我
</button>

📌 属性说明:

属性名 类型 默认值 说明
type String default 按钮类型:primary / warn / default
size String default 尺寸:default / mini
plain Boolean false 是否镂空
disabled Boolean false 是否禁用
loading Boolean false 是否显示加载状态
form-type String - 用于 form 组件内:submit / reset

💡 案例:三种按钮 + 加载状态

vue 复制代码
<template>
  <view class="btn-container">
    <button type="primary" @click="onClick">主按钮</button>
    <button type="warn" plain @click="onWarn">警告按钮</button>
    <button :loading="isLoading" @click="onLoading">加载中...</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      isLoading: false
    }
  },
  methods: {
    onClick() {
      uni.showToast({ title: '点击主按钮', icon: 'success' });
    },
    onWarn() {
      uni.showModal({
        title: '提示',
        content: '你点击了警告按钮',
        showCancel: false
      });
    },
    onLoading() {
      this.isLoading = true;
      setTimeout(() => {
        this.isLoading = false;
        uni.showToast({ title: '加载完成', icon: 'success' });
      }, 2000);
    }
  }
}
</script>

<style scoped>
.btn-container {
  padding: 40rpx;
}
</style>

2. input 输入框

单行文本输入框,支持密码、数字、邮箱等类型。

🔧 语法结构:

html 复制代码
<input 
  type="text"         <!-- 输入类型 -->
  value="初始值"      <!-- 当前值 -->
  placeholder="提示文字" <!-- 占位符 -->
  maxlength="10"      <!-- 最大长度 -->
  @input="onInput"    <!-- 输入事件 -->
  @confirm="onConfirm" <!-- 确认事件 -->
/>

📌 属性说明:

属性名 类型 默认值 说明
type String text 类型:text / number / idcard / digit / tel / email / password
value String '' 输入框内容
placeholder String '' 占位文字
maxlength Number 140 最大输入长度
disabled Boolean false 是否禁用
focus Boolean false 是否聚焦
confirm-type String done 键盘确认键类型:search / go / next / send / done

💡 案例:用户名+密码输入 + 验证

vue 复制代码
<template>
  <view class="input-container">
    <input 
      type="text" 
      v-model="username" 
      placeholder="请输入用户名" 
      maxlength="20"
      @input="checkUsername"
    />
    <input 
      type="password" 
      v-model="password" 
      placeholder="请输入密码" 
      maxlength="16"
      @input="checkPassword"
    />
    <text v-if="errorMsg" style="color: red; margin-top: 20rpx;">{{ errorMsg }}</text>
    <button @click="login">登录</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      username: '',
      password: '',
      errorMsg: ''
    }
  },
  methods: {
    checkUsername() {
      if (this.username.length < 3) {
        this.errorMsg = '用户名至少3个字符';
      } else {
        this.errorMsg = '';
      }
    },
    checkPassword() {
      if (this.password.length < 6) {
        this.errorMsg = '密码至少6位';
      } else {
        this.errorMsg = '';
      }
    },
    login() {
      if (!this.username || !this.password) {
        this.errorMsg = '请填写完整信息';
        return;
      }
      uni.showToast({ title: '登录成功', icon: 'success' });
    }
  }
}
</script>

<style scoped>
.input-container {
  padding: 40rpx;
}
input {
  height: 80rpx;
  line-height: 80rpx;
  border: 1px solid #ccc;
  border-radius: 10rpx;
  padding: 0 20rpx;
  margin: 20rpx 0;
}
</style>

3. picker 选择器

提供多种选择方式:普通选择、时间选择、日期选择、省市区选择。

🔧 语法结构:

html 复制代码
<!-- 普通选择 -->
<picker 
  mode="selector" 
  :range="options" 
  :value="selectedIndex" 
  @change="onPickerChange"
>
  <view>当前选中:{{ options[selectedIndex] }}</view>
</picker>

<!-- 时间选择 -->
<picker mode="time" @change="onTimeChange">选择时间</picker>

<!-- 日期选择 -->
<picker mode="date" @change="onDateChange">选择日期</picker>

<!-- 省市区选择 -->
<picker mode="region" @change="onRegionChange">选择地区</picker>

📌 属性说明:

属性名 类型 默认值 说明
mode String selector 类型:selector / multiSelector / time / date / region
range Array [] 选项数组(selector模式)
value Number 0 当前选中索引
start / end String - 日期/时间范围限制
custom-item String - 自定义选项显示内容

💡 案例:多列选择器 + 地区选择

vue 复制代码
<template>
  <view class="picker-container">
    <picker 
      mode="selector" 
      :range="colors" 
      :value="colorIndex" 
      @change="onColorChange"
    >
      <view>颜色:{{ colors[colorIndex] }}</view>
    </picker>

    <picker 
      mode="region" 
      @change="onRegionChange"
    >
      <view>地区:{{ regionText }}</view>
    </picker>

    <picker 
      mode="multiSelector" 
      :range="multiRange" 
      :value="multiValue" 
      @change="onMultiChange"
    >
      <view>多列选择:{{ multiText }}</view>
    </picker>
  </view>
</template>

<script>
export default {
  data() {
    return {
      colors: ['红色', '绿色', '蓝色', '黄色'],
      colorIndex: 0,
      regionText: '请选择地区',
      multiRange: [['北京', '上海', '广州'], ['朝阳区', '海淀区', '浦东新区']],
      multiValue: [0, 0],
      multiText: '北京 - 朝阳区'
    }
  },
  methods: {
    onColorChange(e) {
      this.colorIndex = e.detail.value;
    },
    onRegionChange(e) {
      this.regionText = e.detail.value.join(' ');
    },
    onMultiChange(e) {
      const val = e.detail.value;
      this.multiValue = val;
      this.multiText = `${this.multiRange[0][val[0]]} - ${this.multiRange[1][val[1]]}`;
    }
  }
}
</script>

<style scoped>
.picker-container {
  padding: 40rpx;
}
</style>

4. slider 滑块

滑动选择数值,常用于音量、亮度调节等。

🔧 语法结构:

html 复制代码
<slider 
  min="0"           <!-- 最小值 -->
  max="100"         <!-- 最大值 -->
  value="50"        <!-- 当前值 -->
  step="1"          <!-- 步长 -->
  show-value="true" <!-- 是否显示当前值 -->
  @change="onChange"
/>

📌 属性说明:

属性名 类型 默认值 说明
min Number 0 最小值
max Number 100 最大值
value Number 0 当前值
step Number 1 步长
show-value Boolean false 是否显示当前值
disabled Boolean false 是否禁用
activeColor String #007AFF 激活部分颜色
backgroundColor String #CCC 背景颜色

💡 案例:音量调节滑块 + 实时反馈

vue 复制代码
<template>
  <view class="slider-container">
    <slider 
      min="0" 
      max="100" 
      :value="volume" 
      step="5" 
      show-value 
      activeColor="#FF6B6B" 
      @change="onVolumeChange"
    />
    <text>当前音量:{{ volume }}%</text>
  </view>
</template>

<script>
export default {
  data() {
    return {
      volume: 50
    }
  },
  methods: {
    onVolumeChange(e) {
      this.volume = e.detail.value;
      // 可在此处调用音频API控制音量
    }
  }
}
</script>

<style scoped>
.slider-container {
  padding: 40rpx;
}
</style>

5. radio & checkbox 单选/复选框

用于选择单个或多个选项。

🔧 语法结构:

html 复制代码
<!-- 单选组 -->
<radio-group @change="onRadioChange">
  <label v-for="(item, index) in options" :key="index">
    <radio :value="item.value" :checked="item.checked" />
    {{ item.name }}
  </label>
</radio-group>

<!-- 复选组 -->
<checkbox-group @change="onCheckboxChange">
  <label v-for="(item, index) in options" :key="index">
    <checkbox :value="item.value" :checked="item.checked" />
    {{ item.name }}
  </label>
</checkbox-group>

📌 注意事项:

  • 必须用 radio-groupcheckbox-group 包裹。
  • 使用 v-model 时注意绑定的是数组或字符串。

💡 案例:性别选择 + 兴趣爱好选择

vue 复制代码
<template>
  <view class="radio-checkbox-container">
    <text>性别:</text>
    <radio-group @change="onGenderChange">
      <label v-for="item in genderOptions" :key="item.value">
        <radio :value="item.value" :checked="item.checked" />
        {{ item.name }}
      </label>
    </radio-group>

    <text>兴趣爱好:</text>
    <checkbox-group @change="onHobbyChange">
      <label v-for="item in hobbyOptions" :key="item.value">
        <checkbox :value="item.value" :checked="item.checked" />
        {{ item.name }}
      </label>
    </checkbox-group>

    <text>已选性别:{{ selectedGender }}</text>
    <text>已选爱好:{{ selectedHobbies.join(', ') }}</text>
  </view>
</template>

<script>
export default {
  data() {
    return {
      genderOptions: [
        { value: 'male', name: '男', checked: true },
        { value: 'female', name: '女', checked: false }
      ],
      hobbyOptions: [
        { value: 'reading', name: '阅读', checked: false },
        { value: 'sports', name: '运动', checked: false },
        { value: 'music', name: '音乐', checked: false }
      ],
      selectedGender: 'male',
      selectedHobbies: []
    }
  },
  methods: {
    onGenderChange(e) {
      this.selectedGender = e.detail.value;
      this.genderOptions.forEach(item => {
        item.checked = item.value === e.detail.value;
      });
    },
    onHobbyChange(e) {
      this.selectedHobbies = e.detail.value;
      this.hobbyOptions.forEach(item => {
        item.checked = e.detail.value.includes(item.value);
      });
    }
  }
}
</script>

<style scoped>
.radio-checkbox-container {
  padding: 40rpx;
}
label {
  display: flex;
  align-items: center;
  margin: 20rpx 0;
}
</style>

6. switch 开关

用于切换开关状态,如开启/关闭功能。

🔧 语法结构:

html 复制代码
<switch 
  :checked="isOn" 
  @change="onSwitchChange" 
  color="#FF6B6B"  <!-- 激活颜色 -->
/>

📌 属性说明:

属性名 类型 默认值 说明
checked Boolean false 是否选中
disabled Boolean false 是否禁用
color String #007AFF 开关颜色

💡 案例:夜间模式开关

vue 复制代码
<template>
  <view class="switch-container">
    <switch 
      :checked="nightMode" 
      @change="toggleNightMode" 
      color="#FF6B6B"
    />
    <text>{{ nightMode ? '夜间模式已开启' : '夜间模式已关闭' }}</text>
  </view>
</template>

<script>
export default {
  data() {
    return {
      nightMode: false
    }
  },
  methods: {
    toggleNightMode(e) {
      this.nightMode = e.detail.value;
      if (this.nightMode) {
        uni.setNavigationBarTitle({ title: '夜间模式' });
        uni.setTabBarStyle({ backgroundColor: '#222' });
      } else {
        uni.setNavigationBarTitle({ title: '白天模式' });
        uni.setTabBarStyle({ backgroundColor: '#ffffff' });
      }
    }
  }
}
</script>

<style scoped>
.switch-container {
  padding: 40rpx;
  display: flex;
  align-items: center;
}
</style>

7. textarea 多行输入框

支持多行文本输入,适合评论、描述等内容。

🔧 语法结构:

html 复制代码
<textarea 
  value="初始内容" 
  placeholder="请输入内容" 
  maxlength="200" 
  auto-focus="false" 
  @input="onTextareaInput"
/>

📌 属性说明:

属性名 类型 默认值 说明
value String '' 输入内容
placeholder String '' 占位文字
maxlength Number 140 最大长度
auto-focus Boolean false 是否自动聚焦
fixed Boolean false 是否固定定位(避免键盘遮挡)
disable-default-padding Boolean false 是否禁用默认内边距

💡 案例:评论输入框 + 字数统计

vue 复制代码
<template>
  <view class="textarea-container">
    <textarea 
      v-model="comment" 
      placeholder="请输入您的评论..." 
      maxlength="200" 
      auto-focus 
      @input="updateCount"
    />
    <text style="font-size: 24rpx; color: #999;">{{ count }}/200</text>
    <button @click="submitComment">提交评论</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      comment: '',
      count: 0
    }
  },
  methods: {
    updateCount() {
      this.count = this.comment.length;
    },
    submitComment() {
      if (!this.comment.trim()) {
        uni.showToast({ title: '评论不能为空', icon: 'none' });
        return;
      }
      uni.showToast({ title: '评论提交成功', icon: 'success' });
      this.comment = '';
      this.count = 0;
    }
  }
}
</script>

<style scoped>
.textarea-container {
  padding: 40rpx;
}
textarea {
  height: 200rpx;
  border: 1px solid #ccc;
  border-radius: 10rpx;
  padding: 20rpx;
  font-size: 32rpx;
  margin-bottom: 20rpx;
}
</style>

8. form 表单容器

用于包裹表单元素,支持提交、重置等操作。

🔧 语法结构:

html 复制代码
<form @submit="onSubmit" @reset="onReset">
  <input name="username" v-model="formData.username" placeholder="用户名" />
  <input name="password" type="password" v-model="formData.password" placeholder="密码" />
  <button form-type="submit">提交</button>
  <button form-type="reset">重置</button>
</form>

📌 属性说明:

  • @submit:表单提交事件
  • @reset:表单重置事件
  • form-type="submit":提交按钮
  • form-type="reset":重置按钮

💡 案例:用户注册表单 + 数据校验

vue 复制代码
<template>
  <view class="form-container">
    <form @submit="onSubmit" @reset="onReset">
      <input 
        name="username" 
        v-model="formData.username" 
        placeholder="请输入用户名" 
        maxlength="20"
      />
      <input 
        name="email" 
        type="email" 
        v-model="formData.email" 
        placeholder="请输入邮箱" 
      />
      <input 
        name="password" 
        type="password" 
        v-model="formData.password" 
        placeholder="请输入密码" 
        maxlength="16"
      />
      <input 
        name="confirmPassword" 
        type="password" 
        v-model="formData.confirmPassword" 
        placeholder="请确认密码" 
      />
      <button form-type="submit">注册</button>
      <button form-type="reset">重置</button>
    </form>
  </view>
</template>

<script>
export default {
  data() {
    return {
      formData: {
        username: '',
        email: '',
        password: '',
        confirmPassword: ''
      }
    }
  },
  methods: {
    onSubmit(e) {
      const { username, email, password, confirmPassword } = this.formData;
      if (!username || !email || !password || !confirmPassword) {
        uni.showToast({ title: '请填写完整信息', icon: 'none' });
        return;
      }
      if (password !== confirmPassword) {
        uni.showToast({ title: '两次密码不一致', icon: 'none' });
        return;
      }
      if (!/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(email)) {
        uni.showToast({ title: '邮箱格式错误', icon: 'none' });
        return;
      }
      uni.showToast({ title: '注册成功', icon: 'success' });
      console.log('提交数据:', this.formData);
    },
    onReset() {
      this.formData = {
        username: '',
        email: '',
        password: '',
        confirmPassword: ''
      };
    }
  }
}
</script>

<style scoped>
.form-container {
  padding: 40rpx;
}
input {
  height: 80rpx;
  line-height: 80rpx;
  border: 1px solid #ccc;
  border-radius: 10rpx;
  padding: 0 20rpx;
  margin: 20rpx 0;
}
</style>

🎥 四、媒体组件


1. camera 相机组件

调用设备摄像头,可拍照或录制视频。

🔧 语法结构:

html 复制代码
<camera 
  device-position="back"  <!-- 前后摄像头 -->
  flash="off"             <!-- 闪光灯 -->
  @error="onCameraError" 
  @stop="onCameraStop"
/>

📌 属性说明:

属性名 类型 默认值 说明
device-position String back 摄像头方向:front / back
flash String off 闪光灯:off / on / auto
@error Function - 摄像头错误回调
@stop Function - 录制停止回调

💡 案例:拍照功能 + 保存图片

vue 复制代码
<template>
  <view class="camera-container">
    <camera 
      style="width: 100%; height: 400rpx;" 
      device-position="back" 
      flash="auto" 
      @error="onCameraError"
    ></camera>
    <button @click="takePhoto">拍照</button>
    <image v-if="photoSrc" :src="photoSrc" mode="aspectFit" style="width: 100%; height: 400rpx; margin-top: 20rpx;" />
  </view>
</template>

<script>
export default {
  data() {
    return {
      photoSrc: ''
    }
  },
  methods: {
    takePhoto() {
      const ctx = uni.createCameraContext();
      ctx.takePhoto({
        quality: 'high',
        success: (res) => {
          this.photoSrc = res.tempImagePath;
          uni.showToast({ title: '拍照成功', icon: 'success' });
        },
        fail: (err) => {
          uni.showToast({ title: '拍照失败', icon: 'none' });
        }
      });
    },
    onCameraError(e) {
      console.error('摄像头错误:', e.detail.errMsg);
      uni.showToast({ title: '摄像头权限未开启', icon: 'none' });
    }
  }
}
</script>

<style scoped>
.camera-container {
  padding: 40rpx;
}
</style>

2. video 视频播放器

播放本地或网络视频,支持全屏、倍速、弹幕等。

🔧 语法结构:

html 复制代码
<video 
  src="https://example.com/video.mp4" 
  controls="true"       <!-- 是否显示控制栏 -->
  autoplay="false"      <!-- 是否自动播放 -->
  loop="false"          <!-- 是否循环播放 -->
  @play="onPlay" 
  @pause="onPause"
/>

📌 属性说明:

属性名 类型 默认值 说明
src String '' 视频地址
controls Boolean true 是否显示播放控件
autoplay Boolean false 是否自动播放
loop Boolean false 是否循环播放
poster String '' 封面图
muted Boolean false 是否静音
@play / @pause / @ended Function - 播放/暂停/结束事件

💡 案例:播放视频 + 控制播放状态

vue 复制代码
<template>
  <view class="video-container">
    <video 
      :src="videoUrl" 
      controls 
      :poster="poster" 
      @play="onPlay" 
      @pause="onPause" 
      @ended="onEnded"
      style="width: 100%; height: 400rpx;"
    />
    <text>播放状态:{{ playStatus }}</text>
    <button @click="togglePlay">播放/暂停</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      videoUrl: 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4',
      poster: 'https://via.placeholder.com/750x400?text=Video+Poster',
      playStatus: '未播放'
    }
  },
  methods: {
    onPlay() {
      this.playStatus = '正在播放';
    },
    onPause() {
      this.playStatus = '已暂停';
    },
    onEnded() {
      this.playStatus = '播放结束';
    },
    togglePlay() {
      const videoContext = uni.createVideoContext('myVideo');
      if (this.playStatus === '正在播放') {
        videoContext.pause();
      } else {
        videoContext.play();
      }
    }
  }
}
</script>

<style scoped>
.video-container {
  padding: 40rpx;
}
</style>

3. map 地图组件

显示地图,支持标记、缩放、定位等功能。

🔧 语法结构:

html 复制代码
<map 
  :latitude="latitude" 
  :longitude="longitude" 
  scale="16" 
  :markers="markers" 
  @regionchange="onRegionChange"
/>

📌 属性说明:

属性名 类型 默认值 说明
latitude Number 0 中心纬度
longitude Number 0 中心经度
scale Number 16 缩放级别(5~18)
markers Array [] 标记点数组
@regionchange Function - 地图区域变化事件

💡 案例:显示当前位置 + 添加标记

vue 复制代码
<template>
  <view class="map-container">
    <map 
      :latitude="latitude" 
      :longitude="longitude" 
      scale="16" 
      :markers="markers" 
      style="width: 100%; height: 400rpx;"
    />
    <text>当前位置:{{ locationText }}</text>
    <button @click="getCurrentLocation">获取当前位置</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      latitude: 39.9042,
      longitude: 116.4074,
      locationText: '北京',
      markers: [
        {
          id: 1,
          latitude: 39.9042,
          longitude: 116.4074,
          title: '天安门',
          iconPath: '/static/location.png',
          width: 30,
          height: 30
        }
      ]
    }
  },
  methods: {
    getCurrentLocation() {
      uni.getLocation({
        type: 'gcj02',
        success: (res) => {
          this.latitude = res.latitude;
          this.longitude = res.longitude;
          this.locationText = `纬度:${res.latitude}, 经度:${res.longitude}`;
          this.markers = [{
            id: 1,
            latitude: res.latitude,
            longitude: res.longitude,
            title: '我的位置',
            iconPath: '/static/location.png',
            width: 30,
            height: 30
          }];
        },
        fail: () => {
          uni.showToast({ title: '获取位置失败', icon: 'none' });
        }
      });
    }
  }
}
</script>

<style scoped>
.map-container {
  padding: 40rpx;
}
</style>

🎯 综合性实战案例


🧩 案例一:典型注册页(综合运用表单组件)

包含用户名、邮箱、密码、确认密码、协议勾选、提交按钮等。

vue 复制代码
<template>
  <view class="register-page">
    <view class="header">
      <text>用户注册</text>
    </view>
    <form @submit="onRegister">
      <input 
        v-model="form.username" 
        placeholder="请输入用户名(3-20字符)" 
        maxlength="20"
        @input="validateUsername"
      />
      <input 
        v-model="form.email" 
        type="email" 
        placeholder="请输入邮箱" 
        @input="validateEmail"
      />
      <input 
        v-model="form.password" 
        type="password" 
        placeholder="请输入密码(6-16字符)" 
        maxlength="16"
        @input="validatePassword"
      />
      <input 
        v-model="form.confirmPassword" 
        type="password" 
        placeholder="请确认密码" 
        @input="validateConfirmPassword"
      />
      <view class="agree-box">
        <checkbox v-model="form.agree" />
        <text>我已阅读并同意《用户协议》</text>
      </view>
      <button form-type="submit" :disabled="!canSubmit">注册</button>
    </form>
    <text v-if="errorMessage" style="color: red; margin-top: 20rpx;">{{ errorMessage }}</text>
  </view>
</template>

<script>
export default {
  data() {
    return {
      form: {
        username: '',
        email: '',
        password: '',
        confirmPassword: '',
        agree: false
      },
      errorMessage: ''
    }
  },
  computed: {
    canSubmit() {
      return (
        this.form.username.length >= 3 &&
        this.form.email &&
        this.form.password.length >= 6 &&
        this.form.password === this.form.confirmPassword &&
        this.form.agree
      );
    }
  },
  methods: {
    validateUsername() {
      if (this.form.username.length < 3) {
        this.errorMessage = '用户名至少3个字符';
      } else {
        this.errorMessage = '';
      }
    },
    validateEmail() {
      const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
      if (!reg.test(this.form.email)) {
        this.errorMessage = '邮箱格式错误';
      } else {
        this.errorMessage = '';
      }
    },
    validatePassword() {
      if (this.form.password.length < 6) {
        this.errorMessage = '密码至少6位';
      } else {
        this.errorMessage = '';
      }
    },
    validateConfirmPassword() {
      if (this.form.password !== this.form.confirmPassword) {
        this.errorMessage = '两次密码不一致';
      } else {
        this.errorMessage = '';
      }
    },
    onRegister() {
      if (!this.canSubmit) {
        uni.showToast({ title: '请检查表单填写', icon: 'none' });
        return;
      }
      uni.showToast({ title: '注册成功', icon: 'success' });
      console.log('注册数据:', this.form);
      // 可在此处调用接口提交数据
    }
  }
}
</script>

<style scoped>
.register-page {
  padding: 40rpx;
  background: #f8f8f8;
}
.header {
  font-size: 48rpx;
  font-weight: bold;
  text-align: center;
  margin-bottom: 40rpx;
}
input {
  height: 80rpx;
  line-height: 80rpx;
  border: 1px solid #ccc;
  border-radius: 10rpx;
  padding: 0 20rpx;
  margin: 20rpx 0;
}
.agree-box {
  display: flex;
  align-items: center;
  margin: 20rpx 0;
}
.agree-box checkbox {
  margin-right: 10rpx;
}
button {
  margin-top: 40rpx;
}
</style>

🧩 案例二:典型个人中心页(综合运用容器、媒体、基础组件)

包含头像、昵称、积分、设置开关、历史订单、退出登录等。

vue 复制代码
<template>
  <view class="profile-page">
    <view class="header">
      <image :src="user.avatar" mode="aspectFill" class="avatar" />
      <text class="nickname">{{ user.nickname }}</text>
    </view>

    <scroll-view scroll-y="true" class="content">
      <view class="section">
        <text>账户信息</text>
        <view class="info-item">
          <text>手机号:</text>
          <text>{{ user.phone }}</text>
        </view>
        <view class="info-item">
          <text>积分:</text>
          <text>{{ user.points }} 分</text>
        </view>
      </view>

      <view class="section">
        <text>功能设置</text>
        <view class="setting-item">
          <text>夜间模式</text>
          <switch :checked="settings.nightMode" @change="toggleNightMode" />
        </view>
        <view class="setting-item">
          <text>消息通知</text>
          <switch :checked="settings.notification" @change="toggleNotification" />
        </view>
      </view>

      <view class="section">
        <text>历史订单</text>
        <view v-for="(order, index) in orders" :key="index" class="order-item">
          <text>订单号:{{ order.id }}</text>
          <text>金额:¥{{ order.amount }}</text>
          <text>状态:{{ order.status }}</text>
        </view>
      </view>

      <view class="section">
        <button @click="logout">退出登录</button>
      </view>
    </scroll-view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      user: {
        avatar: 'https://via.placeholder.com/100x100?text=User',
        nickname: '小明同学',
        phone: '138****1234',
        points: 1250
      },
      settings: {
        nightMode: false,
        notification: true
      },
      orders: [
        { id: '20250101001', amount: 299, status: '已完成' },
        { id: '20250102002', amount: 199, status: '待发货' },
        { id: '20250103003', amount: 599, status: '已取消' }
      ]
    }
  },
  methods: {
    toggleNightMode(e) {
      this.settings.nightMode = e.detail.value;
      if (e.detail.value) {
        uni.setNavigationBarTitle({ title: '夜间模式' });
        uni.setTabBarStyle({ backgroundColor: '#222' });
      } else {
        uni.setNavigationBarTitle({ title: '个人中心' });
        uni.setTabBarStyle({ backgroundColor: '#ffffff' });
      }
    },
    toggleNotification(e) {
      this.settings.notification = e.detail.value;
      uni.showToast({
        title: e.detail.value ? '通知已开启' : '通知已关闭',
        icon: 'none'
      });
    },
    logout() {
      uni.showModal({
        title: '确认退出',
        content: '您确定要退出登录吗?',
        success: (res) => {
          if (res.confirm) {
            uni.clearStorageSync();
            uni.reLaunch({ url: '/pages/login/login' });
          }
        }
      });
    }
  }
}
</script>

<style scoped>
.profile-page {
  padding: 40rpx;
  background: #f8f8f8;
}
.header {
  display: flex;
  align-items: center;
  margin-bottom: 40rpx;
}
.avatar {
  width: 100rpx;
  height: 100rpx;
  border-radius: 50%;
  margin-right: 20rpx;
}
.nickname {
  font-size: 36rpx;
  font-weight: bold;
}
.content {
  height: calc(100vh - 200rpx);
}
.section {
  background: white;
  border-radius: 20rpx;
  padding: 30rpx;
  margin-bottom: 30rpx;
  box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);
}
.section > text {
  font-size: 32rpx;
  font-weight: bold;
  margin-bottom: 20rpx;
  display: block;
}
.info-item, .setting-item {
  display: flex;
  justify-content: space-between;
  padding: 20rpx 0;
  border-bottom: 1px solid #eee;
}
.order-item {
  padding: 20rpx 0;
  border-bottom: 1px solid #eee;
}
.order-item text {
  display: block;
  margin: 5rpx 0;
}
button {
  margin-top: 40rpx;
  background: #FF6B6B;
  color: white;
}
</style>

📝 本章小结

容器组件scroll-view 实现滚动交互,swiper 实现轮播图,是布局核心。

基础组件rich-text 渲染富文本,progress 显示进度,提升用户体验。

表单组件 :涵盖 inputbuttonpickersliderradio/checkboxswitchtextareaform,是数据采集核心。

媒体组件cameravideomap 扩展应用能力,满足多媒体需求。

综合案例:通过"注册页"和"个人中心页",将各组件串联,形成完整业务闭环。

📌 建议

  • 多动手实践,熟悉每个组件的属性和事件。
  • 结合 uni-app 官方文档深入理解兼容性和平台差异。
  • 学会组合使用组件,构建复杂交互页面。

🎯 进阶方向

  • 使用 ref 获取组件实例进行操作
  • 结合 Vuex 管理全局状态
  • 使用 uni.request 调用后端接口
  • 优化性能:节流、防抖、虚拟列表等
相关推荐
雪芽蓝域zzs3 小时前
uniapp 修改android包名
android·uni-app
芒果沙冰哟3 小时前
uniapp canvas实现手写签字功能(包括重签,撤回等按钮)
uni-app
爱折腾的小码农3 小时前
uni-app 小程序开发避坑:诡异的 `module ‘...‘ is not defined` 错误与我的解决方案
uni-app
Q_Q19632884753 小时前
python+uniapp基于微信小程序的助眠小程序
spring boot·python·小程序·django·flask·uni-app·node.js
ZYMFZ3 小时前
python面向对象
前端·数据库·python
wahkim3 小时前
Flutter 学习资源及视频
学习
长空任鸟飞_阿康3 小时前
在 Vue 3.5 中优雅地集成 wangEditor,并定制“AI 工具”下拉菜单(总结/润色/翻译)
前端·vue.js·人工智能
lapiii3583 小时前
快速学完React计划(第一天)
前端·react.js·前端框架