网站申请名称和域名万州网站建设
如果我要说的是大多数JavaScript代码库都在努力的一件事,那通常是紧密耦合的,尤其是与DOM的耦合。 紧密耦合会导致开发人员头痛并在单元测试代码时遇到问题。
在这个由两部分组成的系列文章中,我将向您提供一些有关如何实现松散耦合代码的提示,并引导您完成一个如何将代码与DOM分离的示例。 在第一部分中,我将向您介绍具有紧密耦合的代码的问题,并且还将介绍一种实际情况,在这种情况下我们可以应用所讨论的概念:表单验证。
什么是联轴器?
在许多应用程序中,代码与各种外部API进行交互。 在Web应用程序中,我们与DOM API交互,可能与网络交互(通过XMLHttpRequest),用于数据交换的JSON或XML等。 从概念上讲,这些关注点是严格分开的。
如果您的应用与之交互的REST API进行了一些结构上的更改,则合理的是,您需要更新与REST服务交互的代码。 这要求更改UI呈现代码是不合理的。 但是,经常会这样。 发生这种情况时,您将拥有所谓的“紧密耦合”。
松耦合与紧耦合相反。 在松耦合的系统中,不断变化的网络要求不会导致渲染代码发生变化。 改进的CSS样式表和类名称的新规则不会导致数据序列化代码的更改。 这意味着更少的问题,而且代码基础也更容易推理。
现在,我为您提供了一些背景信息,让我们看一下这在实践中意味着什么。
表格验证
表单验证也许是用JavaScript棒击败过的最致命的一匹马。 它是JavaScript最古老的用例之一,已经被开源库解决了无数次,更不用说引入了HTML5属性,例如required
和pattern
。 但是,仍然会弹出新的库,指示:
- 我们没有创建正确的抽象,从而导致不断需要重写。
- JavaScript开发人员真的很喜欢重新发明轮子(并将结果作为开源软件发布)。
对于后者,我真的不能帮忙,但是我当然希望对前者有所帮助,即使我自己为已经存在的混乱做出了贡献。
表单验证在许多方面都“接近” DOM。 我们正在针对form
的当前状态测试一组期望值,然后通过对DOM进行更改来向用户报告。 但是,如果退后一步,我们可以轻松想象一些涉及DOM程度较小的相关用例:
- 将验证报告发送到分析系统,以了解如何改进站点设计
- 验证通过网络获取的数据
- 验证从拖动到浏览器的文件中的数据
- 使用React之类的库输出验证消息
即使DOM涉及很多,也有多种因素会发生变化:
- 验证何时触发? 何时触发
onsubmit
事件?onblur
?onchange
? 通过JavaScript代码以编程方式进行? - 在整个表单或每个字段报告错误? 都?
- 错误报告标记的详细信息可能相差很大
- 错误报告的需求可能取决于上下文
将输入-验证-输出周期紧密地捆绑在一起,将很难考虑这些东西的所有可能组合。 如果您真的计划得很好,那么您可以做出一个非常灵活的解决方案,但我保证您会发现有人会用到破坏骆驼后背的用例。 相信我,我之前已经走过这条路,跌入了沿途的每个沟渠。
似乎还不够,请考虑以下事实:多种验证规则取决于多个字段。 我们如何解决这些情况? 可以通过首先分析我们需要完成的任务,然后决定如何最好地做到这一点来找到答案:
- 从表单读取数据(以DOM为中心)
- 根据一组规则验证数据(纯业务逻辑)
- 输出验证结果(可能以DOM为中心)
此外,我们需要一薄层代码,将这些片段组合在一起并在所需时间触发验证。 也许还有更多方面需要考虑,但是只要我们能够将这些方面实现为正交关注点,我们就应该能够相对轻松地分层到此抽象上。
验证数据
任何验证库的核心是其验证功能集。 这些功能应该适用于任何数据,而不仅仅是表单元素。 毕竟,区别强制执行表单中的name
字段与强制显示对象的name
属性存在的唯一区别是我们访问值的方式。 验证逻辑本身是相同的。 因此,明智的做法是将验证器功能设计为使用纯数据,然后提供不同的机制来提取值以分别通过验证器运行。 这也意味着我们的单元测试可以使用简单的JavaScript对象,这很容易实现。
我们的验证者应该期望什么输入? 我们将需要为各个字段指定规则(以及复合规则,稍后再介绍),并且将上下文错误消息与每个检查相关联将非常有帮助。 所以像这样:
var checkName = required("name", "Please enter your name");
required
函数返回一个将检查所有数据并查找name
的函数。 可以这样称呼:
var result = checkName({name: 'Chris'});
如果提供给函数的数据通过了检查,则返回undefined
。 如果失败,函数将返回一个描述问题的对象:
// returns {id: "name", msg: "Please enter your name"}
checkName({});
可以“在另一端”使用此数据,例如将消息呈现到表单上。
为了实现此功能,让我们制定一个测试:
describe('required', function () {it('does not allow required fields to be blank', function () {var rule = required('name', 'Name cannot be blank');assert.equals(rule({}), {id: 'name',msg: 'Name cannot be blank'});});
});
该函数检查一个非空值:
function required(id, msg) {return function (data) {if (data[id] === null ||data[id] === undefined ||data[id] === '') {return {id: id, msg: msg};}};
}
虽然调用单个验证函数很简单,但我们的主要用例是验证完整表单。 为此,我们将使用另一个函数,该函数将采用一组规则 (由各种验证器函数产生)并将它们与数据集进行匹配。 结果将是一系列错误。 如果数组为空,则验证成功。 因此,我们可能会有这样的事情:
var rules = [required("name", "Please enter your name"),required("email", "Please enter your email")
];var data = {name: "Christian"};// [{id: "email", messages: ["Please enter your email"]}]
var errors = enforceRules(rules, data);
请注意,所得的messages
属性是一个数组,因为enforceRules
可能会遇到多个失败于同一属性的规则。 因此,我们必须为每个属性名称考虑多个错误消息。
这看起来像是一个合理的设计:它简单明了,没有外部依赖性,并且不对数据来自何处或结果将要去做任何假设。 让我们尝试一个实现。 我们将从测试开始:
describe('required', function () {it('does not allow required fields to be blank', function () {var rules = [required('name', 'Name cannot be blank')];assert.equals(enforceRules(rules, {}), [{id: 'name', messages: ['Name cannot be blank']}]);});
});
该测试很好地描述了我们计划的设计。 有一个规则数组,一个带有数据的对象,以及一系列错误。 该功能没有副作用。 这种设计有可能幸免于不断变化的需求。
经过更多测试之后,您可能最终会实现一个类似于以下内容的enforceRules
:
function enforceRules(rules, data) {var tmp = {};function addError(errors, error) {if (!tmp[error.id]) {tmp[error.id] = {id: error.id};tmp[error.id].messages = [];errors.push(tmp[error.id]);}tmp[error.id].messages.push(error.msg);}return rules.reduce(function (errors, rule) {var error = rule(data);if (error) {addError(errors, error);}return errors;}, []);
}
至此,我们已经有了一个系统,可以很容易地实现新的验证器。 例如,正则表达式测试在表单验证器中非常常见,可以这样实现:
function pattern(id, re, msg) {return function (data) {if (data[id] && !re.test(data[id])) {return {id: id, msg: msg};}};
}
重要的是要注意,如果有问题的数据为空/不存在,则此验证器将通过。 如果我们在这种情况下失败,验证器将隐式地也是required
检查。 由于我们已经在独立版本中提供了该功能,因此最好允许API用户将它们组合起来以适应他们的需求。
如果您想看到到目前为止创建的代码并付诸实践,请看一下该Codepen 。
结论
在第一部分中,我们讨论了许多表单验证库共有的问题:紧密耦合的代码。 然后,我描述了紧密耦合的代码所带来的缺点,并展示了如何创建不存在此问题的验证函数。
在下一部分中,我将向您介绍复合验证器,以及其他正交问题:从HTML表单收集数据并将错误报告给用户。 最后,我将所有这些放在一起,以提供一个完整的视觉示例,供您使用。
From: https://www.sitepoint.com/thinking-outside-dom-concepts-setup/