​作者:easonruan

笔者目前正在开发一个涉及较多表单的场景的新项目。但由于是新项目进度赶,产品人员紧缺,表单需求往往没有考虑得很周全。

那作为一名前端开发,如何辅助产品尽可能让表单需求一次即通关,减少反复沟通以及提缺陷修缺陷的时间,从而加快项目进度?

以下是笔者在项目中在表单开发方面的一些总结:

以下演示案例为vue项目,组件库为element-ui

1. 重视通用型表单验证

业务场景:

表单中如果涉及手机号码,因为手机号码是特殊场景,我们很容易想到特殊的校验规则——手机号的正则校验。

然而对于一个通用型字段,如标题 title 、描述 desc 等基本的字段,它们实在太普通太一般,导致我们放松了警惕。

导致问题:开发与测试反复在 ​tapd​提缺陷修缺陷,在一堆小问题上浪费了大量时间,工作效率低。

解决方法:

  • 避免用户的输入前后有空格,即trim
  • 限制最大输入长度,即max-length
  • 不能包含特殊字符,即emoji表情是否能输入等
  1. ​// form rules​
  2. ​export default {​
  3. ​  title: [​
  4. ​    /**​
  5. ​    * Tips 避免用户的输入前后有空格​
  6. ​    * 可以使用 v-model.trim 指令自动清除用户前后空格,​
  7. ​    * 技术手段能解决的,我们避免提示用户​
  8. ​    **/​
  9. ​    { max: 50, message: '不能超过50个字', trigger: 'blur' },​
  10. ​    {​
  11. ​        pattern: /^[ -~ 一-龥 ＀-。  -   -〗]+$/,​
  12. ​        // - 基本拉丁字母  https://unicode-table.com/cn/blocks/basic-latin/​
  13. ​        // 一-龥 中文 https://unicode-table.com/cn/blocks/cjk-unified-ideographs/​
  14. ​        // ＀-。 半角及全角形式字符 https://unicode-table.com/cn/blocks/halfwidth-and-fullwidth-forms/​
  15. ​        //  -  英文标点 https://unicode-table.com/cn/blocks/general-punctuation/​
  16. ​        //  -〗 中文标点 https://unicode-table.com/cn/blocks/cjk-symbols-and-punctuation/​
  17. ​        message: "不能包含特殊字符",​
  18. ​        trigger: "blur"​
  19. ​    }​
  20. ​  ]​
  21. ​}​

2. 避免重复提交

业务场景:当用户快速点击提交按钮,导致问题:页面会重复发请求给后端。

解决方法虽然很简单,但这却是开发最容易忽略的,也是tapd上最经常见的缺陷问题。

解决方法一:在业务代码执行完之前不能再次触发

  1. ​export default {​
  2. ​  methods: {​
  3. ​     onSubmit () {​
  4. ​         // 可以与Loading搭配使用​
  5. ​        if (this.isCommitting) return;​
  6. ​        this.isCommitting = true;​

  7. ​        // 表单验证以及业务请求代码​

  8. ​        this.isCommitting = false;​
  9. ​    },​
  10. ​  }​
  11. ​}​

解决方法二:经过评论的提醒,遗漏了利用 ​debounce​防抖实现防二次点击操作。

  1. ​export default {​
  2. ​    mounted() {​
  3. ​        // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。​
  4. ​        this.debouncedSaveForm = _.debounce(​
  5. ​            this.onSubmit, // 回调函数​
  6. ​            500, // 时间窗口的间隔​
  7. ​            {​
  8. ​                leading: true, // 点击立即执行​
  9. ​                trailing: false // 延迟时间过后不执行​
  10. ​            }​
  11. ​        );​
  12. ​    },​
  13. ​    methods: {​
  14. ​        onSubmit () {​
  15. ​            if (this.isCommitting) return;​
  16. ​            this.isCommitting = true;​

  17. ​            // 表单验证以及业务请求代码​

  18. ​            this.isCommitting = false;​
  19. ​        },​
  20. ​    }​
  21. ​}​

3. 表单提交或出错时的Loading提示

业务场景:表单提交后没展示Loading导致问题:当请求request较久时,页面像是卡死了,没任何响应,用户体验很差。

业务场景:遇到错误时没隐藏Loading导致问题:当请求request出错时,Loading没关闭,页面流程进行不下去。

