React是什么?
React是什么呢?从React的官网我们可以获知:React是用于构建用户界面的JavaScript库。React使创建交互UI变得轻而易举,为Web应用的每个状态设计简洁的视图,当数据改变时React能有效地更新并正确地渲染组件。这仅是对React的总结性的描述,对于想学习React的同学(或初次接触React的同学)还是希望能更深层的了解React是什么?React中包含了些什么?又是什么使得React变得那么特别?那么这篇文章将会简单的介绍一些React中的一些术语,并探索React的一些特色。最后希望这篇文章能帮助你能先熟悉一些概念,从而不至于在后面的学习过程中感到绝望。而且会慢慢喜欢上并掌握好React。
Web的开发模式
作为一名Web开发人员,都知道,一个Web页面或Web应用程序包括了HTML、CSS和JavaScript部分:
这三个部分各斯其职:
早期我们开发一个Web页面或Web应用程序可以说都是围绕着HTML、CSS和JavaScript来进行。而且随着时间的推移,今天的Web页面或Web应用程序除了外观比过去更好看以外,其开发方式也有很大的不同。比如我们有一个这样的Web网站,包括了首页、搜索结果页、详情页等:
较早前我们可能会创建多外页面:
- 首页 ->
index.html
- 搜索结果页 ->
searchresults.html
- 详情页 ->
itemdetails.html
- 订单页 ->
order.html
页面流可能会像下面这样:
在多页设计方式下,对于大多数改变页面显示的行为,Web应用会通过导航(或链接等)跳转到一个完全不同的页面。用户会看到原页面被销毁,然后出来一个新页面。对于用户体验上来说,略为差强人意。为了提供用户一个更好的体验,也有开发者采用在一个iframe
中嵌套别的页面(共用一些组件),让用户看上去没有别离开。
时至今日,应用程序趋向于采用**单页应用(SPA)**的开发模式。这种模式下,不需要导航到不同的页面,甚至不需要得新加载一个页面,应用不同视图被加载和卸载到同一页面上。在单页应用模式下,上面的Web应用看起来可能会像下面这样:
看上去有点类似于页面嵌套在一个iframe
,事实并非如此哟。
MVC的基础
随着Web应用变得越来越复杂,在大多数Web应用中都会使用前后端分离的方式来开发Web应用,而Web应用也同时会包含Model、View以及Controller三个部分,不在通过服务端直接生成前端的HTML代码了。
事实上,在老的Web应用开发模式下,也有MVC的模式,比如早期的开发语言PHP、ASP.net等。这些框架为控制器增加了一个新的职责:处理初始HTTP请求。大概是这样的:
控制器现在是应用程序的入口点,而不是视图。视图的职责也发生了变化:它的工作不是直接向用户显示某些内容并处理输入,而是为浏览器渲染组装一组HTML,CSS和JavaScript。HTML、JavaScript将包含类似于按钮单击处理程序的逻辑,该处理程序将通过XMLHttpRequest
将操作发送回控制器。注意,在浏览器中没有明显的MVC模式。随着现代Web框架的出现,这种情况很快就发生了改变。
通常这些框架引入了额外的构建步骤来创建HTML、JavaScript和CSS的静态包,以便通过一个简单的视图控制器(通常是/
或/index.html
)直接托管这些包。这些资源的静态特性意味着我们可以设置cache-control
头,并依赖CDN以更低的延迟为它们提供服务。这些单页应用程序包括逻辑,用于对一组由API控制器提供的资源发出HTTP API请求,这些资源通常用JSON
响应:
现在最流行的Web前端框架有三个,分别是React、Vue和Angular:
而我们要学的React在MVC中充当V。主要作用于视图层,所有关心的问题都是围绕着界面元素,并且让界面元素保持最新。这意味着不管项目中所用的MVC架中的M和C部分是什么,我们都可以自由的使用React作为V部分。这种灵活性,让我们可以挑选熟悉的技术,并且让React不仅可以用来创建Web应用,还可以用它来修改已有的应用,而不需要删除或重构整个代码。
简单地说,使用React,可以设计应用程序的所有前端部分。这意味着,你可以轻松地创建应用程序的所有用户界面:
有关于MVC相关的话题不在这里深入展开,这是一件较为复杂的事情,如果你感兴趣的话,可以阅读下面几篇文章:
单页应用开发主要问题
前面提到了,我们现在开发Web应用的方式主要采用单页应用模式来进行开发,虽然React、Vue这样的现代框让我们开发单页应用变得简单地多,但开发中也存在不少的问题,其中最要的有下面三个方面。
单页应用开发中大部分时间花在保持数据与UI同步上。例如,如果用户加载新的内容,在单页应用中需要考虑哪些内容保持,哪些内容要销毁,哪些内容要加载等。这些问题也是单页应用开发独有的问题。而在老式多页应用开发模式中,就从来不需要考虑这些问题。因为多页应用模式下页面与页面之间是独立的,加载新页面时,老页面就会销毁。
DOM操作很慢很慢。手动查询元素、添加子节点、删除子树,执行其他DOM操作,这些都是在浏览器中所做的最慢的事情。然而,更不幸的是,在单页应用中,我们要做很多这种事情。要响应用户行为以及显示新内容,就不得不操作DOM。
处理HTML模板会很痛苦。在单页应用中导航,实际上就是处理HTML文档片段。这些HTML文档片段用来表示要显示的内容,经常被称为模板。要处理模板,将内容在同一页面中显示出来,我们就得用JavaScript来操作模板,用数据来填充模板。这样代码很快变得很复杂。
而React的出现,让我们可以很好的解决这几个痛点。因为使用React的话,开发者不再需要直接操作DOM。React此时充当了开发者和DOM之间的桥梁(中间人),这样就降低了两者之间的沟通成本,同时也简化了构建的过程。
React不仅解决了这些缺点,还改变了我们构建单页应用的思考方式。接下来,简单地先了解React的几个核心技术点。
如果要详细了解这些核心技术点的话,每个技术点都可以通过一篇或多篇文章来阐述。
React带来的一些变化
React的出现,使用一些技术来解决了我们以前开发Web应用的一些痛点,也带来了一些变化。接下来看看React给我们带来哪些革命性的变化。
响应式UI
使用React构建的UI是响应式的。作为Web前端开发者,我们只需要编写想要的是什么,React自己知道该怎么做。当数据变化时,UI会相应地发生改变。开发者不需要再关心DOM的更新,React会自动帮你完成。响应式UI的理念大大地简化了UI开发。
比如下面这个示例,我们只需要改变帽子的类型,就可以给卡通更换不同的形状帽子:
示例来自于@focuser的Codepen。
事实上,在单页应用的开发中,跟踪UI并维护状态是很难的,而且耗费时间。就比如上面的示例中,跟踪卡通人物头顶的帽子形状是较为困难的。只不过React让我们变得更容易而以。我们不需要考虑其他的事项,只需要关注:UI所处的最终状态(即卡通头顶上的帽子是什么形状)。换句话说,React不会关心UI开始状态是什么,也不关心用户给卡通人物带什么样的帽子(用户改变UI采取哪些步骤),只需要关心UI结束的状态(卡通人物头顶的帽子是什么形状)。
就上面的示例而言,初次接触React的同学可能会看不太懂,但这并不重要,因为随着后续的学习,我们会明白每一行代码的作用。此时只需要简单看一眼关键代码即可:
const ThinkerWithHat = ({ hat }) => (
<div>
<Hat type={hat} />
<Thinker />
</div>
);
注意,只需要定义你想要的帽子是什么,并连接上数据("type = {hat}"
)。当数据发生变化时(用户在下拉选择框中选择帽子的选项),UI就会自动更新(卡通会戴上用户选择好的帽子)。
虚拟DOM
熟悉DOM操作的同学,都知道使用JavaScript操作DOM真的很慢。就上面的内容我们也知道,在React中,开发者不会直接去操作DOM,而是会借助React来做为中间人去做相应的沟通。更为糟糕的是,开发者在完成想要的效果之前,开发者没办法与DOM进行有效的沟通。在这个阶段,开发者什么也做不了,只有等,而等的这一个过程却是非常浪费时间的。
庆幸的是,React采用了另一项技术来解决此问题。React画草稿的速度超级快(这个过程用技术术语来描述的话是修改内存中的虚拟DOM)。当开发者告诉React,你想要的是什么后,React几乎就能立即将草稿(虚拟DOM)完成并准备绘制下一张。也就是说,你不需要花任何等待时间。因为开发者可以不停地告诉React,你想的要效果。React将会记录草稿的所有细节,并在适当的时候告诉DOM哪些地方有变化。更重的是,React会对所有草稿进行整理,拿掉重复的并确保DOM和开发者的工作量维持在最底的水平。
操作虚拟 DOM 非常快,当时机合适时,React 负责更新真实 DOM。它通过比较虚拟 DOM 和真实 DOM 之间的差别,查明哪个改变很重要,然后在一个称为 Reconciliation 的过程中作出最少量的 DOM 改变,以确保一切保持最新。
简单说, React在每次需要渲染时,会先比较当前DOM内容和待渲染内容的差异,然后再决定如何最优地更新DOM。这个过程被称为 reconciliation
。
也就是说,React不会让脚本操作DOM,而会先处理虚拟DOM(即前面提到的绘制草稿图),当用户改变状态时,React创建的虚拟DOM和旧的虚拟DOM会进行比较(有一个Diff算法),这样会找出有变化的部分,找出来之后,React也只会让变化的部分进行渲染。这样一来,整个过程省去了DOM获取和操作,而仅仅只有渲染过程。
React提出了一种假设,相同的结点具有类似的结构,而不同的结点具有不同的结构。在这种假设基础上进行逐层比较,如果发现结点不同,则删除旧结点及其包含的子节点;如果结点相同,则进行属性修改。
React操作DOM,虚拟DOM整个链路如下图所示:
React都是组件
组件对于Web开发人员而言应该更易于理解,因为在现实生活中就是由各种不同的“组件”所组成。比如我们平时用的车、住的房子,甚至我们自己都是由不同的组件所组成。而这些组件又是由很多更小的组件组合而成,以此类推,直至分解成原子。
如果你熟悉Atomic Design的话,每个Atoms都是一个组件:
而且在React中,React也鼓励我们将视觉元素分为更小的组件,而不是整大块,如下图所示,一个Web页面,我们可以拆解出来很多个组件,而且尽可能的往更小的原子方向拆分:
组件另外很酷的一点就是,如果改变了某个组件,所有使用此组件的地方都将自动更新。如下图所示,如果树叶(Hair
组件)不同,呈现到你眼前的树的形状就会有所不同:
完全可以在JavaScript中定义UI
早前Web开发模式提倡的是结构(HTML)、样式(CSS)和行为(JavaScript)进行分离,而且这种开发模式持续了很久的时间。随着React和Vue这样的框架出现之后,Web应用的开发模式也相应的发生了变化,大家不再葱白UI的结构、样式和行为单独分离,而是集中在一个JavaScript文件中定义。
说实话,刚开始的时候,我非常排斥这种开发模式,因为我觉得这跟Web标准背道而驰,脱离我对Web标准的认识。后来细想,如果像过去一样采用HTML结构的方式来定义UI,除了语法上难以让人接受之外,还有另一个主要问题。在模板中,除了只是显示数据,我们被限制做很多事情。比如,你想根据特定条件,选择显示哪一块UI,就不得不在应用中到处写JavaScript,或者用一些古怪的框架特有的模板语法,才能让它起作用。
现在,当我们思考完全用 JavaScript 定义 UI 时,可能会想到有些可怕的事情,比如引号、转义符、大量 createElement
调用等。不要担心。React 允许我们(可选)用 类似 HTML 的语法,即 JSX, 来定义 UI,而 JSX 是 JavaScript 完全支持的。我们可以像下面这样用 JSX 指定标记,而不是编写代码定义 UI:
<h1 class="title">Hello World!</h1>
上面的代码用JavaScript写起来,像下面这样:
React.createElement(
"h1",
{ class: "title" },
"Hello World!"
);
通过使用 JSX,你就能使用很熟悉的 HTML 语法很轻松地定义 UI,同时依然拥有 JavaScript 的强大功能和灵活性。更佳的是,在 React 中,界面和 JavaScript 通常在一个地方。我们不再需要在定义一个界面组件的外观和行为的多个文件之间跳转了。
如果你对JSX方面的知识感兴趣的话,可以花一点时间阅读《深入了解JSX》一文。
总结
上面的内容简单的阐述了React是什么?并助通过Web的新旧开发模式为起点,引出React。而且向大家简单的介绍了React中的几个核心概念以及一些术语。这篇文章的出发点只是希望大家对React有一些概念上的熟悉,并不要求大家如何通过React来写任何东西。当然有了这些概念,希望大家在后面的学习过程中有一定的印象,从而不会对React感到完全陌生。另外希望大家阅读完知道能简单的了解到React是什么,它的哪些方面比较适合应用开发以及React中的几个核心概念:即响应式UI、虚拟DOM、React就是UI组件和JavaScript构建UI等。
如果文章中有不对之处,烦请路过的各路大神拍正。如果你在这方面有更多的经验欢迎在下面的评论中与我们一起分享。
特别的声明,文章中部分图片来自于@kirupa和@Linton Ye的博文中。