前端框架的主要特性

每种JavaScript前端框架都以自己不同的方式更新DOM,处理浏览器事件,为开发者提供愉快的使用体验,这篇文章将探索“四大”框架的主要特性,从高级角度探讨框架的工作方式以及它们之间的区别。

先决条件:熟悉核心的 HTML, CSSJavaScript 语言。
目标:了解框架主要代码的特性。

领域特定语言

本模块中讨论的所有框架都由JavaScript提供支持,并且都允许您使用领域特定语言(DSL)来构建应用程序。比如,React推荐使用JSX编写组件,而Ember则使用Handlebars。与HTML不同,这些语言知道如何读取数据变量,这些数据可以用来简化编写UI的过程。

Angular应用程序经常大量使用TypeScript。TypeScript与用户界面的编写无关,但它是一种特定于域的语言,与普通JavaScript有着显著的区别。

DSL不能被浏览器直接读取;必须首先将它们转换为JavaScript或HTML。转换是开发过程中的额外步骤,但框架工具通常包括处理此步骤所需的工具,或者可以进行调整以包括此步骤。虽然不使用这些特定领域的语言就可以构建框架应用程序,但接受它们将简化您的开发过程,并使您更容易从这些框架周围的社区获得帮助。

JSX

JSX,代表JavaScript和XML,是JavaScript的扩展,将类似HTML的语法引入JavaScript环境。它是由React团队发明的,用于React应用程序,但也可以用于开发其他应用程序,例如Vue应用程序。

下面显示了一个简单的JSX示例:

const subject = "World";
const header = (
  <header>
    <h1>Hello, {subject}!</h1>
  </header>
);

此表达式表示一个HTML <header>元素,其中包含一个<h1>元素。第4行subject周围的大括号告诉应用程序读取subject常量的值,并将其插入到我们的h1中。

与React一起使用时,前面代码段中的JSX将编译为:

var subject = "World";
var header = React.createElement("header", null,
  React.createElement("h1", null, "Hello, ", subject, "!")
);

当最终由浏览器呈现时,上述代码段将生成如下所示的HTML:

<header>
  <h1>Hello, World!</h1>
</header>

Handlebars

Handlebars模板语言并非特定于Ember应用程序,但在Ember应用程序中大量使用。Handlebar代码类似于HTML,但它可以选择从其他地方拉入数据。这些数据可以用来影响应用程序最终构建的HTML。

与JSX一样,Handlebar使用花括号插入变量的值。Handlebar使用双花括号,而不是单对。

如下Handlebar模板:

<header>
  <h1>Hello, {{subject}}!</h1>
</header>

以及对应的数据:

{
  subject: "World"
}

Handlebar将构建如下HTML:

<header>
  <h1>Hello, World!</h1>
</header>

TypeScript

TypeScript是JavaScript的超集,这意味着它扩展了JavaScript——所有JavaScript代码都是有效的TypeScript,但不是相反。TypeScript非常有用,因为它允许开发人员对其代码实施严格的控制。例如,考虑一个函数add(),它接受整数ab,并返回它们的和。

在JavaScript中,该函数可以这样编写:

function add(a, b) {
  return a + b;
}

对于习惯JavaScript的人来说,这段代码可能微不足道,但它可以更清晰。JavaScript允许我们使用+操作符将字符串连接在一起,因此,如果ab是字符串,则此函数在技术上仍然可以工作——它可能不会给出预期的结果。如果我们只允许将数字传递到此函数中,该怎么办?TypeScript使这成为可能:

function add(a: number, b: number) {
  return a + b;
}

此处每个参数后面写的:number告诉TypeScriptab都必须是数字。如果使用此函数并将2作为参数传递给它,TypeScript将在编译过程中引发错误,我们将被迫修复错误。我们可以编写自己的JavaScript来引发这些错误,但这会使我们的源代码更加冗长。让TypeScript为我们处理这样的检查可能更有意义。

编写组件

如前一章所述,大多数框架都有某种组件模型。React组件可以使用JSX编写,Ember组件可以使用Handlebar编写,Angular和Vue组件可以使用模板语法编写,可以轻松扩展HTML。

不管他们如何编写组件,每个框架的组件都提供了一种描述它们可能需要的外部属性、组件应该管理的内部状态以及用户可以在组件标记上触发的事件的方法。

本节其余部分中的代码片段将使用React作为示例,并使用JSX编写。

属性

Properties或props是组件渲染所需的外部数据。假设你正在为一本在线杂志建立一个网站,你需要确保每一位投稿作家的作品都能获得好评。您可以创建一个AuthorCredit组件来配合每篇文章。此组件需要显示作者的肖像和有关作者的简短署名。为了知道要渲染什么图像,以及要打印什么署名,AuthorCredit需要接受一些props。