解决方案:

  1. ​export default {​
  2. ​  methods: {​
  3. ​     onSubmit () {​

  4. ​        // 容易忽略一:发送请求前,没展示Loading​
  5. ​        this.$loading.show('努力加载中...');​
  6. ​        request('apiUrl', data)​
  7. ​          .then(() => {})​
  8. ​          .catch(err => {​
  9. ​            // 容易忽略二:请求出错时,没隐藏Loading​
  10. ​            this.$loading.hide();​
  11. ​          })​

  12. ​    },​
  13. ​  }​
  14. ​}​

4. 表单重新打开时,要重置表单数据

业务场景:如果表单是属于弹窗 Dialog 内,部分开发为了代码可复用性,新增和编辑是共用同一个表单代码。

导致问题:用户在编辑某一条数据后,再点击新增,会发现新增表单里面的内容是上一条编辑内容的数据。

解决方案:

  1. ​export default {​
  2. ​    mounted() {​
  3. ​        // 页面初始化时,先备份表单数据​
  4. ​        this._bak_form = _.cloneDeep(this.form);​
  5. ​    },​
  6. ​    methods:  {​
  7. ​        onOpenDialog(actionType, tableRowIndex, data) {​
  8. ​            if (actionType === "add") {​
  9. ​                // 新增时,需要恢复为默认数据​
  10. ​                this.form = _.cloneDeep(this._bak_form);​
  11. ​                /**​
  12. ​                * Tips​
  13. ​                * 这里不能用解构 this.form = {...this._bak_form},​
  14. ​                * 不然会导致,改了form里面的(object或array类型)数据,同时会影响到_bak_form的数据​
  15. ​                * 这是因为引用数据类型的指针还是指向同一个地址。​
  16. ​                **/​
  17. ​            } else if (actionType === "edit") {​
  18. ​                // 编辑​
  19. ​                this.tableRowIndex = tableRowIndex;​
  20. ​                this.form = _.cloneDeep(data);​
  21. ​            }​
  22. ​            this.actionType = actionType;​
  23. ​            this.visible = true;​
  24. ​        },​
  25. ​    }​
  26. ​}​

Tips 避免在关闭窗口时恢复为默认数据

  • 造成问题:恢复为默认数据会触发表单校验规则,因此会有显眼的警告“XX不能为空”。
  • 触发原因:与此同时,窗口的visible变为false,假若窗口的隐藏式有过渡效果的话,窗口隐藏需要500ms,而重置表单是立即生效的,用户是会看到一闪而过的红色警告。
  • 解决方法: 一是避免在关闭窗口时恢复为默认数据 二是使用 ​​resetFields​​将所有字段值重置为初始值并移除校验结果(但不能解决点编辑后再点新增时,恢复为默认数据)

5. 不小心点击关闭页面时,要提示让用户确认

业务场景:当用户在填写一个长表单时,手误点了关闭页面或者点击去到其他页面。

导致问题:用户花时间填写的表单数据会丢失,用户又要重新填一遍。用户体验大大降低。

解决方法:

  1. ​export default {​
  2. ​    watch: {​
  3. ​        "visible": value => {​
  4. ​            if (!value) {​
  5. ​                // 当弹窗关闭不涉及表单时,清除事件​
  6. ​                window.onbeforeunload = null;​
  7. ​                return;​
  8. ​            }​
  9. ​            // 当弹窗显示有表单数据时,网页跳转或者关闭时提醒用户​
  10. ​            window.onbeforeunload = e => (e.returnValue = "确定离开当前页面?");​
  11. ​        }​
  12. ​    },​
  13. ​}​

总结

最后,我汇总一下上面5个技巧点的真实场景Demo:

​https://codepen.io/ryqsky​

以上都不是什么新内容,但如果工作中能重视并注意到这些细节问题,就能一次即通关表单开发需求,减少与产品测试反复沟通的时间,加快项目的进度

不然等到产品或测试临下班前发现这些问题时,会出现这样的场景:开发被拖着对着屏幕敲代码修缺陷,测试重新打开缺陷,反复循环N次。

N=上面提到的5个技巧点

中华人民共和国消防法第二条是 ​“消防工作贯彻预防为主,防消结合的方针”​,放在需求开发上也是一样道理:**提前发现提前修复才能避免当代码发生“火灾”时的一团乱。

关注我们

IMWeb 团队隶属腾讯公司,是国内最专业的前端团队之一。

我们专注前端领域多年,负责过 QQ 资料、QQ 注册、QQ 群等亿级业务。目前聚焦于在线教育领域,精心打磨 腾讯课堂 及 企鹅辅导 两大产品。


【JS】183-『表单开发』一次即通关的5个技巧_解决方法