GMTC全球大前端技术大会聚焦前端、移动、AI应用等领域15个热门专题方向,购票立减¥960 了解详情
写点什么

React.js 在 Codecademy 中的实际应用

2015 年 3 月 24 日

自从 HTML5__ 变得流行以来,整个 Web__ 平台取得了长足的进步,人们也开始将 JavaScript__ 视为一门能够创建复杂应用的语言。许多新的 API__ 纷纷浮现,而关于浏览器如何应用这些技术的文章也大量涌现。

这一系列文章的视角更进一步,它们将关注于如何在实践中应用这些强大的技术,这并不是指创建多么酷炫的示例和原型,而是在第一线进行实际应用。在这个(后)HTML5__ 系列文章中,我们不需要响亮的口号,而是基于行业专家的实际经验,获得实践性的见解。我们也将讨论那些更进一步的技术(例如AngularJS _),并对 web__ 标准和 web__ 开发的未来进行定义。_

发布在 InfoQ__ 上的这篇文章是“下个时代的HTML5 和JavaScript __”系列文章其中的一篇。你可以订阅本系列文章,通过RSS__ 获取文章更新的通知。

2014 年八月,Codecademy 为了更新用户的学习体验,决定采用 Facebook 的 React.js 库,这是一个用于编写 JavaScript UI 的类库。在开始阶段,我们的问题是很难找到 React 在复杂应用中应用的示例,例如像 Codecademy 这样复杂的应用。因为大多数的教程都是针对小型示例应用上的特性,而不是针对在开发大型应用中更常见的问题。在本文中,我不仅会对 React 的使用进行一番概述,还会特别说明在大型 web 应用程序中使用 React 的某些特别注意事项。

React 是什么?

简单来说:React 是一个使用 JavaScript 创建用户界面的代码库。与编写用户界面常见的方式不同,React 将每个 UI 元素视为一个抑制的状态机。它并不是类似于 AngularJS 这样的“框架”。虽然 Facebook 有时会将 React 描述为“MVC 中的 V”,但我发觉这一描述并没有什么帮助,因为 React 应用并不需要遵守 MVC 模型。React 能够帮助你创建快速的用户界面,处理复杂的交互,而无需编写大量糟糕的代码。

如果你打算在工作中使用 React,你需要了解以下特性:

  • React会为你处理 DOM

DOM 操作的开销很大,而 React 的吸引力很大程度上来自于它对这一问题的处理方式。React 通过对自身虚拟 DOM 的维护,只在需要时进行重新渲染,将 DOM 操作的数量降至了最低,这要归功于 React 中高性能的比较操作的实现。

这就意味着你很少会需要直接与 DOM 打交道,与之相反,React 会替你处理 DOM 的操作。这一特性也是诸多 React 设计的基础。如果你打算滥用 React 的 API,或是打算按照自己的方式进行改动,那有可能会影响到 React 对 DOM 的理解。

该特性也使得使用 Node.js 进行内置的服务端渲染成为可能,这一点就使你能够轻易地创建对于 SEO 友好的页面。

  • React**** 使用声明式风格以及组件。

在 React 中,所有的组件都必须继承自 Component 类。组件中包含了属性(由父类决定)和状态(能够自行改变,通常是基于用户行为进行改变),组件的渲染和行为应当完全由它们的状态和属性所决定(而不依赖于任何其它值),因此组件就是状态机。这一模型鼓励使用者创建模块化的 UI,并且在实践中能够简化 UI 的操作与创建工作。

  • React将标记紧密地结合在 JavaScript中。

虽然在 JavaScript 中编写 HTML 代码听起来很奇怪,但在 React 中这是一种自然的选择。JSX 是原生的 JS 与 HTML 标记相结合的一种语言,对于它的使用是可选的,但我强烈建议你选择这种方式。React 认为,由于你的标记已经紧密地结合在控制这些标记的 JavaScript 中,因此可以将它们安置在同一个文件中,我也同意这一看法。

  • 单向信息流动。

这一点更多的是一种通用的 React 模式,而不是一种严格的规则。信息的流动在 React 中倾向于单向流动。在本文的稍后部分中,当我们开始考虑在大型应用程序中如何处理信息流动时,会再次提及这一模式。

解析 React 应用程序

为了让这些原则显得更为清晰,让我们看看 Codecademy 的学习环境是如何使用 React 进行构建的。

(单击图片以放大)

1:学习环境。

