Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

如何做好 Web 项目的表单?

  我个人觉得,Web 项目中最烦人的一块莫过于表单了。把表单搞清楚就可以节省很多工作量。一些前端框架在炒作「双向绑定」的概念试图解决表单复杂的问题。实际上问题并不在于此。表单之所以复杂是因为数据结构没设计好,没有满足「对等原则」就会变成一坨。
  首先,这里说的表单是基于前后端分离的,基于后端渲染的项目就不用往下看了。

对等原则

  POST、PUT 以及 GET 所操作的数据都应该是对等的。举个非常常见的例子吧,假如有一个表单页面上有图片上传的功能,前端实现是使用 form-data 把图片数据和其它表单项一起 POST 过去的。如果这个数据将来再也不会在前端更新和读取,那这个表单就完全是单向的,确实没有「对等」可言。可如果数据是需要后期再编辑的,那么在用户进入编辑页时从服务器取的数据也应该完全和之前 POST 过去的结构对等。然而之前是 form-data 传图片,HTTP 客户端并不擅长解析同样是 form-data 的数据,所以这个方案是有问题的。
  正确的姿势应该是在别的地方把图片传好,这个表单只提交一个图片的 URL 而已。或者如果来不及做图片上传的功能,可以把图片在前端 base64 后塞进 JSON 中上传,GET 时服务器把这个图片从这个接口响应回来,这也是满足「对等原则」的。

控件模块化

  满足对等原则是最基本的,但如果仅是如此还会遇到一些很难处理的问题。比如表单是一个结构化的数据,前端的控件如何和数据对应起来呢?再举个例子吧,现在的需求是有一个可以无限嵌套的字符串数组,要为其设计表单。一个摆在眼前的问题就是,INPUT 控件的 name 应该是什么?
  现在很多 MV* 框架的做法都是直接把数据和 DOM 绑定的,再加上双向绑定,确实可是解决这个问题。但这并不是我想要的最终方案。假如一个表单的数据结构非常复杂,可能有几十个字段,每一个字段都是一个数形结构,而且它们是不同类型的树。如果把这么复杂的东西直接在表单这个业务层搞个双向绑定来解决,那么单个文件的代码量会变得很多,而且将来会很难维护。
  其实表单就应该是扁平的,结构化只是数据的结构。理想的表单是,提交时只要找到所有带 name 属性的元素,取出其 name 和 value,放入一个 JSON 中 POST 到服务器(当然其中还有表单验证)。编辑时同样也是找到所有带 name 属性的元素,把从服务器取到的一坨字段依次赋于他们的 value 属性。至于字段里面是数组还是树甚至其他更加奇葩的结构,要如何处理交互逻辑,如何和 INPUT 绑定等,那都是控件模块应该做的事情,不要让表单本身去做。

表单验证

  前端的表单验证有很多处理方式。比如在编辑时实时验证、失去焦点后验证、点击提交后验证。其实无论是何种,这些验证都是针对一个交互控件的,所以在每个基本控件上自己做好验证就行。
  我以前也纠结这个验证应该怎么做,认为它是对数据的校验。在后端程序中确实是数据校验,但是前端的表单验证不是。如果前端也把表单验证这件事从数据层面来处理就会遇到一些问题。比如你确实检测出了数据是不合法的,但你找不到这个数据的来源了,无法把错误信息显示到具体出错的控件后面告诉用户具体什么地方错了。
  我建议的做法是,在获取表单控件的值时,控件内部去做这个校验,具体的业务校验规则可以在 HTML 标签上通过属性告诉它。如果试图获取一个校验失败的控件上的 value,那么可以设计为得到一个 undefined 或直接抛出异常,在取的时候自己捕获。表单只需要处理这些异常的值,然后阻止提交即可。

网名:
34.203.245.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^