UniApp 表单校验两种方式对比:命令式与声明式

目录

  • 前言
  • [1. 实战](#1. 实战)
  • [2. Demo](#2. Demo)

前言

🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF

以下主要针对Demo讲解,从实战中的体会

何为命令式 何为声明式

  • 命令式的体验,随时都会有提交的按钮,但是点击提交才会显示不满足的条件!
  • 声明式的体验,不满足条件时,按钮框是灰色的!
  1. 命令式:

    提交逻辑复杂,需要异步校验(如服务端唯一性检查)

    表单字段多,依赖用户行为触发验证

    需要复用校验函数,或表单逻辑分散多处

  2. 声明式:

    表单简单明了

    用户体验优先,提前告知无法提交的情况

    状态可视化,一目了然

对比项 命令式校验(方法中校验) 声明式校验(computed + disabled 控制)
📋 方式 在 submit() 方法内通过 validateForm() 显式校验 通过 computed 计算属性实时判断是否可提交
🧠 编程范式 命令式(Imperative) 声明式(Declarative)
💡 表达方式 手动控制流程:如果失败就 return false 自动计算状态:按钮根据是否满足条件自动禁用
🔁 可复用性 校验逻辑聚焦在 validateForm(),但要手动调用 校验逻辑绑定在状态中,按钮等 UI 自动响应
🧪 用户体验 用户点击"提交"后才提示不通过 一目了然,提交按钮禁用,无法误触提交
⚙️ 灵活性 可以灵活插入额外逻辑,如提交前弹窗确认 逻辑适合纯状态驱动,复杂流程需另外处理
🪛 适用场景 需要流程控制、嵌套逻辑、异步校验时更适合 表单项简单明确、状态驱动时更适合

1. 实战

实战中抽取的Demo比较简易:

命令式 submit 校验方式(validateForm)

html 复制代码
<template>
  <button type="primary" @click="submit">提交</button>
</template>

<script>
export default {
  data() {
    return {
      imgCntrF: [],
      damPhotoList: []
    };
  },
  methods: {
    validateForm() {
      if (!this.imgCntrF.length) {
        uni.showToast({ title: '请拍摄箱门照片', icon: 'none' });
        return false;
      }
      if (this.damPhotoList.length < 2) {
        uni.showToast({ title: '请至少拍摄 2 张照片', icon: 'none' });
        return false;
      }
      return true;
    },
    async submit() {
      if (!this.validateForm()) return;
      // 执行提交逻辑
      console.log("提交成功");
    }
  }
}
</script>

声明式 computed 控制按钮状态

html 复制代码
<template>
  <button type="primary" :disabled="!canSubmit" @click="submit">提交</button>
</template>

<script>
export default {
  data() {
    return {
      photoList: {
        door: '',  // 对应 imgCntrF
        side: ''
      },
      damPhotoList: [],
      photoField: [
        { key: 'door', label: '箱门照片' },
        { key: 'side', label: '侧面照片' }
      ]
    };
  },
  computed: {
    canSubmit() {
      return this.photoField.every(field => this.photoList[field.key]) &&
             this.damPhotoList.length >= 2;
    }
  },
  methods: {
    submit() {
      console.log("提交成功");
    }
  }
}
</script>

2. Demo

以UniApp( Vue2 语法 + script 写法)

命令式校验提交(Imperative)

html 复制代码
<template>
  <view class="container">
    <view class="section">
      <text>箱门照片:</text>
      <button @click="selectBoxDoorPhoto">选择照片</button>
      <image v-if="imgCntrF" :src="imgCntrF" class="img-preview" />
    </view>

    <view class="section">
      <text>破损照片:</text>
      <button @click="addDamPhoto">添加照片</button>
      <view class="photo-list">
        <image v-for="(photo, index) in damPhotoList" :key="index" :src="photo" class="img-preview" />
      </view>
    </view>

    <button type="primary" @click="submit">提交</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      imgCntrF: '', // 箱门照片 URL
      damPhotoList: [] // 破损照片 URL 列表
    };
  },
  methods: {
    selectBoxDoorPhoto() {
      uni.chooseImage({
        count: 1,
        success: (res) => {
          this.imgCntrF = res.tempFilePaths[0];
        }
      });
    },
    addDamPhoto() {
      uni.chooseImage({
        count: 1,
        success: (res) => {
          this.damPhotoList.push(res.tempFilePaths[0]);
        }
      });
    },
    validateForm() {
      if (!this.imgCntrF) {
        uni.showToast({ title: '请拍摄箱门照片', icon: 'none' });
        return false;
      }
      if (this.damPhotoList.length < 2) {
        uni.showToast({ title: '请至少拍摄 2 张破损照片', icon: 'none' });
        return false;
      }
      return true;
    },
    submit() {
      if (!this.validateForm()) return;

      // 模拟提交成功
      uni.showToast({ title: '提交成功', icon: 'success' });
    }
  }
};
</script>