正如你在这个屏幕截图中所看到的一样,主要的学习环境由多个不同的 UI 元素所组成。某些元素是始终展现在页面上的,例如 header、menu 和 navigation。而有些组件会根据当前的练习的不同,处于显示或是不显示的状态。比方如,根据课程的不同,web 浏览器、命令行和代码编辑器可能会进行混合或是匹配。

创建该界面的逻辑解决方案是为各个部分创建 React 组件。在下面的屏幕截图中,我将特别指出主要的 React 组件:

(单击图片以放大)

2:学习环境以及对应的组件。

每个组件都有可能包含多个子组件:比方如,屏幕左方的 Lesson 面板实际上就包含了多个组件:

(单击图片以放大)

3:组成Lesson组件的各个子组件。

在这个示例中,我们将使用 React 以决定哪些组件应该显示在该 lesson 面板中。举例来说:

  • 只有在用户已登录的前提下,才会显示“Report a Problem”按钮
  • 只有在该练习中包括测试的情况下,才会显示 Instructions 部分

此外,React 会处理该组件与其它组件之间的信息流动。整个学习环境对应着一个父组件,该组件会持续跟踪多个状态,例如当前用户处于哪个练习中。父组件会为子组件的属性进行相应的赋值,以决定这些子组件如何显示。

现在,让我们来看一个组件通信的示例,以下组件来自经过大量简化后的组件树结构:

  • LearningEnvironment
  • CodeEditor
  • RunButton
  • ErrorDisplayer
  • Navigation

(单击图片以放大)

4:与代码提交相关的某些组件。

如果某个用户打算运行他的代码,我们该如何处理这一工作流?我们将尝试对他们提交的代码进行测试,然后显示错误信息,或是允许用户继续下一题。以下是某个可能发生的工作流:

  • 当用户单击 RunButton 之后,该组件会在事件的调用中,通过回调方式通知它的父组件 CodeEditor。
  • CodeEditor 组件会通过另一个回调函数通知它的父组件,即 Learning Environment 组件,并将用户的当前代码传递给父组件。
  • LearningEnvironment 组件将针对用户的代码进行测试。

根据结果的不同……

  • LearningEnvironment 组件会对 CodeEditor 中的属性 ****errorMessage赋值,CodeEditor 则会依次为它的子组件 ErrorDisplayer 中的属性 errorMessage 赋值。
  • 如果用户已经完成了该练习的所有测试,LearningEnvironment 组件就会为 Navigation 组件中的属性 progress 赋值。

如果我们的组件都能够像在 LearningEnvironment 组件中的 render 方法一样进行声明(同样进行了大量简化),那么就可以通过一个单一的函数调用实现整个 UI 的更新:

复制代码
render: function() {
return(
<div>
<CodeEditor
error={this.state.error}
/>
<Navigation
mayProceed={this.state.mayProceed}
/>
</div>);
}

请记住,React 中混合了 JavaScript 和 HTML 标记。在这个例子中,render方法定义了一个LearningEnvironment组件,其中包括了一个CodeEditor组件和一个Navigation组件。

我们可以更新 LearningEnvironment 组件的状态,它会触发组件的重绘,并在必要时更新子组件。

复制代码
handleTestResult: function(currentIndex, passing, error) {
this.setState({
error: error,
mayProceed: passing && currentIndex === this.state.currentExercise.tests.length-1
});
}

这就是全部的代码了。React 以一种优雅而简单的方式替我们处理 UI 的更新操作。

大型应用程序中的考虑因素

信息流动

正如我之前所说的一样,React 不一定要遵循 MVC 模型,实际上,你可以按照任何你喜欢的方式处理信息流动,但你需要一种确切的信息策略。为了让属性的变化传递给子 - 子 - 子 - 子 - 子组件,你是否需要将该属性一路传递下去,哪怕中间的那些子组件完全不需要了解该属性?如果该叶子节点接受用户输入,它又该如何将这一变更通知它的父 - 父 - 父 - 父组件呢?

在大型应用程序中,这种处理方式是很令人受挫的。即使是在以上那个简单的示例中,Run 按钮该如何与 LearningEnvironment 组件之间传递用户的行为呢?我们需要传递回调函数,但这种方式很难写出真正模块化、可重用的组件。

Codecademy 的解决方案是通过创建通信适配器(Adapter),以管理各别组件间的信息流动。与传递回调函数的方式不同,高层次的组件,例如 CodeEditor 会接收到一个 Adapter,它为重要的通信任务提供了一种单一的接口。举例来说,当 CodeEditor 处于显示状态时,LearningEnvironment 会创建一个 Adapter,它能够生成和处理与用户提交代码相关的事件。

