UniApp表單校驗兩種方式對比詳解
以下主要針對Demo講解,從實戰(zhàn)中的體會
何為命令式 何為聲明式
- 命令式的體驗,隨時都會有提交的按鈕,但是點擊提交才會顯示不滿足的條件!

- 聲明式的體驗,不滿足條件時,按鈕框是灰色的!

命令式:
提交邏輯復雜,需要異步校驗(如服務端唯一性檢查)
表單字段多,依賴用戶行為觸發(fā)驗證
需要復用校驗函數(shù),或表單邏輯分散多處聲明式:
表單簡單明了
用戶體驗優(yōu)先,提前告知無法提交的情況
狀態(tài)可視化,一目了然
| 對比項 | 命令式校驗(方法中校驗) | 聲明式校驗(computed + disabled 控制) |
|---|---|---|
| 方式 | 在 submit() 方法內(nèi)通過 validateForm() 顯式校驗 | 通過 computed 計算屬性實時判斷是否可提交 |
| 編程范式 | 命令式(Imperative) | 聲明式(Declarative) |
| 表達方式 | 手動控制流程:如果失敗就 return false | 自動計算狀態(tài):按鈕根據(jù)是否滿足條件自動禁用 |
| 可復用性 | 校驗邏輯聚焦在 validateForm(),但要手動調(diào)用 | 校驗邏輯綁定在狀態(tài)中,按鈕等 UI 自動響應 |
| 用戶體驗 | 用戶點擊“提交”后才提示不通過 | 一目了然,提交按鈕禁用,無法誤觸提交 |
| 靈活性 | 可以靈活插入額外邏輯,如提交前彈窗確認 | 邏輯適合純狀態(tài)驅(qū)動,復雜流程需另外處理 |
| 適用場景 | 需要流程控制、嵌套邏輯、異步校驗時更適合 | 表單項簡單明確、狀態(tài)驅(qū)動時更適合 |
1. 實戰(zhàn)
實戰(zhàn)中抽取的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;
// 執(zhí)行提交邏輯
console.log("提交成功");
}
}
}
</script>
聲明式 computed 控制按鈕狀態(tài)
<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: '側(cè)面照片' }
]
};
},
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>
到此這篇關于UniApp表單校驗兩種方式對比詳解的文章就介紹到這了,更多相關UniApp表單校驗方式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Object.defineProperty()?完整指南示例詳解
本文深入理解`Object.defineProperty()`的方法,包括基礎概念、屬性描述符的完整選項、常見使用場景等,感興趣的朋友跟隨小編一起看看吧2025-01-01
詳解JS: reduce方法實現(xiàn) webpack多文件入口
本篇文章主要介紹了詳解JS: reduce方法實現(xiàn) webpack多文件入口,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02
微信小程序基于高德地圖API實現(xiàn)天氣組件(動態(tài)效果)
這篇文章主要介紹了微信小程序基于高德地圖API實現(xiàn)天氣組件(動態(tài)效果),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10
JavaScript將坐標字符串轉(zhuǎn)為數(shù)組的項目實踐
本文主要介紹了JavaScript將坐標字符串轉(zhuǎn)為數(shù)組的項目實踐,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-01-01
JavaScript中變量提升導致未定義(undefined)的問題及解決方法
在 JavaScript 中,變量提升(Hoisting)是一個相對常見的行為,尤其是當你遇到 undefined 錯誤時,本文將詳細探討變量提升的概念、其對代碼執(zhí)行的影響以及如何避免因為變量提升而導致 undefined 的問題,需要的朋友可以參考下2024-09-09