<style>
.container {
  padding: 20rpx;
}
.section {
  margin-bottom: 30rpx;
}
.img-preview {
  width: 200rpx;
  height: 200rpx;
  margin-top: 10rpx;
}
.photo-list {
  display: flex;
  flex-wrap: wrap;
  gap: 10rpx;
}
</style>

声明式按钮控制提交(Declarative)

html 复制代码
<template>
  <view class="container">
    <view class="section">
      <text>箱门照片:</text>
      <button @click="selectBoxDoorPhoto">选择照片</button>
      <image v-if="photoList.door" :src="photoList.door" class="img-preview" />
    </view>

    <view class="section">
      <text>破损照片:</text>
      <button @click="addDamPhoto">添加照片</button>
      <view class="photo-list">
        <image v-for="(photo, index) in damPhotoList" :key="index" :src="photo" class="img-preview" />
      </view>
    </view>

    <button type="primary" :disabled="!canSubmit" @click="submit">提交</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      photoList: {
        door: '' // 箱门照片
      },
      damPhotoList: []
    };
  },
  computed: {
    canSubmit() {
      return !!this.photoList.door && this.damPhotoList.length >= 2;
    }
  },
  methods: {
    selectBoxDoorPhoto() {
      uni.chooseImage({
        count: 1,
        success: (res) => {
          this.photoList.door = res.tempFilePaths[0];
        }
      });
    },
    addDamPhoto() {
      uni.chooseImage({
        count: 1,
        success: (res) => {
          this.damPhotoList.push(res.tempFilePaths[0]);
        }
      });
    },
    submit() {
      uni.showToast({ title: '提交成功', icon: 'success' });
    }
  }
};
</script>

<style>
.container {
  padding: 20rpx;
}
.section {
  margin-bottom: 30rpx;
}
.img-preview {
  width: 200rpx;
  height: 200rpx;
  margin-top: 10rpx;
}
.photo-list {
  display: flex;
  flex-wrap: wrap;
  gap: 10rpx;
}
</style>
相关推荐
Jiaberrr3 小时前
uniapp 安卓 APP 后台持续运行(保活)的尝试办法
android·前端·javascript·uni-app·app·保活
不老刘3 小时前
uniapp+vue3实现CK通信协议(基于jjc-tcpTools)
前端·javascript·uni-app
疯狂的沙粒4 小时前
uni-app 如何实现选择和上传非图像、视频文件?
前端·javascript·uni-app
^Rocky4 小时前
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
uni-app·腾讯云·媒体
$程4 小时前
Uniapp 二维码生成与解析完整教程
前端·uni-app
tryCbest5 小时前
UniApp系列
uni-app·web
iOS阿玮6 小时前
社交的本质是价值交换,请不要浪费别人的时间。
uni-app·app·apple
monika_yu7 小时前
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uni-app
大阳光男孩9 小时前
Vue3 + UniApp 蓝牙连接与数据发送(稳定版)
uni-app
八月林城1 天前
echarts在uniapp中使用安卓真机运行时无法显示的问题
android·uni-app·echarts