当前位置: 首页 > article >正文

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

目录

  • 前言
  • 1. 实战
  • 2. Demo

前言

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

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

何为命令式 何为声明式

  • 命令式的体验,随时都会有提交的按钮,但是点击提交才会显示不满足的条件!
    在这里插入图片描述
  • 声明式的体验,不满足条件时,按钮框是灰色的!
    在这里插入图片描述
  1. 命令式:
    提交逻辑复杂,需要异步校验(如服务端唯一性检查)
    表单字段多,依赖用户行为触发验证
    需要复用校验函数,或表单逻辑分散多处

  2. 声明式:
    表单简单明了
    用户体验优先,提前告知无法提交的情况
    状态可视化,一目了然

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

1. 实战

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

命令式 submit 校验方式(validateForm)

<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 控制按钮状态

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

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

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

http://www.kler.cn/a/601210.html

相关文章:

  • A2 最佳学习方法
  • 本地JAR批量传私服
  • ngx_http_index_set_index
  • Transformer、ELMo、GPT、BERT的详细理解
  • 【大模型】什么是循环神经网络(RNNs)
  • ollama迁移已下载的单个模型到服务器
  • 联核防爆无人叉车:高危环境中的安全搬运守护者
  • 【C++】深入理解list迭代器的设计与实现
  • zookeeper部署教程
  • element-ui calendar 组件源码分享
  • 软件测试之单元测试/集成测试/系统测试详解
  • CentOS 7部署主域名服务器 DNS
  • Windows 下使用 Docker 部署 Go 应用与 Nginx 详细教程
  • DeepSeek smallpond为何选中DuckDB?轻量级分析数据库的“屠龙术“
  • 内核编程十三:进程状态详解
  • React 知识回顾(HOC、合成事件、Fiber)
  • 【数据结构进阶】位图
  • Python Sanic面试题及参考答案
  • 手动创建kkFileView4.4.0镜像
  • 嵌入式八股RTOS与Linux--hea4与TLSF篇