AuthorCredit组件的React表示形式可能如下所示:

function AuthorCredit(props) {
  return (
    <figure>
      <img src={props.src} alt={props.alt} />
      <figcaption>{props.byline}</figcaption>
    </figure>
  );
}

{props.src}{props.alt}{props.byline}表示将我们的props插入组件的位置。要渲染此组件,我们将在需要渲染的位置编写如下代码(可能位于另一个组件中):

<AuthorCredit
  src="./assets/zelda.png"
  alt="Portrait of Zelda Schiff"
  byline="Zelda Schiff is editor-in-chief of the Library Times."
/>

这将最终在浏览器中呈现以下<figure>元素,其结构如 AuthorCredit组件中所定义,其内容如 AuthorCredit组件调用中包含的props:

<figure>
  <img
    src="assets/zelda.png"
    alt="Portrait of Zelda Schiff"
  >
  <figcaption>
    Zelda Schiff is editor-in-chief of the Library Times.
  </figcaption>
</figure>

状态

我们在前一章中讨论了state的概念——健壮的状态处理机制是有效框架的关键,每个组件可能都有需要控制其状态的数据。只要组件在使用中,这种状态就会以某种方式持续存在。与属性一样,状态也可以用来影响组件的渲染方式。

例如,考虑一个按钮,该按钮计算它被单击的次数。此组件应负责跟踪其自己的计数状态,可以这样编写:

function CounterButton() {
  const [count] = useState(0);
  return (
    <button>Clicked {count} times</button>
  );
}

采用React hook方式,使用useState()给定初始数据值,更新时跟踪该值。代码最初将在浏览器中呈现为这样:

<button>Clicked 0 times</button>

useState()调用在整个应用程序中以一种可靠的方式跟踪count值,而无需您自己编写代码。

事件

为了具有交互性,组件需要响应浏览器的事件,以便我们的应用程序能够响应用户。每个框架都提供自己的语法来侦听浏览器事件,这些事件引用等效的本地浏览器事件名称。

在React中,侦听click事件需要一个特殊属性onClick。让我们从上面更新CounterButton代码,以允许它计算点击次数:

function CounterButton() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>Clicked {count} times</button>
  );
}

在这个版本中,我们使用额外的useState()功能来创建一个特殊的setCount()函数,我们可以调用它来更新count的值。我们在第4行调用此函数,并将count设置为其当前值加1。

给组件增加样式

每个框架都提供了一种为组件或整个应用程序定义样式的方法。虽然每个框架定义组件样式的方法略有不同,但它们都提供了多种方法。通过添加一些助手模块,您可以使用 SassLess设置框架应用程序的样式,可以使用PostCSS转换成CSS样式表。

处理依赖

所有主要框架都提供了处理依赖关系的机制——使用其他组件中的组件,有时使用多个层次结构级别。与其他特性一样,不同框架之间的机制会有所不同,但最终结果是相同的。组件倾向于使用标准的JavaScript模块语法将组件导入其他组件,或者至少是类似的东西。

嵌套组件

基于组件的UI体系结构,一个关键好处是组件可以组合在一起。就像您可以在嵌套编写HTML标记来构建网站一样,您也可以嵌套组件来构建web应用程序。每个框架都允许您编写利用(并因此依赖)其他组件的组件。

例如,AuthorCreditReact组件可能在Article组件中使用。这意味着Article需要导入AuthorCredit

import AuthorCredit from "./components/AuthorCredit";

完成后,AuthorCredit可以在Article组件中使用,如下所示:

  ...

<AuthorCredit />

  ...

依赖注入

现实世界中的应用程序通常会涉及具有多层嵌套的组件结构。由于某种原因,AuthorCredit组件嵌套了很多级别,可能需要来自应用程序根级别的数据。

假设我们正在构建的杂志网站的结构如下:

<App>
  <Home>
    <Article>
      <AuthorCredit {/* props */} />
    </Article>
  </Home>
</App>

App组件具有AuthorCredit组件所需的数据。我们可以重写HomeArticle,让他们知道如何传递props,但如果数据源和目的地之间有很多层次,这可能会变得很乏味。这也太过分了:HomeArticle实际上并没有使用作者的肖像或署名,但如果我们想将这些信息输入AuthorCredit,我们需要更改Homeauthor以适应它。

通过多层组件传递数据的问题称为prop钻入,对于大型应用程序来说并不理想。