这种方式也不是完全没有缺陷的,我也在 React 大会的演讲中针对这一点进行了详细的论述。我的主要观点在于,无论你 _ 如何 _ 处理组件树中的信息流动,你的团队都应该坚持一种一致的策略。

整合

React 的上手非常简单,但要在你的工作流中高效地使用它,你需要一些工具的支持。举例来说,我们使用了以下工具:

  • 用一段脚本对.jsx 文件的本地文化进行监控,并在必要时对它们进行重新编译
  • 一个独立的 node.js 服务器,用于处理服务端的展示
  • 用于在需要时自动生成新组件文件的开发者工具

以上这些工具都不是非常复杂。对于.jsx 的监控来说,Gulp 是一个很好的选择,不过我们选择了使用 Go 语言自行编写脚本。我们使用了一个简单的批处理脚本负责生成新的组件文件,这种方式也能够确保命名规范。如果你打算使用一个 node.js 服务器以负责服务端展示,你需要当心的是,要强制 require.js 能够获取到 React 代码中的变更可能会有些困难,因此我们创建了一个监控器,让它在必要时重启 node 服务器。

为什么使用 React?

在我们重新设计整个学习环境时,我们需要决定选择使用哪一套工具或框架。我们最终选择了 React,对这一决定我们感到非常满意。(关于我们如何选择一套 JavaScript 框架的详细过程,可以在以下演讲视频中找到: https://www.youtube.com/watch?v=U5yjPG5mHZ8

我们对于 React 的欣赏之处主要在于以下几个方面:

它经过了实战检验

React 已经在 Facebook 和 Instagram 的生产环境中得到应用,因此我们对于它的性能和可靠性很有信心。目前为止,它在我们的平台上同样表现良好,我们也没有遇到过任何严重的问题。

组件化的方式便于理解。

React 对每个独立的组件进行单独处理,这些组件会按照它内部的状态进行展现,因此对于某一时刻应该发生什么事,很容易形成概念化的理解。你的应用程序会有效地成为一个大型状态机。这意味着你可以单独测试 UI 中的每个片段,同样可以自由地添加新组件,而无需担心会影响整个应用程序中其它部分的代码。

SEO 非常容易实现。

因为 React 本身就支持服务端展现,因此在搜索引擎看来,你提供的是一个基本已完成的页面,这对于 SEO 来说是一个极大的优势,而所需的工作量非常小。的确,这一点必需由 Node 完成。由于 Codecademy 的主应用是由 Rails 编写的,因此我们搭建了一个独立的 Node 服务器,专门用于处理 React 的展现。

React 能够兼容遗留代码,并且它的灵活性足以应对未来。

虽然采用一整套框架的确是一件大事,但你也可以慢慢地尝试将 React 添加到现有的代码库中。与之类似,如果将来我们需要移除 React,我们也可以轻易地实现这一点。在 Codecademy,我们首先决定完全使用 React 来编写一个全新的项目,以便尝试它的功能,并学习如何以最佳的方式使用它。这个项目很成功,因此我们现在基本上在所有的新 UI 元素中都使用 React 了。我建议你首先做些功课,创建一些实验项目,然后再考虑怎样让 React 适应于你的现有代码库。

不必担心编写样板代码了。

在编写样板代码上所花的时间越少,就意味着你可以将更多的时间花在更有意义的问题上了。从这个角度上来说,React 是个既简洁又轻量级的类库。以下代码是创建一个新的组件所需的最少代码:

复制代码
var dummyComponent = React.createClass({
render: function() {
return (<div>HTML markup in JS, what fun!</div>);
}
});

简短且切题,还有什么不满意的?

我们的社区正在成长

React 社区的发展非常迅速。当你遇到各种问题时,你可以和许多社区成员讨论这一问题。并且,由于许多公司都已经在生产环境中使用了 React(仅举几例,Facebook、Instagram、Yahoo!、Github 和 Netflix),因此我们并不独孤。

总结

React 是一个轻量级、强大,并且经过实战检验的使用 JavaScript 创建用户界面的类库。它不是一个框架,而是一个强大的工具,或许会改变你进行前端开发的方式。我们认为它对于我们的前端开发来说,作用之大是难以置信的,而我们对于自己的选择也感到相当满意。对我自己来说,使用 React 进行工作至少是极大地影响了我思考编写用户界面的方式。我也乐于看到 React 的不断成长:现在 Facebook 已经通过 React Native 将 React 的功能带到移动开发上了,我想它的未来一定会是一片光明。

如果你打算上手使用 React,它的教程是一个不错的逻辑起点。互联网上也有着大量介绍React 中的关键概念的帖子(这个幻灯片是我最爱的教程之一)。不要停下脚步,学习钻研,尝试着创建些什么,然后看看你对于React 这种前端开发方式是怎么想的。我非常乐于聆听你的想法,请将你的想法发送至我的Twitter 帐号 @brindelle

关于作者

Bonnie Eisenman**** 是一位来自于Codecademy.com 的软件工程师。她最近刚刚从普林斯顿大学的计算机科学专业毕业。她对硬件也有一定兴趣,在业余时间喜欢从事一些 Arduino 方面的工作,以及乐曲编辑。她的 Twitter 帐号是 @brindelle。

自从 HTML5__ 变得流行以来,整个 Web__ 平台取得了长足的进步,人们也开始将 JavaScript__ 视为一门能够创建复杂应用的语言。许多新的 API__ 纷纷浮现,而关于浏览器如何应用这些技术的文章也大量涌现。

这一系列文章的视角更进一步,它们将关注于如何在实践中应用这些强大的技术,这并不是指创建多么酷炫的示例和原型,而是在第一线进行实际应用。在这个(后)HTML5__ 系列文章中,我们不需要响亮的口号,而是基于行业专家的实际经验,获得实践性的见解。我们也将讨论那些更进一步的技术(例如AngularJS _),并对 web__ 标准和 web__ 开发的未来进行定义。_

发布在 InfoQ__ 上的这篇文章是“下个时代的HTML5__ 和JavaScript”系列文章其中的一篇。你可以订阅本系列文章,通过RSS__ 获取文章更新的通知。

查看英文原文: React.js in Real Life at Codecademy

2015 年 3 月 24 日 11:145932
用户头像

发布了 428 篇内容, 共 150.6 次阅读, 收获喜欢 22 次。

关注

评论

发布
暂无评论
发现更多内容

毕业三年了,我开始明白为什么说三年是一个坎

鄙人薛某

程序员 程序人生 程序员成长 职场回顾

华为云FusionInsight MRS通过信通院大数据能力评估 单集群突破2万+规模

FI洞见

大数据 FusionInsight MRS 华为云

typora设置图片自动上传,实现快速发文章

诸葛小猿

Typora PicGo gitee 上传图片

rc-form源码解读

Lee Chen

前端进阶训练营

官宣 | 千呼万唤,Apache Flink 1.11.0 正式发布啦!

Apache Flink

flink

人生就是体会矛盾的过程

封不羁

成长 感悟

啃碎并发(七):深入分析Synchronized原理

猿灯塔

Java

鲲鹏说:高考之路你们走,高考阅卷我来守

脑极体

架构师训练营-作业5

紫极

redis系列之——分布式锁

诸葛小猿

Java redis 分布式 分布式锁

架构师师傅在训练营第5周感想

tuuezzy

架构师

CORS 和 CSRF 修炼宝典

pingan8787

前端 Web CORS CSRF

这样的二维码,你见过吗?

诸葛小猿

Java Python 后端开发 二维码 myqr

如何优雅地运用位运算实现产品需求?

梁桂钊

Java 架构

技术选型课程小结

行下一首歌

极客大学架构师训练营

ArrayList源码阅读

慌张而黑糖

ArrayList 源码阅读

依旧乐观的李彦宏,十年寻光的百度AI

脑极体

一致性hash

石刻掌纹

Java 后端博客系统文章系统——No1

猿灯塔

为了把握新基建风口,科技公司都在紧密筹备这件事...

极客时间企业版

Struct embedding in Go

Interstate5

golang time.Time dynamodb apigateway

如何通过DDD构建一辆汽车

Winfield

领域驱动设计 DDD

信创舆情一线--5省发布区块链发展计划

统小信uos

区块链 舆情

golang内存对齐

PONPON

go golang go内存对齐

分布式缓存与消息队列

紫极

vagrant

飞翔

极客大学架构师训练营

Hadoop大数据存算分离下,如何解决新旧存储共存?

XSKY融合存储

如何学习Visual Studio Code

博文视点Broadview

学习 读书笔记 vscode 能力提升 编辑器

数据分析师成长体系漫谈-数仓模型设计

analysis-lion

学习 数据仓库 数据分析 随笔杂谈

开发者必备——API设计问题

Noneplus

统一物品编码破解追溯“断链”困局

CECBC区块链专委会

大数据技术升级脉络及认知陷阱

大数据技术升级脉络及认知陷阱

React.js在Codecademy中的实际应用-InfoQ