是的,React 正在接管前端开发。问题是为什么。
本文的重点是 React 的受欢迎程度,而不是它与其他框架或库的比较
以下是 React 如此迅速流行的几个原因:
- 使用 DOM API (大部分情况下)比较难。React 基本上为开发者提供了一种使用比真实浏览器更友好的虚拟浏览器的能力。
- React 就是 JavaScript。只需要学习很少的 API,之后你的 JavaScript 技能就能让你成为更优秀的 React 开发者。没有任何入门门槛。
- 学习 React 对 iOS 和 Android 移动应用也大有裨益。React Native 让你能够运用 React 技能构建原生移动应用。你甚至可以在 Web、iOS 和 Android 应用之间共享部分逻辑。
- Facebook 的 React 团队会在 facebook.com 上直接测试所有引入 React 的改进和新功能,这增强了社区对该库的信任。React 版本很少出现重大且严重的 bug,因为它们都是在 Facebook 经过全面的生产测试后才会发布的。
- 最重要的是,React 允许开发者以声明式的方式描述用户界面,并对这些界面的状态进行建模。这意味着开发者无需费力地设计步骤来描述界面上的事务,只需用最终状态(例如函数)来描述界面即可。当该状态发生事务时,React 会根据该状态来更新用户界面。
让我们更详细地了解一下。React 的一个著名优势是它的虚拟 DOM(或协调算法)。我们将通过一个示例来展示这种算法的实际应用价值。
React 的官方定义指出它是一个用于构建用户界面的 JavaScript 库。理解此定义的两个不同部分非常重要:
- React 是一个JavaScript 库,它不是一个框架。它不是一个完整的解决方案,我们通常需要结合使用其他库来构建解决方案。React 不会对任何完整解决方案中的其他部分做任何假设。它只专注于一件事,并且力求将这件事做到极致。
- React 真正擅长的是定义的第二部分:构建用户界面。用户界面是我们呈现在用户面前,让他们与机器交互的任何东西。用户界面无处不在,从微波炉上的简单按钮到航天飞机的仪表盘。如果我们尝试交互的设备能够理解 JavaScript,我们就可以使用 React 为其描述用户界面。
由于 Web 浏览器能够理解 JavaScript,我们可以使用 React 来描述 Web 用户界面。我喜欢在这里使用“描述”这个词,因为这正是我们使用 React 的基本操作:我们只需告诉它我们想要什么,React 就会在 Web 浏览器中为我们构建实际的用户界面。如果没有 React 或类似的库,我们就需要使用原生 Web API 和 JavaScript 手动构建用户界面。
当你听到“React 是声明式的”这句话时,它的含义正是如此:我们用 React 描述用户界面,告诉它我们想要什么(而不是怎么做)。React 会负责“怎么做”,并将我们用 React 语言编写的声明式描述转化为浏览器中的实际用户界面。React 与 HTML 本身共享这种简单的声明式能力,但使用 React,我们可以对表示动态数据(而不仅仅是静态数据)的 HTML 界面进行声明。
React 有三个主要的设计理念推动了它的流行:
1. 使用可重用、可组合和有状态的组件
在 React 中,我们使用组件来描述用户界面。你可以将组件视为简单的函数(在任何编程语言中都是如此)。我们通过一些输入调用函数,函数会返回一些输出。我们可以根据需要重用这些函数,也可以将一些小函数组合成更大的函数。
组件也完全一样;我们称它们的输入为“属性”和“状态”,组件的输出是对用户界面(类似于浏览器的 HTML)的描述。我们可以在多个用户界面中重用单个组件,并且组件可以包含其他组件。
然而,与纯函数不同,完整的 React 组件可以具有私有状态来保存可能随时间而变化的数据。
2 — 响应式更新的本质
React 的名称是对这一概念的简单解释。当组件的状态(输入)发生变化时,它所代表的用户界面(输出)也会随之变化。用户界面描述的变化必须反映在我们所使用的设备上。
在浏览器中,我们需要在文档对象模型 (DOM) 中重新生成 HTML 视图。使用 React,我们无需担心如何反映这些更改,甚至无需管理何时将更改反映到浏览器中;React 只会对状态变化做出反应,并在需要时自动更新 DOM。
3 — 内存中视图的虚拟表示
使用 React,我们使用 JavaScript 编写 HTML。我们依靠 JavaScript 的强大功能来生成依赖于某些数据的 HTML,而不是增强 HTML 使其能够处理这些数据。增强 HTML 正是其他 JavaScript 框架通常所做的。例如,Angular 通过循环、条件语句等功能扩展了 HTML。
当我们仅从服务器接收数据(在后台使用 AJAX 进行)时,我们需要一些比 HTML 更强大的东西来处理这些数据。要么使用增强型 HTML,要么使用 JavaScript 本身的功能来生成 HTML。这两种方法各有优缺点。React 更倾向于后者,并认为其优点大于缺点。
事实上,这种方法本身就有一个主要优点;使用 JavaScript 渲染 HTML 使得 React 可以轻松地在内存中保留 HTML 的虚拟表示(通常称为虚拟 DOM)。React 首先使用虚拟 DOM 虚拟渲染 HTML 树,然后,每次状态发生变化时,我们都会得到一个需要带到浏览器 DOM 的新 HTML 树,React 不会写入整个新树,而只会写入新树和前一棵树之间的差异(因为 React 内存中同时保存了这两棵树)。这个过程称为树协调,我认为这是自 AJAX 以来 Web 开发中发生的最好的事情!
在下面的示例中,我们将重点讨论最后一个概念,并通过一个简单实用的示例来展示树协调过程及其带来的巨大变化。我们将两次编写相同的 HTML 示例,第一次使用原生 Web API 和原生 JavaScript,第二次我们将学习如何使用 React 描述相同的 HTML 树。
为了纯粹地专注于最后一个概念,我们将不使用组件,而是使用 JavaScript 计时器模拟状态更改操作。我们也不会使用 JSX,尽管使用 JSX 可以使代码更简洁。我在编写 React 时一直使用 JSX,但在本例中直接使用 React API 希望能帮助您更好地理解这个概念。
React 的协调算法示例
要继续本示例,您需要一个浏览器和一个代码编辑器。您实际上可以使用在线编程平台,但我将使用本地文件并直接在浏览器中进行测试(我们不需要 Web 服务器):
我们将从头开始这个例子。创建一个新目录,并在其中启动你最喜欢的编辑器:
mkdir react-democd react-demoatom .
在该目录中创建一个index.html
文件,并将一个标准 HTML 模板放入其中。在该模板中包含一个script.js
文件,并在该脚本中添加一条console.log
语句,以测试包含操作是否有效:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>React Demo</title>
</head>
<body>
<script src="script.js"></script> </body>
</html>
在浏览器中打开该index.html
文件,确保您可以毫无问题地看到空模板,并且可以在控制台 dev-tools 选项卡中看到console.log
您输入的测试消息script.js
:
open index.html # On Macexplorer index.html # On Windows
现在,让我们引入 React 库本身,我们可以从Reactjs 网站引入它。复制react
和react-dom
脚本,并将它们包含在 中index.html
:
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
我们在这里引入两个不同的脚本,原因很重要:React
库本身无需浏览器即可使用。而要在浏览器中使用 React,我们需要ReactDOM
库。
现在,当我们刷新浏览器时,我们应该看到React
和ReactDOM
在全局范围内可用:
通过这个简单的设置,我们现在可以访问React
和ReactDOM
API,当然,我们也可以访问我们将首先使用的本机 Web API 和 JavaScript。
要在浏览器中动态插入 HTML,我们可以简单地使用纯 JavaScript 和 DOM Web API 本身。让我们创建一个div
元素来托管我们的 JavaScript HTML 内容,并赋予它 id "js"
。在 的 body 元素中index.html
,在标签之前script
,添加:
<div id="js"></div>
现在script.js
,让我们div
通过 id 获取这个新元素,并将其放入一个常量中。我们将这个常量命名为jsContainer
。我们可以使用document.getElementById
它从 HTML 中获取div
:
const jsContainer = document.getElementById("js");
为了控制 this 的内容div
,我们可以直接在元素innerHTML
上使用 setter 调用div
。我们可以使用此调用提供任何想要插入到 DOM 中的 HTML 模板。让我们插入一个div
类名为“demo”、内容为字符串“Hello JS”的元素:
jsContainer.innerHTML = `
<div class="demo">
Hello JS
</div>
`;
确保它在浏览器中正常运行。现在你应该在屏幕上看到一行“Hello JS”。
这个 demo div 就是我们目前的用户界面。它非常简单。我们只是输出一段文本供用户查看。
document.getElementById
和实际上都是element.innerHTML
原生 DOM Web API 的一部分。我们在这里使用 Web 平台支持的 API 直接与浏览器通信。然而,当我们编写 React 代码时,我们改用 React API,并让 React 使用 DOM Web API 与浏览器通信。
React 就像我们浏览器的代理一样,我们大多数时候只需要与 React(我们的代理)进行通信,而无需与浏览器本身进行通信。我说“大多数”是因为我们在某些情况下仍然需要与浏览器进行通信,但这种情况很少见。
为了创建与目前完全相同的用户界面,但这次使用 React API,让我们创建另一个div
元素并赋予其 id "react"
。在元素index.html
下方div#js
,添加:
<div id="react"></div>
现在,在 中script.js
为新的 创建一个新的容器常量div
:
const reactContainer = document.getElementById("react");
这个容器将是我们对原生 Web API 的唯一调用。ReactDOM 需要这个容器来知道在 DOM 中将我们的应用程序托管在哪里。
识别出 React 容器后,我们现在可以使用 ReactDOM 库将render
React 版本的 HTML 模板应用到这个容器中:
ReactDOM.render(
/* TODO: React's version of the HTML template */,
reactContainer
)
接下来我们要做的事情,就是真正理解 React 库的第一个里程碑。还记得我说过,用 React 我们是用 JavaScript 编写 HTML 的吗?这正是我们接下来要做的。
为了编写简单的 HTML 用户界面,我们将使用 JavaScript 调用 React API,在示例结束时,您将更好地了解这样做的原因。
在 React 中,我们不是使用字符串(就像我们在上面的原生 JavaScript 示例中所做的那样),而是使用对象React.createElement
。任何 HTML 字符串都将使用call (这是 React API 中的核心函数)表示为对象。
以下是我们目前使用 React 实现的等效 HTML 用户界面:
ReactDOM.render(
React.createElement(
"div",
{ className: "demo" },
"Hello React"
),
reactContainer
);
React.createElement
有很多论点:
- 第一个参数是 HTML 标签,在
div
我们的示例中。 - 第二个参数是一个对象,它代表我们希望此标签拥有的任何属性。为了匹配原生 JS 示例,我们使用了
{ className: "demo" }
,将其转换为class="demo"
。请注意,我们在属性中使用了className
,而不是 ,class
因为在 React 中,与 Web API 匹配的是 JavaScript,而不是 HTML 本身。 - 第三个参数是元素的内容。我们在其中输入了“Hello React”字符串。
现在我们可以测试一下了。浏览器应该会同时渲染“Hello JS”和“Hello React”。让我们用这段 CSS 将演示中的 div 样式设置为一个盒子,这样就能在视觉上分割屏幕。在index.html
:
<style media="screen">
.demo {
border: 1px solid #ccc;
margin: 1em;
padding: 1em;
}
</style>
我们现在有两个节点,一个直接由 DOM Web API 控制,另一个由 React API 控制(React API 又使用 DOM Web API)。我们在浏览器中构建这两个节点的方式唯一的主要区别在于:在 JS 版本中,我们使用字符串来表示内容;而在 React 版本中,我们使用纯 JavaScript 调用,并用对象而不是字符串来表示内容。
无论 HTML 用户界面变得多么复杂,当使用 React 时,每个 HTML 元素都将通过React.createElement
调用用 JavaScript 对象来表示。
现在让我们为简单的用户界面添加一些功能。让我们添加一个文本框来读取用户的输入。
要在 HTML 模板中嵌套元素,在 JS 版本中操作很简单,因为它就是 HTML。例如,要使演示div
渲染一个<input />
元素,我们只需将其添加到内容中:
jsContainer.innerHTML = `
<div class="demo">
Hello JS
<input />
</div>
`;
我们可以在 React 中执行相同的操作,通过在 的第三个参数后添加更多参数来实现React.createElement
。为了匹配我们在原生 JS 示例中所做的操作,我们可以添加第四个参数,它是另一个React.createElement
渲染元素的调用input
(记住,每个 HTML 元素都是一个对象):
ReactDOM.render( React.createElement( "div", { className: "demo" }, "Hello React", React.createElement("input") ), reactContainer);
此刻,如果你质疑我们的做法,并认为“这会让简单的流程变得复杂”,那你完全正确!但我们的做法是有充分理由的。请继续阅读。
我们在两个版本中都渲染一个时间戳。在 JS 版本中,我们将时间戳放在段落元素中。我们可以调用以下命令来new Date()
显示一个简单的时间戳:
jsContainer.innerHTML = `
<div class="demo">
Hello JS
<input />
<p>${new Date()}</p>
</div>
`;
在 React 中实现同样的效果,我们向顶级div
元素添加第五个参数。这个新的第五个参数是另一个React.createElement
调用,这次使用一个p
没有属性的标签,以及一个new Date()
表示内容的字符串:
ReactDOM.render(
React.createElement(
"div",
{ className: "demo" },
"Hello React",
React.createElement("input"),
React.createElement(
"p",
null,
new Date().toString()
)
),
reactContainer
);
JS 和 React 版本仍在浏览器中呈现完全相同的 HTML。
正如你所见,到目前为止,使用 React 实际上比简单熟悉的原生方式要困难得多。React 究竟有什么优势,值得我们放弃熟悉的 HTML,并学习一套新的 API 来编写本来可以用 HTML 轻松编写的代码?答案不在于渲染第一个 HTML 视图,而在于我们需要如何更新 DOM 中现有的视图。
那么,让我们对现有的 DOM 进行一次更新操作。我们只需让时间戳每秒更新一次即可。
我们可以使用 Web 计时器 API 在浏览器中轻松重复 JavaScript 函数调用setInterval
。因此,我们将 JS 和 React 版本的所有 DOM 操作都放在一个函数中,调用它render
,并在调用中使用它setInterval
来使其每秒重复执行一次。
以下是完整的最终代码script.js
:
const jsContainer = document.getElementById("js");
const reactContainer = document.getElementById("react");
const render = () => {
jsContainer.innerHTML = `
<div class="demo">
Hello JS
<input />
<p>${new Date()}</p>
</div>
`;
ReactDOM.render(
React.createElement(
"div",
{ className: "demo" },
"Hello React ",
React.createElement("input"),
React.createElement(
"p",
null,
new Date().toString()
)
),
reactContainer
);
}
setInterval(render, 1000);
现在,当我们刷新浏览器时,两个版本的时间戳字符串应该每秒更新一次。我们现在正在更新 DOM 中的用户界面。
这正是 React 可能让你大吃一惊的时刻。如果你尝试在 JS 版本的文本框中输入内容,你将无法输入。这完全在意料之中,因为我们基本上会在每次更新时丢弃整个 DOM 节点并重新生成它。然而,如果你尝试在使用 React 渲染的文本框中输入内容,你当然可以!
虽然整个 React 渲染代码都在我们的计时计时器内,但 React 只修改了时间戳段落,而不是整个 DOM 节点。这就是为什么文本输入框没有重新生成,而我们却能够在其中输入内容。
如果您在 Chrome 开发者工具元素面板中检查这两个 DOM 节点,就能直观地看到我们更新 DOM 的不同方式。Chrome 的 div 工具会高亮显示所有更新的 HTML 元素。您会看到我们如何在每次更新时重新生成整个“js”div,而 React 则巧妙地只重新生成带有时间戳字符串的段落。
React 拥有一套智能的diffing算法,它只在 DOM 节点中重新生成真正需要生成的内容,其余部分则保持原样。这种 diffing 过程之所以能够实现,得益于 React 的虚拟 DOM,以及我们在内存中拥有用户界面的表示(因为我们是用 JavaScript 编写的)。
使用虚拟 DOM,React 将最新的 DOM 版本保存在内存中,当它有新的 DOM 版本带到浏览器时,新的 DOM 版本也将保存在内存中,因此 React 可以计算新旧版本之间的差异(在我们的例子中,差异是时间戳段落)。
然后,React 会指示浏览器仅更新计算出的 diff,而不是整个 DOM 节点。无论我们重新生成界面多少次,React 都只会将新的“部分”更新推送到浏览器。
这种方法不仅效率更高,而且还大大降低了我们思考更新用户界面的复杂性。让 React 负责所有关于是否应该更新 DOM 的计算,让我们能够专注于思考数据(状态)以及如何描述用户界面。
然后,我们根据需要管理数据更新,而不必担心在浏览器中的实际用户界面上反映这些更新所需的步骤(因为我们知道 React 会做到这一点,并且会以有效的方式做到这一点!)
感谢阅读!您可以在这里查看我的演示的源代码,也可以在这里查看演示的运行情况。
本文最初发表于此处
学习 React 或 Node?看看我的书 ;)
我为Pluralsight、Lynda等众多平台创建在线课程。我还为团队提供 JavaScript、Node.js、React.js 和 GraphQL 的从初级到高级的线上和线下培训。如果您想为您的团队预约课程,请发送电子邮件至training@agilelabs.com 。
文章来源:https://dev.to/samerbuna/yes-react-is-takeing-over-front-end-development-the-question-is-why-4mad