本周,Chris Ferdinandi 审查了一个 巧妙的 JavaScript 代码片段,该片段以新语法特性创造性地编写,但可能可读性和性能较差。 它读起来很快,但他对我们行业对巧妙性的迷恋的提醒值得…… 提醒
……作为行业,我们已经痴迷于简洁和巧妙的代码,这导致代码有时性能较差,并且通常难以阅读和理解。
上个月底,当他写关于 代码可读性 时,他也提出了类似的论点,指出简洁可能看起来很酷,但最终会导致代码库出现各种问题。
通常,Web 开发人员痴迷于简洁。 开发人员会尝试用最少的字符数编写相同的函数。
我个人认为简洁毫无意义。 可读性更为重要。
我完全同意 Chris 的观点,但是,我认为需要做出一个重要的区别,那就是原型代码和生产代码之间的区别。 正如 Jeremy Keith 之前论证的那样
有趣的是,在原型设计方面,我们通常的前端优先级可以并且应该抛到脑后。 现在的优先级是速度。 如果这意味着牺牲语义或性能,那就这样吧。 如果我正在构建原型,并且发现自己想“现在,这个组件的正确类名是什么?”,那么我知道我的思维方式错了。 这个问题对于生产代码来说可能是有效的,但对于原型来说是浪费时间。
我同意 Chris 的观点,即在生产环境中,我们应该编写易于阅读的代码。 我也认为,在原型设计方面,以这种方式试验代码是一件好事。 我们永远不应该回避玩代码和稍微推动一下——只要我们不在一个大型的 Web 应用程序中这样做,并且有一组其他开发人员与我们一起工作。
我注意到有些人正在使用 Sass 做出真正巧妙的事情。 我一直坐在那里想,“哇,我以前从未见过这样的东西。” 但是当涉及到必须由数百人同时理解的生产 Web 应用程序时,我不认为当有人查看代码时,这是我们想要的反应。
因此,我一直在尝试编写实际上非常简单的 Sass 代码,几乎看起来很愚蠢。 使代码更简单的一种简单方法是减少嵌套量。
.element {
.heading { ... }
}
当内部有代码时,这看起来不错——而且很容易理解——但是将复杂的设计混合进来(例如,使用伪元素和媒体查询),您突然面前出现了一组相当复杂的规则。 创造力和巧妙性可能更难以扫描和识别您正在寻找的代码的一小部分。 此外,在本例中,我们不必要地使我们的 .heading
类稍微更具体了一些,这可能会鼓励我们在代码库的其他地方以一种不规范的方式覆盖它们。
我们可以编写如下代码
.element { ... }
.element-heading { ... }
我知道这看起来愚蠢地简单,但是这两个类之间的关系更容易看到,并且将来更容易扩展。 将所有这些代码捆绑到一个嵌套类中可能会很快失控。 即使它碰巧看起来更酷。
(顺便说一句,Andy Bell 关于在 Sass 中使用与号的文章——以及由此产生的评论——是创造力和实用性之间冲突的一个很好的例子。)
无论如何,我在这里要说明的重点是,CSS(以及 JavaScript)是一种奇怪的语言,因为您无法对其制定明确的规则。 这实际上都取决于代码库和项目。 但我认为我们可以肯定地说,当我们的代码转移到生产环境时,我们的代码 应该更加无聊。
继续制作狂野和实验性的或不可能奇怪的原型! 代码质量无关紧要。
原型代码随意发挥的问题在于,原型代码经常会变成生产代码。 并且可能无法始终有时间对其进行清理。
同意。 这就是我的生活。
“没有什么比临时解决方案更永久了。”
所以是的,我同意你的观点。 我看到我的原型进入生产环境的次数令人震惊。
我经常处理生产环境中“未完全清理的原型代码”带来的痛苦。 我的观点是,只要您的团队擅长持续重构代码(我的团队就是这样),就可以了。 这会浪费一些时间,但远没有工程师(尤其是初级工程师)在核心问题尚未解决时就尝试编写生产就绪代码所浪费的时间多。
为什么不写
.element {
…
&-heading { … }
}
@Bytebrand
在使用 SMACSS 命名约定时,我使用了这种方法,然后很快放弃了,因为我为每个模块(本例中的 .element)创建了一个部分文件,在一个括号内编写所有内容对我来说毫无意义,因为它降低了我对模块的整体可见性。 所以我更喜欢按照本文所述编写我的 SASS。 不过,我会将修饰符嵌套在相关类中,例如
.element {
&#{–bigger} {
font-size: 20px;
}
}
这让我感到非常舒服,并在扫描文件时节省了我的时间。
为您提供尽可能广泛的空间来向工作代码中添加任何内容,而无需考虑结构和编码标准。 您可以在结果稳定后对其进行排列和修复。
我同意,如果您试图提出逻辑流程,您可以做到简单。 但是,在原型设计时,我始终需要使用生产名称、注释和代码。 如果您建立了一种文化,使这成为可能,那将是很棒的。
我想知道作者是否真的想表达这一点。 改变文化。
我:我不完全赞同这个想法。
这是关于清晰度并远离嵌套,因为它自然会导致更多复杂性。
感谢提及。 需要澄清的一点(我在您链接的第二篇文章中提到了这一点)是,可读性更重要的一个主要原因是您可以让工具为您压缩和优化代码。
当 Uglify 可以获取您提供给它的任何内容并输出大小只有一小部分且对人类完全不可读的内容时,编写简短的代码毫无意义。
我喜欢确保我们的 SCSS 部分始终有一个根选择器。
而不是拥有一个完全平坦的 SCSS 文件(如示例所示)
原因是,它确保添加到部分的样式与该部分相关。 使用平坦的 SCSS 文件,很容易添加与部分整体无关的选择器。 当开发人员赶时间时,他们不会找到放置代码的正确位置,而是将其放在第一个有效的位置,即所有 SCSS 部分都平坦时的任何文件。 使用单个根选择器将所有样式限定在部分负责的组件中,这可以防止添加不相关的样式。
显然,有人可以在该根选择器之外添加样式,但这样在代码审查中会更突出。
我喜欢 Sass 嵌套,因为对我来说它完全可读。 我大量使用嵌套,我不喜欢太多类名。 通常它只需要一个模块类,并且内部的所有标签都可以通过嵌套在此类名中来寻址。
我也不会一行写一个属性——我在一行中包含类及其所有属性。 我习惯了。 对我的眼睛来说,它紧凑而美观,对于生产来说,它是经过压缩的。 你的例子会让我眼睛非常不舒服。
关于为什么巧妙的代码不好,有很多话要说,但是 SCSS 示例未能突出这一点,而且非常具有误导性。
它声称第二个代码示例更简单,因为没有创造力或巧妙性,第二个示例愚蠢地简单,但它们都愚蠢地简单。 它还声称第一个看起来更酷,这完全是主观的,并且无关紧要。
这两个代码示例并不相同。第一个示例会生成“.element .heading {}”,这与“.element-heading {}”非常不同。可以推断,重点是你不应该嵌套选择器,但仅仅通过说我们使选择器更具体了一些,就轻描淡写地提到了这一点。它也带来了新的问题:重命名类变得更加困难,因为现在你必须进行多次替换;你不能将元素从容器中提取出来,而不也对CSS代码进行更改;可能还有其他问题。这些听起来可能像吹毛求疵,但重点是你正在权衡利弊,两者之间并没有客观上的优劣之分。
在我看来,文章中关于“我知道这看起来非常简单,但这两个类之间的关系更容易理解,并且将来更容易扩展”的观点完全错误,事实恰恰相反。如果你使用第二种风格,则无法保证声明的开始或结束位置。你或其他人可能会犯错误,并在中间插入其他组件的其他声明。第一个示例通过其语法保证了这一点,并消除了人为错误的可能性:属于此组件及其子组件的所有样式都位于此处,在{}块之间。你不能放置任何属于此树结构之外的其他组件的样式,因为你必须关闭{}块,并在稍后重新打开它。这增加了清晰度,而不是降低了清晰度。
它也更容易重构:重命名类,或当声明过多时将其移动到另一个文件,这些操作都更容易,并且由于保证的存在,你可以确保你正在移动所有内容,并且没有遗漏任何内容,或者移动了不应该移动的内容。
关于“添加一个复杂的的设计元素(例如,使用伪元素和媒体查询),你突然会看到一套相当复杂的规则”的观点是正确的,但这由你的应用程序/需求变得更大所导致,无论如何复杂性都会存在,嵌套/取消嵌套声明不会对它产生太大影响。
说实话?糟糕的代码就是糟糕的。如果我们总是追求性能曲线的最高点,我们就会被迫完全忽略函数式编程,并且在许多情况下忽略面向对象编程,因为它们相对于优化的过程逻辑而言存在性能损失。
然而,我倾向于认为“可读性”论点有利于人才库的低端。人们虔诚地引用鲍勃大叔的话,然后把话塞进他的嘴里,甚至说连语言特性都是不好的,因为“可读性”。SOLID也以同样的方式被广泛误解。
诸如“减少嵌套”之类的陈述都很好,但哪里有关于圈复杂度的论点呢?就我个人而言,如果我阅读暴露了高阶函数或嵌套调用的代码,而我被迫连续象征性地追踪应用程序的流程,我倾向于发现它不可读,因为概念上的不连续性,即使它是最时尚的做法。
不过,出现的一个问题是,每个人都坚持要快速前进,这往往意味着一个巧妙编写的原型会被演示给领导层,他们没有看到演示或原型,他们看到的是一个他们认为应该明天发布的成品。所有这些巧妙的、不可读的、无法维护的生产代码都进入了生产环境,或者充其量,你只有几周的时间来尝试清理它。
当开发人员赶时间时,他们不会找到放置代码的正确位置,而是会将其放在第一个有效的位置,当所有SCSS部分都是扁平化时,这个位置就是任何文件。使用单个根选择器将所有样式限定到部分负责的组件,从而防止添加无关的样式。