Web 开发一直在变化。最近,一种趋势变得非常流行,它从根本上违背了关于如何制作网页的传统智慧。这对一些人来说很令人兴奋,但对另一些人来说却令人沮丧,而导致这两种结果的原因很难解释。
网页传统上由三个独立的部分组成,它们分别负责:**HTML 代码**定义页面内容的结构和含义,**CSS 代码**定义其外观,**JavaScript 代码**定义其行为。在拥有专门的设计师、HTML/CSS 开发人员和 JavaScript 开发人员的团队中,这种关注点分离与工作角色完美契合:设计师确定页面的视觉效果和用户交互,HTML 和 CSS 开发人员在 Web 浏览器中复制这些视觉效果,JavaScript 开发人员添加用户交互以将所有内容整合在一起并“使它工作”。人们可以专注于一个部分,而无需参与所有三个部分。
近年来,JavaScript 开发人员意识到,通过在 JavaScript 中而不是在 HTML 中定义页面的结构(使用React等框架),他们可以简化用户交互代码的开发和维护,否则这些代码的构建将变得更加复杂。当然,当你告诉某人他们编写的 HTML 需要被分割并与他们一无所知的 JavaScript 混合在一起时,他们可能会(可以理解地)感到沮丧并开始质疑我们到底从中学到了什么。
作为跨职能团队中的 JavaScript 开发人员,我偶尔会遇到这个问题,并且经常难以回答。我发现的所有关于此主题的材料都是针对已经熟悉 JavaScript 的受众编写的,这对那些专注于 HTML 和 CSS 的人来说并不太有用。但是,这种 **HTML-in-JS** 模式(或提供相同好处的其他模式)很可能在一段时间内存在,因此我认为这是参与 Web 开发的每个人都应该理解的重要内容。
本文将包括代码示例,供有兴趣的人参考,但我的目标是通过一种无需代码也能理解的方式解释这个概念。
背景:HTML、CSS 和 JavaScript
为了尽可能扩大本文的受众范围,我想简要介绍一下创建网页所涉及的代码类型及其传统作用。如果您对此有经验,可以直接跳过。
HTML 用于结构和语义含义
HTML(超文本标记语言)代码定义页面内容的结构和含义。例如,本文的 HTML 包含您现在正在阅读的文本,以及它位于段落中,以及它位于标题之后和 CodePen 之前的事实。
假设我们想要构建一个简单的购物清单应用程序。我们可能从一些 HTML 代码开始,如下所示
我们可以将此代码保存到一个文件中,在 Web 浏览器中打开它,浏览器将显示渲染结果。如您所见,此示例中的 HTML 代码表示页面的一部分,其中包含一个标题“购物清单(2 项)”,一个文本输入框,一个标题为“添加项目”的按钮,以及一个包含两项“鸡蛋”和“黄油”的列表。在传统的网站中,用户会导航到 Web 浏览器中的某个地址,然后浏览器会从服务器请求此 HTML 代码,加载并显示它。如果列表中已经存在项目,服务器可以提供包含这些项目的 HTML 代码,就像它们在这个示例中一样。
尝试在输入框中输入一些内容,然后单击“添加项目”按钮。您会注意到什么都没有发生。该按钮没有连接到任何可以更改 HTML 代码的代码,而 HTML 代码无法自行更改。我们稍后会讲到这一点。
CSS 用于外观
CSS(层叠样式表)代码定义页面的外观。例如,本文的 CSS 包含您正在阅读的文本的字体、间距和颜色。
您可能已经注意到,我们的购物清单示例看起来非常简单。HTML 无法指定间距、字体大小和颜色等内容。这就是 CSS(层叠样式表)发挥作用的地方。在与上述 HTML 代码相同的页面中,我们可以添加 CSS 代码来稍微美化一下
如您所见,此 CSS 更改了字体大小和权重,并为该部分提供了不错的背景颜色(设计师,请不要 @ 我;我知道这仍然很难看)。开发人员可以编写这样的样式规则,并且它们将一致地应用于任何 HTML 结构:如果我们在该页面中添加更多 <section>
、<button>
或 <ul>
元素,它们将应用相同的字体更改。
但是,该按钮仍然不起作用:这就是 JavaScript 的作用。
JavaScript 用于行为
JavaScript 代码定义页面上交互式或动态元素的行为。例如,本文中嵌入的 CodePen 示例由 JavaScript 提供支持。
没有 JavaScript,要使示例中的“添加项目”按钮正常工作,就需要使用特殊的 HTML 代码来使其将数据提交回服务器(<form action="...">
,如果您好奇的话)。然后浏览器将丢弃整个页面并重新加载整个 HTML 文件的更新版本。如果此购物清单是更大页面的一个部分,用户正在执行的任何其他操作都会丢失。向下滚动了吗?您回到了顶部。正在观看视频吗?它重新开始。这就是所有 Web 应用程序在很长一段时间内的工作方式:每当用户与网页交互时,就好像他们关闭了 Web 浏览器并再次打开了它一样。对于这个简单的示例来说,这没什么大不了,但对于一个大型复杂的页面(可能需要一段时间才能加载),这对浏览器或服务器来说效率不高。
**如果我们想要在网页上进行任何更改而无需重新加载整个页面,就需要 JavaScript**(不要与 Java 混淆,Java 是一种完全不同的语言……不要让我开始讲这些)。让我们尝试添加一些
现在,当我们在框中输入一些文本并单击“添加项目”按钮时,我们的新项目将添加到列表中,并且顶部的项目计数也会更新!在实际应用中,我们还会添加一些代码,以便在后台将新项目发送到服务器,以便下次加载页面时它仍然会显示出来。
在这个简单的示例中,将 JavaScript 与 HTML 和 CSS 分开是有意义的。传统上,甚至更复杂的交互也会以这种方式添加:HTML 被加载并显示,JavaScript 随后运行以添加内容并更改它。但是,随着事情变得更加复杂,我们开始需要更好地跟踪 JavaScript 中的内容。
如果我们继续构建这个购物清单应用程序,接下来我们可能会添加用于编辑或从列表中删除项目的按钮。假设我们编写了用于删除项目的按钮的 JavaScript 代码,但忘记添加更新页面顶部项目总数的代码。突然之间,我们遇到了一个错误:用户删除项目后,页面上的总数与列表不符!一旦我们注意到这个错误,我们就通过将“添加项目”代码中的相同 totalText.innerHTML
行添加到“删除项目”代码中来修复它。现在,我们在多个地方重复了相同的代码。稍后,假设我们想要更改该代码,以便页面顶部不再显示“(2 项)”,而是显示“项目:2”。我们将不得不确保在所有三个地方更新它:在 HTML 中,在“添加项目”按钮的 JavaScript 中,以及在“删除项目”按钮的 JavaScript 中。如果我们没有这样做,我们就会遇到另一个错误,即该文本在用户交互后突然更改。
在这个简单的示例中,我们已经可以看出这些事情很快就会变得混乱。有一些方法可以组织我们的 JavaScript 来使这种问题更容易处理,但随着事情变得更加复杂,我们将需要不断地重构和重写代码来跟上。只要 HTML 和 JavaScript 保持分离,就可能需要花费很多努力来确保它们之间的同步。这就是为什么像 React 这样的新 JavaScript 框架获得了吸引力的原因之一:它们旨在显示 HTML 和 JavaScript 之间的关系。要了解它是如何工作的,我们首先需要了解一点计算机科学知识。
两种编程方式
这里要理解的关键概念涉及两种常见编程风格之间的区别。(当然,还有其他编程风格,但我们这里只讨论其中两种。)大多数编程语言都适合其中一种风格,而有些语言则可以在两种风格中使用。为了从 JavaScript 开发人员的角度理解 HTML-in-JS 的主要优势,掌握这两种风格非常重要。
- 命令式编程:这里的“命令式”意味着命令计算机执行某项操作。命令式代码行很像英语中的命令句:它向计算机发出一个特定的指令。在命令式编程中,我们必须告诉计算机如何执行我们需要它执行的每一件事。在 Web 开发中,这开始被认为是“旧方法”,并且是使用原生 JavaScript 或像 jQuery 这样的库时所做的事情。上面购物清单示例中的 JavaScript 就是命令式代码。
- 命令式:“执行 X,然后执行 Y,然后执行 Z”。
- 示例:当用户选择此元素时,向其添加
.selected
类;当用户取消选择时,从其移除.selected
类。
- 声明式编程:这是高于命令式编程的更抽象一层。我们不是向计算机发出指令,而是“声明”我们希望计算机执行某项操作后的结果。我们的工具(例如 React)会自动找出“如何”执行。这些工具内部使用命令式代码构建,我们不需要从外部关注它们。
- 声明式:“结果应该是 XYZ。执行任何需要做的操作来实现这一点。”
- 示例:如果用户选择了此元素,则它具有
.selected
类。
HTML 是一种声明式语言
暂时忘掉 JavaScript。以下是一个重要的事实:HTML 本身是一种声明式语言。在 HTML 文件中,您可以声明类似以下内容
<section>
<h1>Hello</h1>
<p>My name is Mike.</p>
</section>
当 Web 浏览器读取此 HTML 时,它会为您找出这些命令式步骤并执行它们
- 创建一个 section 元素
- 创建一个级别为 1 的标题元素
- 将标题元素的内部文本设置为“Hello”
- 将标题元素放置到 section 元素中
- 创建一个段落元素
- 将段落元素的内部文本设置为“My name is Mike”
- 将段落元素放置到 section 元素中
- 将 section 元素放置到文档中
- 在屏幕上显示文档
作为一名 Web 开发人员,浏览器如何执行这些操作的细节无关紧要;重要的是它确实执行了它们。这是这两种编程风格之间区别的完美示例。简而言之,HTML 是一种声明式的抽象,它包装在 Web 浏览器的命令式显示引擎中。它负责“如何”,因此您只需要担心“什么”。您可以享受用声明式 HTML 编写代码的乐趣,因为 Mozilla、Google 或 Apple 的优秀人员在构建您的 Web 浏览器时为您编写了命令式代码。
JavaScript 是一种命令式语言
我们已经在上面的购物清单示例中看到了命令式 JavaScript 的简单示例,并且我提到了应用程序功能的复杂性如何对实现它们所需的努力和该实现中出现错误的可能性产生连锁反应。现在让我们看一个稍微复杂一点的功能,并看看如何使用声明式方法来简化它。
想象一个包含以下内容的网页
- 一个带标签的复选框列表,每行在选中时会变为不同的颜色
- 底部有“1 of 4 selected”之类的文本,当复选框发生变化时应更新
- 一个“Select All”按钮,如果所有复选框都已选中,则该按钮应被禁用
- 一个“Select None”按钮,如果所有复选框都未选中,则该按钮应被禁用
以下是使用纯 HTML、CSS 和命令式 JavaScript 实现此功能的方法
这里没有太多 CSS 代码,因为我使用了很棒的 PatternFly 设计系统,它为我的示例提供了大多数 CSS。我在 CodePen 设置中导入了它们的 CSS 文件。
所有的小细节
为了使用命令式 JavaScript 实现此功能,我们需要向浏览器发出多个细粒度的指令。以下是用英语描述的上面示例中代码的等效内容
- 在我们的 HTML 中,我们声明了页面的初始结构
- 有四个 row 元素,每个元素包含一个复选框。第三个框被选中。
- 有一些摘要文本,内容为“1 of 4 selected”。
- 有一个“Select All”按钮,该按钮已启用。
- 有一个“Select None”按钮,该按钮已禁用。
- 在我们的 JavaScript 中,我们编写了关于发生以下事件时要更改什么的指令
- 当一个复选框从未选中变为选中时
- 找到包含复选框的 row 元素,并向其添加
.selected
CSS 类。 - 找到列表中的所有复选框元素,并计算选中和未选中的复选框数量。
- 找到摘要文本元素,并使用选中的数量和总数更新它。
- 找到“Select None”按钮元素,如果它已禁用,则启用它。
- 如果所有复选框现在都已选中,则找到“Select All”按钮元素并禁用它。
- 找到包含复选框的 row 元素,并向其添加
- 当一个复选框从选中变为未选中时
- 找到包含复选框的 row 元素,并从其移除
.selected
类。 - 找到列表中的所有复选框元素,并计算选中和未选中的复选框数量。
- 找到摘要文本元素,并使用选中的数量和总数更新它。
- 找到“Select All”按钮元素,如果它已禁用,则启用它。
- 如果所有复选框现在都未选中,则找到“Select None”按钮元素并禁用它。
- 找到包含复选框的 row 元素,并从其移除
- 当单击“Select All”按钮时
- 找到列表中的所有复选框元素,并选中它们。
- 找到列表中的所有 row 元素,并向它们添加
.selected
类。 - 找到摘要文本元素并更新它。
- 找到“Select All”按钮并禁用它。
- 找到“Select None”按钮并启用它。
- 当单击“Select None”按钮时
- 找到列表中的所有复选框元素,并取消选中它们。
- 找到列表中的所有 row 元素,并从它们移除
.selected
类。 - 找到摘要文本元素并更新它。
- 找到“Select All”按钮并启用它。
- 找到“Select None”按钮并禁用它。
- 当一个复选框从未选中变为选中时
哇,很多吧?嗯,我们最好记住为所有这些事情编写代码。如果我们忘记或弄乱了任何这些指令,最终会导致错误,例如总数与复选框不匹配,或者单击时没有执行任何操作的按钮已启用,或者行最终显示了错误的颜色,或者其他我们没有想到的事情,直到用户抱怨才会发现。
这里最大的问题是没有单一事实来源,用于应用程序的状态,在这种情况下是“哪些复选框被选中?”当然,复选框知道它们是否被选中,但是,行的样式也必须知道,摘要文本也必须知道,每个按钮也必须知道。此信息的五个副本分别存储在整个 HTML 中,并且当它在这些地方中的任何一个地方发生变化时,JavaScript 开发人员需要捕获它并编写命令式代码以保持其他副本同步。
这仍然只是一个页面的小组件的简单示例。如果这听起来很麻烦,想象一下当您需要以这种方式编写整个应用程序时,应用程序会变得多么复杂和脆弱。对于许多复杂的现代 Web 应用程序来说,它不是一个可扩展的解决方案。
迈向单一事实来源
像 React 这样的工具允许我们以声明式的方式使用 JavaScript。正如 HTML 是包装在 Web 浏览器显示指令中的声明式抽象一样,React 是包装在 JavaScript 中的声明式抽象。
还记得 HTML 如何让我们专注于页面的结构,而不是浏览器如何显示该结构的细节吗?嗯,当我们使用 React 时,我们可以再次专注于结构,通过基于存储在单一位置的数据来定义结构。当该事实来源发生变化时,React 将自动为我们更新页面的结构。它会在幕后处理命令式步骤,就像 Web 浏览器对 HTML 所做的那样。(虽然这里 React 被用作示例,但这个概念并非 React 独有,而是被其他框架(例如 Vue)使用。)
让我们回到上面示例中的复选框列表。在这种情况下,我们关心的真相很简单:哪些复选框被选中?页面上的其他细节(例如摘要显示的内容、行的颜色、按钮是否启用)是根据该相同真相推断出来的效果。那么,为什么它们需要拥有自己的信息副本?它们应该只使用单一事实来源作为参考,页面上的所有内容都应该“自动知道”哪些复选框被选中,并相应地进行操作。您可能会说 row 元素、摘要文本和按钮应该能够自动响应复选框被选中或取消选中。(你明白我的意思吗?)
告诉我你想要什么(你真正想要的)
为了用 React 实现这个页面,我们可以用一些简单的声明来代替列表。
- 有一个名为
checkboxValues
的真假值列表,它表示哪些框被选中。- 例如:
checkboxValues = [false, false, true, false]
- 这个列表表示事实:我们有四个复选框,第三个复选框被选中。
- 例如:
- 对于
checkboxValues
中的每个值,都有一个行元素,它- 如果值为真,则具有名为
.selected
的 CSS 类,并且 - 包含一个复选框,如果值为真,则选中复选框。
- 如果值为真,则具有名为
- 有一个摘要文本元素,它包含文本“
{x}
of{y}
selected”,其中{x}
是checkboxValues
中真值的个数,而{y}
是checkboxValues
中值的总数。 - 有一个“全选”按钮,如果
checkboxValues
中存在任何假值,则启用。 - 有一个“全不选”按钮,如果
checkboxValues
中存在任何真值,则启用。 - 单击复选框时,它在
checkboxValues
中更改其对应值。 - 单击“全选”按钮时,它将
checkboxValues
中的所有值设置为真。 - 单击“全不选”按钮时,它将
checkboxValues
中的所有值设置为假。
你会注意到最后三项仍然是命令式指令(“当发生这种情况时,执行那件事”),但这是我们唯一需要编写的命令式代码。只有三行代码,它们都更新了单一的事实来源。其余的项目是声明(“有一个……”),现在已经直接内置到页面的结构定义中。为了做到这一点,我们用 React 提供的一种特殊的 JavaScript 语法 JSX 来编写元素,它类似于 HTML,但可以包含 JavaScript 逻辑。这使我们能够将“if”和“for each”之类的逻辑与 HTML 结构混合在一起,因此结构可以根据 checkboxValues
的内容在任何时间点而有所不同。
以下是上面提到的相同购物清单示例,这次使用 React 实现。
JSX 绝对很奇怪。我第一次遇到它时,感觉不对劲。我的第一反应是,“这是什么?HTML 不应该出现在 JavaScript 中!”我不孤单。也就是说,它不是 HTML,而是 装扮成 HTML 的 JavaScript。它也非常强大。
还记得上面那份 20 个命令式指令的列表吗?现在我们只有三个。为了将 HTML 定义在 JavaScript 中,其余的指令都是免费的。React 只要 checkboxValues
发生变化,就会自动完成这些操作。
使用此代码,摘要与复选框不匹配、行的颜色错误或按钮在应该禁用时启用这些错误现在不可能发生。现在我们应用程序中有一类错误是不可能发生的:事实来源不同步。所有内容都从单一的事实来源向下流动,我们开发人员可以编写更少的代码,并且可以睡得更香。至少,JavaScript 开发人员可以…
这是一个权衡
随着 Web 应用程序变得越来越复杂,维护 HTML 和 JavaScript 之间的经典关注点分离的成本越来越高。HTML 最初是为静态文档设计的,为了在这些文档中添加更复杂的交互式功能,命令式 JavaScript 必须跟踪更多内容,并变得更加混乱和脆弱。
好处:可预测性、可重用性和组合性
使用单一事实来源的能力是这种模式最重要的优势,但权衡也为我们带来了其他好处。将页面的元素定义为 JavaScript 代码意味着我们可以将它的块转换成可重用组件,从而防止我们在多个地方复制粘贴相同的 HTML。如果我们需要更改组件,我们可以在一个地方进行更改,它将在应用程序中的所有地方(或在多个应用程序中,如果我们将可重用组件发布到其他团队)更新。
我们可以将这些简单的组件组合在一起,就像乐高积木一样,创建更复杂、更有用的组件,而不会使它们难以使用。而且,如果我们使用他人构建的组件,我们可以轻松地在他们发布改进或修复错误时更新这些组件,而无需重写我们的代码。
缺点:它一直都是 JavaScript
所有这些好处都伴随着成本。人们重视将 HTML 和 JavaScript 分开是有充分理由的,为了获得这些好处,我们需要将它们合并成一个。正如我之前提到的,从简单的 HTML 文件迁移会使以前不需要担心 JavaScript 的人员的工作流程变得复杂。这可能意味着以前可以独立更改应用程序的人员现在必须学习额外的复杂技能来保持这种自主性。
也可能存在技术方面的缺点。例如,一些工具(如 linter 和解析器)期望使用常规 HTML,一些第三方命令式 JavaScript 插件可能变得更难使用。此外,JavaScript 不是设计最好的语言;它只是我们恰好在 Web 浏览器中拥有的语言。较新的工具和功能正在使它变得更好,但它仍然存在一些在有效使用之前需要了解的缺陷。
另一个潜在问题是,当页面的语义结构被分解成抽象组件时,开发人员很容易停止考虑最后生成的实际 HTML 元素是什么。特定的 HTML 标签(如 <section>
和 <aside>
)具有特定的语义含义,这些含义在使用通用标签(如 <div>
和 <span>
)时会丢失,即使它们在页面上视觉上看起来相同。这对可访问性尤为重要。例如,这些选择会影响屏幕阅读器软件为视障用户执行的操作。这可能不是最令人兴奋的部分,但 JavaScript 开发人员应该始终记住,语义 HTML 是 网页最重要的部分。
如果它对你有帮助,就使用它,而不是因为它“现在很流行”
开发人员在每个项目中都使用框架已成为一种趋势。有些人认为将 HTML 和 JavaScript 分开已经过时,但这并不正确。对于一个不需要太多用户交互的简单静态网站来说,这并不值得麻烦。更热情的 React 粉丝可能会不同意我的观点,但如果你的所有 JavaScript 都只是在创建一个非交互式网页,那么你不应该使用 JavaScript。JavaScript 的加载速度不如常规 HTML 快,因此,如果你没有获得明显的开发体验或代码可靠性改进,那么它会弊大于利。
你也不必在 React 中构建整个网站!或者 Vue!或者其他任何东西!许多人不知道这一点,因为所有教程都展示了如何在整个网站中使用 React。如果你在一个简单的网站上只有一个复杂的微件,你可以将 React 用于那个组件。你并不总是需要担心 webpack 或 Redux 或 Gatsby 或者其他人们会告诉你对 React 应用程序“最佳实践”的任何其他内容。
对于足够复杂的应用程序来说,声明式编程绝对值得付出努力。它是一种改变游戏规则的技术,它赋予了世界各地的开发人员能力,让他们充满信心地构建出令人惊叹、健壮且可靠的软件,而不必担心琐碎的事情。React 本身是解决这些问题的最佳方案吗?不是的。它会被下一代技术取代吗?最终会。但声明式编程不会消失,下一代技术可能会做得更好。
我听说过 CSS-in-JS,那是什么?
我不会触碰那个。
我是一名全栈开发人员,但有些人被称为前端设计师,他们对 JavaScript 一无所知,将 JavaScript 与 HTML 混合在一起让他们感到沮丧。
虽然像 Vue 和 React 这样的框架会自动更新很多东西,但如果开发人员忘记编程某些东西,就会出现错误;框架不能自动修复所有问题。因此,框架对于快速开发很有用,但说——我们可以在一个地方进行更改,它会在我们的应用程序中的所有地方更新(使用前端框架时)——可能有些夸张。
此外,最好在公开访问的(无需登录)页面中将 HTML 和 JavaScript 分开;在依赖用户交互的实时更新的动态页面中使用前端框架。
开发人员需要明智地选择和使用这些框架。我完全同意你在“如果它对你有帮助就使用它,而不是因为它‘现在很流行’ ”部分中的信息。
绝对!开发人员仍然需要注意,你仍然可以遇到很多种错误。我只是说像这样的工具可以定义和使用单一的事实来源。开发人员仍然必须利用它并坚持它。
这是一篇写得很好文章。
引人入胜的阅读。
我想补充一下,作为一个依赖命令式编程的相当前端的开发人员,我完全理解迈克在这篇文章中要表达的目的或重点。
这篇文章是关于人们对这些新框架的认识、人们开始使用它们的原因以及它们的优点,当然还有可能不那么令人喜欢的方面。
迈克继续告诉我们,什么时候以及在什么地方使用像 react 这样的东西是有用的,以及在什么地方是多余的或完全过度的。
那么我想知道,如果我没有花很多时间玩原生的 js 和 jquery,尝试用这些语言做一些新东西,学习我可以用这两者做到的但我以前认为永远不可能做到的东西,我是否浪费了宝贵的时间,是否应该‘跳入’到 js 框架的游泳池。
问题是,我目前还没有任何使用它们的理由。在我使用它们之前,我会保持 html5+css3+javascript & jquery+php 和 mysql
我相信时机成熟的时候,我会很乐意跳到下一个大框架,哈哈。
真可惜你浪费了时间。还好你还有足够的时间写一篇冗长的评论!
写得很好,谢谢迈克。
“我听说过 CSS-in-JS?”
我不会碰那个的。”
很高兴你喜欢!哈哈,是的,那是另一天的主题:) 我还没有完全理解它。
我是一家荷兰初创公司的技术主管,也是一家巴基斯坦科技公司的工程主管。
我要在这里提出一个有效的观点,但在此之前,我需要证明一些东西,我的立场和我的位置。从技能上来说,我是一名云与解决方案架构师、计算机系统工程师、计算机架构师、项目与产品经理、物联网专家、全栈开发人员、机器人专家和图形设计师。
现在,首先我认为这篇文章是在比较服务器端渲染,或审查 ejs,或 nest 和静态 HTML 模板,但它不是。
然后我以为它可能是关于 HTML5、它的 API 以及它赋予 Web 开发人员的强大功能以及可用于使用它们的 JS 包装器,但它不是。
请耐心听我讲,因为这篇文章浪费了我的时间,但并非完全无用,但它不是标题所暗示的内容。
相反,它是在比较 JSX 和 HTML。这篇文章将 HTML 描述为一种声明式语言。我的朋友,SQL 是一种声明式语言。HTML 只是标记,它是一种标记语言。先不谈 HTML5。如果你谈论的是内联函数,那你必须研究一下浏览器的运行机制以及它们如何渲染网页。
JSX 不是 HTML,它只是一个花哨的新功能,它也可以在 React 中使用,你可能可以通过导入 babel、设置 ts 配置和调整你的 linter 来使用它。
最好不要发布标题过于吸引人的文章,只是为了获得更多的浏览量。PS,这个页面是 CSS 技巧。我想应该是前端 Web 开发人员组成的吧?
你不需要在 js 中包含 html 来简化应用程序开发。像 Vue 和 svelte 这样的框架不需要这种抽象,对于很多新手来说,它们更容易理解,因为它们更接近于纯 html/css/ks
没错,这不是解决这些问题的唯一方案,也不是最好的方案。我只想解释为什么你会这样做。从我看到的 Vue 和 Svelte 来说,它们也很优雅,可能更容易理解(取决于你如何使用它们),但它们也有自己的缺点。Vue 的自定义属性和模板字符串使其也不是真正的 HTML,只是不同的抽象。Svelte 就像一个黑盒子,我认为在调试时我可能很难理解它。
不过,在过多地批评它们之前,我需要学习更多关于它们的知识,我绝对是以 React 的经验为基础的,将它与使用 jQuery 等东西的经验进行比较。我可能会决定使用 Vue 或 Svelte,而不是使用 React:) 谢谢你的观点!
如果你以一种特定且一致的方式使用 Web Components,并为你的用户维护一个文档齐全的库,那么你可以像发明它那样使用 HTML:用于定义内容及其结构。
我认为最终 Web Components 将统治前端开发。
我认为你关于 Web Components 的说法可能是正确的,我正在关注它们。不过,目前它们似乎还不够成熟,不能满足我的需求。我们会做到的!
阅读这篇文章时,这正是我的想法。作者列出的问题在 React/JSX 中是真实存在的,但 Vue.js 仍然保持了一些关注点的分离。在我的公司,设计师通常编写 HTML/CSS,在采用框架来现代化我们的堆栈时,我们选择使用 Vue.js,因为学习曲线对设计师来说会稍微平缓一些。
这些问题怎么样
后端:对于 web 应用程序,你仍然需要一个完全独立的层,例如,保存到数据库、存储和获取配置等。通过迁移到 React,我们没有简化任何这些工作。
工作流程:纯 HTML/CSS/JS 不需要在我的本地笔记本电脑或我的商品化 web 服务器上进行太多设置。即使是 LAMP 堆栈也是相当商品化的。我可以使用任何我想要的工具(文本编辑器等)。但要开始使用 React,我需要处理(至少)Node、依赖管理、快速版本更改……
团队合作:React 风格组件的一个好处是,团队可以一起工作,而不会把事情搞得一团糟。但它也需要很多强制性的协议——这个文本编辑器、那个版本、这个供应商、那个插件。这就是在 HTML 时代之前的情况,当时 Mac 和 Windows 几乎无法交换文件。这就是为什么“入职”现在是如此热门的词汇:它不仅仅意味着“了解代码库是如何工作的”,还意味着“以这种确切的方式设置你的环境”。
我同意你对 React 有用的说法,如果你正在构建一个“应用程序”——但在“应用程序”盛行的今天,感觉老式的网页已经被降级为次要问题。
在我看来,像谷歌和 Facebook 这样的公司在它们定义的特定基础设施和工作方式普及时,就会从中受益。这是典型的锁定。
谢谢你的这篇有见地的文章。
很高兴你喜欢这篇文章!我认为我同意你在这里所说的很多东西,但有一些
后端:没错,迁移到 React 并不能解决任何后端问题,我试图进行的比较是前端特定的。但在我看来,如果你不在服务器上渲染 HTML,那么这很好地将文档的表示从 API/业务逻辑中分离出来,让后端只负责通过一个不错的 REST API 或 GraphQL 等提供这些东西。如果你愿意,你仍然可以使用工具来服务器端渲染你的 React,以提高性能和 SEO,虽然我承认这可能有点困难。
工作流程:对于你可能在外面找到的大多数“React 和朋友”项目来说,你是完全正确的。但是,打包程序并不是严格要求的:你可以在普通的静态 HTML+JS 中使用 React(甚至 JSX),使用 `<script>` 标签,就像 jQuery 一样。你不会获得打包程序的好处,但你可以用这种方式快速完成原型,或者向一个原本静态的页面添加一个小型的 React 小部件,不需要 webpack:https://reactjs.ac.cn/docs/add-react-to-a-website.html
团队合作:这绝对是这一切的一个合法问题,但我认为这并不是像 React 这样的东西特有的问题。一个有组织的代码库一直以来都是一个难以维护跨协作者的问题。
所有这些都是合理的观点,但事情永远不会如此简单明了。
“但要开始使用 React,我需要处理(至少)Node、依赖管理、快速版本更改……”
是的,对我来说,这是学习这些工具的最大障碍。我的工作站上有 IIS 和 Apache 服务器,还有数据库、Python、PHP 等,所有这些都比我读过的关于 Node、包管理器以及这些新框架附带的所有其他开销更容易理解如何安装和配置。(如果有人可以推荐一个关于如何在 Windows 工作站上设置所有这些的良好教程,那就太好了。)
但我读过关于 React 或所有其他不断涌现的框架的文章时,脑海中更大的问题是,“为什么?”在读这篇文章之前,这听起来像是增加了许多额外的复杂性,而没有太多好处。或者可能是作者在 React 完全过度使用的情况下使用 React,正如其他一些海报所暗示的那样。现在我明白了,或者至少比以前更明白了。谢谢,迈克。
只是另一个我知道 React 所以我比你更神圣的态度。即使是谷歌在 2020 年也遇到了 JS 渲染问题,所以你在电子商务网站上使用 JS 会给自己带来不便。谁能帮我在这里发布小脑 vs 大脑的表情包。
我的文章中哪里暗示我比你更神圣?如果我让你有这种感觉,我表示歉意。我只是想解释一下为什么你可能会使用它的原因。它绝对有缺陷。SEO 是一个大问题,如果你遇到了这个问题,我认为这属于“如果它对你有帮助就使用它”。
这……一点也不。整篇文章中没有任何自鸣得意或难以理解的观点。
这并不新鲜……Blade = HTML-in-PHP,ERB = HTML-in-Ruby,jinja = HTML-in-Python,JSX = HTML-in-JavaScript。
以前,设计师编写模板,开发人员编写 PHP/Python/Ruby。现在明智的划分是表现性组件和有状态组件之间。
是的,也许我应该多介绍一下其他服务器端渲染模板框架的历史。这只是在能够以声明式的方式编写客户端代码方面是新的;我认为这些示例仍然属于“更改需要重新加载页面”的缺陷。如果你不需要很多花哨的客户端小部件,它们绝对是更好的解决方案。你总是可以使用两者中的少许:)
也许值得讨论一下表现性组件和有状态组件,但我认为这可能超出了本文的范围。感谢你添加了这个上下文。
好文章。很好的解释。
请告诉我它与 Angular 有什么不同。
每当需要动态创建控件时,我们可以使用任何一个 JQuery、Angular 或 React 等框架。
我真的很喜欢你解释“你可以为该组件使用 React”的方式。
如果我有许多 HTML 文件,用户交互非常有限,并且只有一个组件的交互量最大,那么请提供一个开发该单个组件的示例,以及如何将它们全部集成到单个网站并发布。
我对 React 和 Angular 之间主要区别的印象是,React 提供自上而下的单向数据流(所有内容都来自真相来源),而 Angular 提供双向数据绑定(将某些东西链接在一起,以便当其中一个发生变化时,其他也会自动发生变化)。我发现 React 更可预测,即使它的元素在 JSX/JavaScript 中定义,它们也比 Angular 更接近于描述普通 HTML,Angular 涉及其 HTML 中的许多自定义属性。React 也不那么有主见(这可能好也可能坏),而 Angular 是一种一体化的软件包,你必须以“Angular 的方式”做所有事情。它们各有优缺点,我承认我对 React 的经验要丰富得多。
有关将单个 React 组件添加到其他非 React 网站的示例,请参阅 React 文档中我在“如果它对你有帮助就使用它”部分链接到的页面:https://reactjs.ac.cn/docs/add-react-to-a-website.html。基本上,你可以在网站上的某个地方包含一个空的
<div>
,然后告诉 React 在那里渲染,它会接管该 div 的子元素,并保留页面的其余部分。我不介意 HTML/CSS-in-JS 方法,只要你的输出是精心制作的。只有在你对三种语言的基础知识有很好的掌握的情况下,才能提供这种工艺。
我的意思是,这些组件不会向 DOM 渲染“div 汤”,它们与它们被插入的组件一起很好地工作(语义)。
对于 CSS-in-JS,只要它不是内联样式,并且使用 CSS-in-JS 的开发人员体验很好,我都很赞成。
100% 同意。
蒂姆·伯纳斯-李有先见之明地预见到了这一点。他的“最小能力法则”多年来一直是 W3C 的指导原则。
https://www.w3.org/2001/tag/doc/leastPower
关于 HTML
明智的话,我无法反驳。这是权衡中要考虑的一件大事。我认为 React 最好在保留索引和读取数据的能力时使用,无论是通过服务器端渲染(例如 Gatsby)还是通过设计应用程序的后端以包含面向公众的 API,以便 React 视图只是一个前端,使用与其他用途相同的数据。
框架和库有什么区别?
说实话,我认为这种区别在 JavaScript 世界中有点消失了;它们经常被互换使用,我也有罪。
根据维基百科,“库提供函数供其父代码调用,而框架定义了整个应用程序设计。” (https://en.wikipedia.org/wiki/JavaScript_framework) 因此,根据该定义,我认为 React 是一个库,而不是一个框架。事实上,React 自己的网站称它为库。感谢你指出这一点!
我喜欢 React 这样的框架,但我也不喜欢它们(大多数)让对 JavaScript 不了解的用户处于黑暗中的方式。我个人喜欢将 React 与服务器端渲染结合起来,以便进行初始加载。
我觉得这篇文章一开始就错了,因为它提到你需要 JavaScript 来阻止整个页面重新加载。大多数用于代码编辑的网站会发布到 iframe 以刷新结果。我们不能忘记提供给我们的所有 HTML 和 CSS 工具。
我很快就会开始创建实时 IRC 客户端,它将无需 JS 和/或 Cookie 无缝工作(主要使用一些 css-tricks!)。
你能详细说明一下吗?我认为帧的内容仍然具有相同的限制,但我以前没有遇到过这样的代码。这听起来是一个有趣的解决方案!
看看 WebAssembly 在几年后会做什么。许多企业系统将成为哑终端,除了画布和 Web 套接字之外什么也没有,并将成为 VNC 客户端。
这里有太多东西需要展开,我们已经远离了我们的根源。与使用 ES 模块、类和 CommonJS 相比,使用像 React 这样的工具如何解决单一真相来源的问题?模板标签和 HTML 导入如何将标记和组件分开?
示例中的所有输入都缺少标签,表格摘要应该是具有 aria-live=assertive 的标题元素。
感谢您指出缺少的标签和 aria 属性!我需要回去更新它们,并实践我所宣扬的。我仍在学习如何制作无障碍标记,我是那些以错误顺序学习这一切的 JavaScript 开发人员之一!
至于您的问题,这些东西解决了不同的真相来源问题,有助于避免重复代码。React 特别管理视图状态的真相来源,并有助于避免重复状态(用户输入、从 API 加载的数据、用户交互的结果等)以及代码。
这是一篇有帮助的文章。尤其对新手来说。我认为迈克对文章的意图很清楚,所以没有任何神圣感。这显然不是一篇布道,而是一篇评论,它提供了对当前辩论的浅显见解。干得好。谢谢迈克。
我仍然不明白 HTML/CSS-in-JS 背后的理由!如果我们必须收回我们对关注点分离原则的言论,我们也必须收回我们对所有其他设计和开发原则的言论,例如控制反转、单一职责原则等。如果分离事物没有意义,为什么还要进行组件化?为什么单体 UI 不更好?你看,即使是专业开发人员也必须宣扬一件事,而实践另一件事,这真是可耻!也许 React 开发人员从未真正理解过这些,以及我们最初拥有 3 种不同技术(HTML、CSS 和 JS)的理由。
一个好的框架是能够在我们的专业领域内促进开发的框架;一个让做正确的事更容易,而做错的事更难的框架。可悲的是,许多开发人员陷入了这种困境。我无法想象有多少 Web 应用程序是用这种代码混乱制作的,它们是如何在目前存活的,以及最终需要多少时间和资源才能将它们重写为正常状态,因为现在社区正在欢迎“理智”的 UI 框架,例如 CHTML (https://github.com/onephrase/chtml).
我不同意。仅仅因为我们正在尝试重新思考一件事,并不意味着我们需要重新思考所有事情。
React 区分了技术分离和关注点分离,所有这些都在他们的文档中。它将状态与视图层分离,但将视图层的各个元素视为状态的函数,这不仅是合乎逻辑的,你甚至可以说我们之前从未真正理解关注点分离的含义。
看看 React 之前的框架。它们分离了技术,这限制了范围。但显然你需要这些技术来实现动态效果,所以范围通过 DI 重新引入,逻辑通过语法扩展(for-each、if 等)重新引入。换句话说,它们分离没有任何理由,只是为了用巨大的复杂性将这些部分粘合在一起。
对我来说,使用 HTML @import 将 HTML 文件导入其他 HTML 文件就是 React。
作为一个单人营销顾问,我偶尔会被拖进全面的网站建设项目,在那里我不得不扩展零散的技能来满足客户的要求。遇到像这样写得很好、没有意见且易于访问的内容简直是天赐良机。
干得漂亮,迈克,你被我收藏了!
感谢您和 CSS-Tricks 团队的这篇文章。有趣、合理、内容丰富。声明式 vs 命令式?我这个小脑瓜不太懂。之前不知道。学到了新东西。:thumbsup
非常感谢您提供这个概述!我现在更好地理解了何时以及为什么要使用 HTML-in-JS 框架,以及它们的好处。我还了解了它们的特定用途,以及它们不打算取代 HTML 本身 - 希望 React、Vue 等框架的过度热衷者也能阅读您的文章。
我不明白为什么人们在喊叫 HTML in JavaScript 很奇怪……同一批人之前在 PHP 和其他服务器端模板系统中使用过 HTML,为了毁掉一切,他们说 Vue、Angular 或 Svelte 更好,他们从不花时间检查这些家伙的最终输出,发现最后都是 HTML in JavaScript。
我必须同意你的观点。至少 JSX 可以防止 SQL 注入将用户输入插值到基于字符串模板的 HTML 中;)
对命令式和声明式编程的精彩解释,我很久没有读到过这么好的文章了。
好文章,我试图推动我工作的公司使用更具声明性的 JS 框架,并且已经获得了进行实验的时间。对我来说,我已经有点爱上了 Angular,因为它仍然具有一定程度的关注点分离(例如,将 html、样式和 typescript 分开到不同的文件中)。在与我的团队的其他人进行实验的过程中,我发现其他更偏向于 html 和 scss 的 FE 开发人员可以比使用 React 或 Vue 更轻松地处理他们需要的东西。
这绝对是支持 Angular 的一个强有力论点,同样也支持 Vue 和 Svelte。我认为它们在这方面权衡的平衡点有所不同,但(仅限个人意见)React 的可预测性和生态系统让它在我看来优于其他框架,至少对于足够复杂的 UI 来说是如此。这绝对是每个团队应该根据自己的优先事项和手头任务做出决定的问题。
就我个人而言,我对 Web Components 的未来非常好奇,因为它们可以用于任何这些框架中。我仍然对它们有一些问题,但它们正在朝着正确的方向发展。也许我的下一篇文章会是关于这个的!
React 区分了技术分离和关注点分离,所有这些都在他们的文档中。它将状态与视图层分离,但将视图层的各个元素视为状态的函数,这不仅是合乎逻辑的,你甚至可以说我们之前从未真正理解关注点分离的含义。
看看 React 之前的框架。它们分离了技术,这限制了范围。但显然你需要这些技术来实现动态效果,所以范围通过 DI 重新引入,逻辑通过语法扩展(for-each、if 等)重新引入。换句话说,它们分离没有任何理由,只是为了用巨大的复杂性将这些部分粘合在一起。
很棒的文章。恰如其分地描述了。
非常感谢您这篇文章,就像专门为我量身定制的一样。直到现在,我一直犹豫着尝试这种 js 吃掉 html 的编程方法,并且对此感到愤怒。我是一个喜欢在复杂应用程序中保持 html、css 和 js 分开的人……我知道这些新框架正在改变这种方法,但这让我很生气,为什么呢?!
我所有的模糊之处现在都清楚了,坦率地说,您给了我一个自信的肩膀拍打,让我知道如何看待这些新框架。
好文章。您完美地解释了它。我是一名全栈开发人员,但我对将 HTML 放入 js 感到不安。我觉得这是一种进化步骤,很快我们就不再这么做了。我还是前端 HTML/CSS/JS 的纯粹主义者。出于您提到的所有原因。在后端,我们使用部分视图,因此这是实现可重用单源组件的方式。客户端交付的实际上只是一个动态响应,但客户端获得的是一个纯净的响应。有太多方法可以减少代码了!
写得很好!我发现几乎不可能找到针对非专家的优秀技术写作(除了教程,当然)。
我最近的项目一直在使用原生 HTML+js 和使用反应式工具之间切换,虽然我很好地理解了实际差异,但我一直在思考“我该如何向新手解释这个?他们需要理解什么思维过程?”我的答案现在是把这篇文章发给他们 :)。
真的鼓舞人心。网络需要更多这样的写作。
在我看来,这更像是一个短期 vs 长期的问题,哪种方法更有意义取决于你的领域以及你愿意或能够提前计划多少。我认为,在该领域有如此多的事情发生,是因为这些技术之间没有原生的中间地带。
JS/React 正在快速变化,并且更灵活,短期内更灵活,HTML 和 CSS 更稳定,但也更僵化。毫不奇怪,在创建中间地带方面有资金投入,通过框架(例如 Gatsby)使 JS 更稳定和长期可行,并通过组合关注点(例如,在 Tailwind 中将样式和语义放在一起)使 HTML 和 CSS 短期内更灵活。
嘿,迈克,
好文章!很有趣,我昨天才刚想到这些。我也是一名前端开发人员,但直到几年前公司开始全面使用 React 之前,我才开始深入学习 JavaScript。像我这样的所有“传统”前端开发人员突然被抛入了 ES6、JSX、React 和 JavaScript 的无形世界。当时,我无法区分它们之间的很多差异。
现在我已经非常习惯编写复杂的 React 应用程序了,我开始思考用原生 JS 创建这些相同的具有状态的应用程序会是多么困难。我昨天实际上创建了一个计数器应用程序的 jsfiddle。我无法做到的是大声说出为什么没有 React 会让人很恼火。我认为您从另一个评论中为我确定了这一点,React 提供了
感谢这篇文章,就像它是为我写的。另外,我很感谢您对 90 年代后期/00 年代初期的音乐的引用!
附注:世界真小;我在高中时上过你的 C++ 课程。你的贪吃蛇游戏简直不可思议。希望一切安好。:)
不是反驳,而是真诚的问题
这在可访问性方面如何衡量?它能与屏幕阅读器配合使用吗?许多政府网站都有非常明确的可访问性要求,而且现在的 HTML 运行良好,即使编写得很糟糕。我想 JavaScript 应该没问题,但前提是开发人员做好了让它正常工作的努力。请参见 https://webaim.org/techniques/javascript/
这对 SEO 有什么影响?Google 最近才更新了其索引器(Caffeine),以使用最近版本的 Chrome。JavaScript 页面不可避免地会比 HTML 页面更晚/更不频繁地被索引——HTML 页面易于索引,JavaScript 页面需要先运行代码。其他搜索引擎对 JavaScript 的支持程度不一;有些对 JavaScript 视而不见。请参见 https://www.deepcrawl.com/knowledge/white-papers/javascript-seo-guide/how-javascript-rendering-works/
我个人不喜欢混合内容和代码——这使得更改内容变得过于困难——但我确实以构建 CMS 为生……
“随着 Web 应用程序变得越来越复杂,在 HTML 和 JavaScript 之间保持经典的关注点分离的成本也越来越高。”
“但是,如果你的所有 JavaScript 只是在创建一个非交互式网页,你就不应该使用 JavaScript。”
是的,当然。但是,违反这些原则就是为什么 JS 会“吞噬”HTML。出现了一种新的锤子(即 React),现在所有东西都是钉子。令人不安的是这些决策中的偏见。也就是说,开发者/工程师把自己放在中心位置,而不是客户和/或应用程序。会有什么问题呢?
很棒的东西。我认为自己是一名前端极简主义者,所以完全同意很多观点。
有一件事让我很好奇,表格的标题包含一个已选中数字状态以及两个按钮。
标题不是用来描述表格本身是什么的吗,比如配料列表之类的东西?我的意思是屏幕阅读器会读取它并说出表格是什么。现在它更像是一个带有操作的状态栏,我想这会让人困惑吗?
嗯。感觉很像 XAML,用于 .NET 中 WPF/UWP/Xamarin(以略微不同的方言)UI 编程的 XML 标记。你的代码隐藏有一个可观察的单一集合,而 XAML 代码声明性地构建了该集合的视图,该视图可以被过滤或排序等等,然后应用为列表框,或组合框的项目,或者作为查看当前所选条目更多详细信息的基础等等。如果可观察集合中的原始项目发生更改,那么所有依赖于该项目的项目也会发生更改。
但是,这确实需要正确地连接很多东西,如果绑定没有按照你认为的方式映射,调试它会非常痛苦。但总体原则非常相似。想出一个让模板起作用的方法,你就快成功了一半。
用 HTML/Javascript 以这种方式构建的东西将会很有趣。使用属性值来确定绑定,让 JS 框架跟踪更改。
注意事项:我已经很久没有认真使用 Javascript 了,它可能已经可以处理这种设置。它看起来只是还没有完全到位。尝试构建这样的系统会很有趣。
Mike,好文章!
出于好奇心和“为了尽可能扩大本文的受众”,我使用 Vue.js(来自 CDN)实现了购物清单。
我想补充一点,我的目的不是说一种框架比另一种更好,这里的想法是让人们从不同的角度了解如何使用不同的工具来实现这样的结果。
哇……
……你在哪里购物可以买到正好 2 个鸡蛋?
我非常喜欢这篇文章。一流的写作,技术性强且信息量大。
这是一篇很棒的文章。这是我第一次真正了解为什么你会使用 React 这样的框架,而不是每个人都说是因为它是“更好”。
我真的很喜欢原生 JS,我仍在学习并变得更擅长它,并且能够使用它完成大多数事情,所以我还没有动力真正深入学习框架,但这篇文章给了我一个不同的视角。
关于 CSS-In-JS 真是太有趣了:-) 我还没有达到那个水平。我非常喜欢我的 SCSS。
至少 JSX 可以防止 SQL 注入将用户输入插入基于字符串模板的 HTML 中;)
这简直太棒了!谢谢,期待后续文章。
如果你想了解更多关于选择哪些 JavaScript 框架的信息,这里有一篇关于这个主题的实用文章:https://www.ideamotive.co/blog/consider-these-javascript-frameworks-to-be-more-valuable-on-a-job-market
你可以在这里阅读关于 Svelte、TypeScript、GraphQL、NestJS、Gatsby、NextJS 和 Immer 的内容。
你好!这是一篇很棒的文章,我同意虽然 HTML 是一种标准的标记语言,它提供了网站的主要结构,但 JavaScript 是一种高级编程语言,它使网页更具交互性和动态性。如果你想成为一名可以快速创建网站的 Web 开发人员,那么深入了解 JavaScript 和各种流行的 JavaScript Web 开发框架对于你的 产品开发流程 (PDP) 非常重要。你可以找到关于技术的信息,这些技术可能是你最小可行产品软件的明智选择。