为了避免prop钻入,框架提供了称为依赖注入的功能,这是一种将某些数据直接获取到需要它的组件的方法,而无需通过中间层传递。每个框架都以不同的名称和方式实现依赖注入,但最终效果是相同的。

Angular调用此方法处理依赖项注入;Vue有provide()inject()组件方法;React具有Context API;Ember通过services分享状态。

生命周期

在框架的上下文中,组件的生命周期是组件从浏览器渲染(通常称为mounting)到从DOM中删除(通常称为unmounting)所经历的各个阶段的集合。每个框架对这些生命周期阶段的命名都不同,并且并非所有框架都允许开发人员访问相同的阶段。所有的框架都遵循相同的通用模型:它们允许开发人员在组件mountsrendersunmounts以及在这两者之间的许多阶段执行某些操作。

renders阶段是最需要理解的,因为它在用户与应用程序交互时重复次数最多。每次浏览器需要呈现新内容时,都会运行它,无论新信息是对浏览器中内容的添加、删除还是编辑。

React组件生命周期图提供该概念的一般概述。

渲染元素

与生命周期一样,框架采用不同但相似的方法来渲染应用程序。它们都跟踪浏览器DOM的当前渲染版本,并且在应用程序中组件重新渲染时,每个都会对DOM应该如何更改做出略有不同的决定。因为框架为您做出这些决策,所以您通常不会自己与DOM交互。这种远离DOM的抽象比自己更新DOM更复杂、内存更密集,但如果没有它,框架就不能允许您以其著名的声明性方式进行编程。

虚拟DOM是一种将有关浏览器DOM的信息存储在JavaScript内存中的方法。您的应用程序更新这个DOM副本,然后将其与真实DOM(实际为您的用户呈现的DOM)进行比较,以决定要呈现什么。应用程序构建一个diff来比较更新的虚拟DOM和当前渲染的DOM之间的差异,并使用该diff将更新应用于真实DOM。React和Vue都使用虚拟DOM模型,但它们在diffrender时应用的逻辑并不完全相同。

您可以在React文档中阅读有关虚拟DOM的更多信息

增量DOM与虚拟DOM类似,它构建DOM差异来决定渲染什么,但不同之处在于它不会在JavaScript内存中创建DOM的完整副本。它忽略了DOM中不需要更改的部分。Angular是本文到目前为止讨论的唯一一个使用增量DOM的框架。

您可以通过Auth0 blog阅读关于增量DOM的更多信息。

Glimmer VM是Ember独有的。它既不是虚拟DOM,也不是增量DOM;这是一个单独的过程,通过这个过程,Ember的模板被转换成一种“字节码”,比JavaScript更容易、更快地读取。

路由

正如上一章中提到的路由是web体验的重要组成部分。为了避免在具有大量视图的足够复杂的应用程序中糟糕的用户体验,本文中介绍的每个框架都提供了一个库(或多个库),帮助开发人员在其应用程序中实现客户端路由。

测试

所有应用程序都受益于测试覆盖率,确保您的软件继续以您期望的方式运行,web应用程序也不例外。每个框架的生态系统都提供了便于编写测试的工具。测试工具本身并没有内置到框架中,但用于生成框架应用程序的命令行界面工具允许您访问适当的测试工具。

每个框架在其生态系统中都有广泛的工具,具有单元测试和集成测试的能力。

测试库是一套测试实用程序,其中包含用于许多JavaScript环境的工具,包括React、Vue和Angular。Ember文档涵盖Ember应用程序的测试

下面是在React测试库的帮助下编写的CounterButton的快速测试——它测试了许多事情,例如按钮的存在,以及在单击0、1和2次后按钮是否显示正确的文本:

import React from "react";
import { render, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";

import CounterButton from "./CounterButton";

it("renders a semantic with an initial state of 0", () => {
  const { getByRole } = render(<CounterButton />);
  const btn = getByRole("button");

  expect(btn).toBeInTheDocument();
  expect(btn).toHaveTextContent("Clicked 0 times");
});

it("Increments the count when clicked", () => {
  const { getByRole } = render(<CounterButton />);
  const btn = getByRole("button");

  fireEvent.click(btn);
  expect(btn).toHaveTextContent("Clicked 1 times");

  fireEvent.click(btn);
  expect(btn).toHaveTextContent("Clicked 2 times");
});

总结

此时,您应该对使用框架创建应用程序时将使用的实际语言、功能和工具有更多的了解。我敢肯定,您很想开始并实际执行一些编码,这就是您下一步要做的!此时,您可以选择首先开始学习哪个框架:

翻译自:https://developer.mozilla.org/zh-CN/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Main_features#handling_dependencies

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值