熟悉一个新技术的关键是熟悉他的特色和理念

React框架本身和我们常用的JavaScript MVC框架,如:AngularJS,Backbone,Ember等,没有直接的可比性。在React的官方博客中明确阐述了React不是一个MVC框架,而是一个用于构建组件化UI的库,是一个前端界面开发工具。所以顶多算是MVC中的V(view)。React并没有重复造轮子,而是有很多颠覆性的创新,具体的特性如下:

编写简单直观的代码

在年初的React开发者大会上,React的项目经理Tom Occhino讲述了React的最大的价值,React最大的价值不是高性能的虚拟DOM、封装的事件机制、服务器端渲染,而是声明式的直观的编码方式。React号称能让新人第一天开始使用就能开发新功能。简单的编码方式会让新手能很快地上手,同时也降低了代码维护的成本。这一特性决定了React能快速引起开发者的兴趣并广泛传播的基础。以下是React基于这一理念的具体做法。

简化可复用的组件

React构建UI是使用组件化的方式,而不是常见的模板。组件并不是一个新概念,它是某个独立功能或者界面的封装,达到复用或者UI和业务松耦合的目的。

组件化的设计理念也出现了很多年了,我们常用的ExtJS、YUI、jQueryUI、BootStrap等等都会提供大量的可复用的UI组件。比如在Bootstrap中使用对话框组件:

// 初始化
$('#myModal').modal({
   keyboard: false
}); // 显示
$('#myModal').modal('show'); // 关闭事件
$('#myModal').on('hidden.bs.modal', function (e) {
 // do something...
});

可以看到我们常用的这些组件提供了大量的接口和配置,让开发者选择合适的使用场景。这些组件的设计复杂,使用也较繁琐,新人上手有一定的门槛。W3C也在加紧制定Web Components(即组件)的标准,试图制定一个统一的简单实用的标准化的组件概念。我们看看React是如何设计组件模型的以及其和Web Component的区别。

React框架里面使用了简化的组件模型,但更彻底地使用了组件化的概念。React将整个UI上的每一个功能模块定义成组件,然后将小的组件通过组合或者嵌套的方式构成更大的组件。这种做法已经在instagram网站上普遍实施,大家可以看看instagram的前端源代码。 如下通过一个简单的例子来阐述React中模块化的概念。这个例子来自于React的官方网站教程,是完成一个简单的评论框。这个评论框主要包含两个部分,评论列表和评论表单。显示效果如下:

aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkJDQzA1MTVGNkE2MjExRTRBRjEzODVCM0Q0NEVFMjFBIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkJDQzA1MTYwNkE2MjExRTRBRjEzODVCM0Q0NEVFMjFBIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QkNDMDUxNUQ2QTYyMTFFNEFGMTM4NUIzRDQ0RUUyMUEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QkNDMDUxNUU2QTYyMTFFNEFGMTM4NUIzRDQ0RUUyMUEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6p+a6fAAAAD0lEQVR42mJ89/Y1QIABAAWXAsgVS/hWAAAAAElFTkSuQmCC" alt="" data-src="http://mmbiz.qpic.cn/mmbiz/vLKqut7Zx925tWxe4Un7X8Uebrt1q6bWMQcSNiasQrUUao7KSWCYu03t7AQvUfpD8YqMkV3taxicKIa47zr3OHlA/0?wx_fmt=png" data-type="png" data-ratio="0.8048048048048048" data-w="333" />

按照React模块组合的设计,把评论框组件commentBox分为两个子组件模块:commentList和commentForm,代码如下:

<div className="commentBox">
 <h1>Comments</h1>
 <CommentList data={this.state.data} />
 <CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>

commentList和commentForm组件对应的代码如下:

<div className="commentList">
 {commentNodes}
</div> <form className="commentForm" onSubmit={this.handleSubmit}>
 <input type="text" placeholder="Your name" ref="author" />
 <input type="text" placeholder="Say something..." ref="text" />
 <input type="submit" value="Post" />
</form>

从代码中可以看到CommentList组件又可以划分为comment组件的列表。comment组件代码如下:

<div className="comment">
 <h2 className="commentAuthor">
   {this.props.author}
 </h2>
 <span dangerouslySetInnerHTML= />
</div>

可以看出,为了完成评论框功能,使用React定义了四个不同的组件:commentBox、commentList、commentForm、comment。commentBox是由commentList和commentForm组合而来,commentList是由comment组合而来。这个例子充分体现了React组件化的理念,每个组件的UI和逻辑都定义到了内部,暴露少量的API和外部交互,组件之间组合形成更复杂的组件。总结一下,React的组件具有如下的特性:

  • 可组合:简单组件可以组合为复杂的组件

  • 可重用:每个组件都是独立的,可以被多个组件使用

  • 可维护:和组件相关的逻辑和UI都封装在了组件的内部,方便维护

  • 可测试:因为组件的独立性,测试组件就变得方便很多。

React使用了组件化的设计,所以开发者自然而然和原生的Web Components相提并论,StackExchange上有一篇精彩的讨论,解释了React的组件和原生组件的对比。文章从语言层面、样式的封装、数据绑定、DOM操作方式等这几个方面展开讨论,结论是说两者没有优劣之分,只是编码习惯问题。Web Components规范毕竟还在制定过程中,应该可以从React的组件设计方式上借鉴一些理念。在后续的文章中,会深入探讨React中组件的设计原理及使用。

虚拟DOM

在JavaScript中DOM操作是独立成为一个分支的。各浏览器在实现DOM操作库也是大同小异,都是在单独的模块中实现了DOM操作,由于各种技术上的原因,DOM操作的性能损耗相对于其他操作是很大的。在前端开发中都是需要特别尽量保持较小的DOM操作次数来提高性能。

React作为一个UI框架,不可避免要有界面上元素的交互。为了提高性能,React在操作页面交互时引入了虚拟DOM的概念。虚拟DOM是在React中用JavaScript重新实现的一个DOM模型,和原生的DOM并没有多少关系,只是借鉴了原生DOM的一些概念。虚拟DOM并没有完全实现DOM,只是保留了元素直接的层级关系和少量必要的属性。因为减少了不必要的复杂性,实践校验的结果是虚拟DOM的性能比原生DOM高很多。来看看普通DOM和虚拟DOM在代码上的差别。

如下是使用原生DOM生成的元素:

var a = document.createElement('a')
a.setAttribute('class', 'link')
a.setAttribute('href', 'https://github.com/facebook/react')
a.appendChild(document.createTextNode('React'))

那么使用虚拟DOM则代码为如下:

var a = React.createElement('a', {
   className: 'link',
   href: 'https://github.com/facebook/react'
}, 'React')

可以看到React中使用了自己实现的createElement方法来生成元素DOM结构。

基于React开发中构建的DOM都是通过虚拟DOM进行的。在React的实际的使用中,需要根据不同的数据展现不同的UI,当数据变化时,React会重新构建整个DOM树,然后将当前的DOM树和之前的比较,得到DOM树的区别,然后仅仅把变化的部分反映到实际的浏览器UI更新上。React会在同一个事件循环内合并DOM的变化,只是会对比开始和结束的DOM变化,忽略中间过程的DOM变化。尽管每次数据变化都是重新构建DOM树,但虚拟DOM的操作性能极高。这样使用React时,开发者不在需要关心数据变化时页面上DOM元素的更新,而只是关心各个数据状态下页面实际展现的效果。此外,因为React使用了由JavaScript实现的虚拟DOM,意味着可以在服务器端完成HTML结构的构建。

JSX

JSX是React的重要组成部分,他使用类似XML标记的方式来声明界面及关系,所以他只是一个文档规范。如下是一个在React里面使用JSX的例子:

var HelloMessage = React.createClass({
 render: function() {
   return <div>Hello {this.props.name}</div>;
 }
}); React.render(<HelloMessage name="John" />, mountNode);

可以看到如上使用了JSX的代码,像是HTML和JavaScript代码的混合体。很多人很不习惯这样的编码方式,认为这和我们一直倡导的表现和逻辑分离的思想相违背,是一种倒退。那么React这样的设计用意是啥呢?

React一个主要的设计理念是编写简单容易理解的代码。HTML模板的作用是让表现和逻辑分离,但是很多情况下模板还是严重依赖于业务逻辑,两者没有办法做到完全的松耦合。稍微复杂一点的例子,比如AngularJS使用了一套独特的机制来让UI和逻辑交互,示例代码如下。

<ul class="unstyled">
 <li ng-repeat="todo in todoList.todos">
   <input type="checkbox" ng-model="todo.done">
   <span class="done-"></span>
 </li>
</ul>

使用AngularJS的确从代码角度做到表现和逻辑分离,但是在HTML里面混入了大量的属性标记,这些标记但从语义上很难理解,新手比如要整个熟悉Angular中每个类似ng-*对应的用法及意义才能理解整个逻辑,所以有一定的入门门槛。如上例子使用JSX方式编写如下:

render: function () {
 var lis = this.todoList.todos.map(function (todo) {
   return  (
     <li>
       <input type="checkbox" checked={todo.done}>
       <span className="done-{todo.done}">{todo.text}</span>
     </li>);
 });
 return (
   <ul class="unstyled">
     {lis}
   </ul>
 );
}

可以看到,JSX中除了使用HTML标记之外,并没有复杂的标记。这种自然而直观的方式直接降低了React的学习门槛并且让代码更容易理解。

JSX只是简化了React的使用难度,但并不是必须的。在React中也可以不使用JSX,而是使用原生JavaScript的方式编写代码。在实际使用过程中也是把JSX转换成了JavaScript代码来运行的。React官方网站上提供了一个在线转换JSX到原生JavaScript代码的工具,通过这个工具也可以体会JSX使用上的优势及其内在原理。

Flux

Flux是另外一个独立于React的架构。之所以说Flux是一个架构而不是框架或者类库,是因为Flux仅仅用于配合React框架来处理组件和数据之间的交互。简单来说Flux就是用于管理数据流。和其他MVC框架倡导的双向数据绑定不同,Flux使用了单向数据绑定的机制,即数据模型到视图的流动。如下两个图展示MVC和Flux之间的差异:

Flux中主要使用了三个概念:Dispatcher、Action和Store。这三个概念区别于MVC的model、view和controller概念,因为MVC中更多的是数据双向绑定。

Actions是用于传递数据给Dispatcher的操作集合。Action可能来自于用户界面的操作,也可能是服务器端的数据更新。

Dispatcher是一个全局的分发器,接受Action,并传递给注册的回调函数。

Stores包含了应用的状态及注册到Dispatcher的回调函数,这些函数用于处理业务逻辑。

和React Views最密切的是Store,React view从Store取得state和其他数据,并更新界面。

总结

从以上的React相关设计可以看出,React是以降低前端开发的复杂度为原则的。使用React编写的代码也易于理解,所以适合大规模多人开发,能提高项目的开发效率和质量。

React设计思想的更多相关文章

  1. React 设计思想

    https://github.com/react-guide/react-basic React 设计思想 译者序:本文是 React 核心开发者.有 React API 终结者之称的 Sebasti ...

  2. React中的响应式设计思想和事件绑定

    这两个点是react入门非常重要的两个点,以前我们是直接操作dom的形式去做,react的设计思想和以前直接操作dom是完全不同的,react是一个响应式的框架,他在做编程的时候,强调的是我们不要直接 ...

  3. React-Native(三):React Native是基于React设计的

    React Native是基于React js设计的. 参考:<React 入门实例教程> React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript ...

  4. 使用Unity3D的设计思想实现一个简单的C#赛车游戏场景

    最近看了看一个C#游戏开发的公开课,在该公开课中使用面向对象思想与Unity3D游戏开发思想结合的方式,对一个简单的赛车游戏场景进行了实现.原本在C#中很方便地就可以完成的一个小场景,使用Unity3 ...

  5. spring事务管理器设计思想(二)

    上文见<spring事务管理器设计思想(一)> 对于第二个问题,涉及到事务的传播级别,定义如下: PROPAGATION_REQUIRED-- 如果当前没有事务,就新建一个事务.这是最常见 ...

  6. 掌握 Cinder 的设计思想 - 每天5分钟玩转 OpenStack(46)

    上一节介绍了 Cinder 的架构,这节讨论 Cinder 个组件如何协同工作及其设计思想. 从 volume 创建流程看 cinder-* 子服务如何协同工作 对于 Cinder 学习来说,Volu ...

  7. javascript继承机制的设计思想(ryf)

    我一直很难理解Javascript语言的继承机制. 它没有"子类"和"父类"的概念,也没有"类"(class)和"实例" ...

  8. 09A-独立按键消抖实验01——小梅哥FPGA设计思想与验证方法视频教程配套文档

    芯航线--普利斯队长精心奉献   实验目的: 1.复习状态机的设计思想并以此为基础实现按键消抖 2.单bit异步信号同步化以及边沿检测 3.在激励文件中学会使用随机数发生函数$random 4.仿真模 ...

  9. 08-FPGA状态机设计实例——小梅哥FPGA设计思想与验证方法视频教程配套文档

    芯航线--普利斯队长精心奉献   实验目的:1.学习状态机的相关概念 2.理解一段式.两段式以及三段式状态机的区别以及优缺点 实验平台:芯航线FPGA核心板 实验原理: 状态机全称是有限状态机(fin ...

随机推荐

  1. [ios 开发笔记]:一句话笔记

    1.NSString转int int a=[@"123" intValue]; 同样适用于NSDictionary将NSNumber转为int   2.switch(stateme ...

  2. Mysql Order By 字符串排序,mysql 字符串order by

    Mysql Order By 字符串排序,mysql 字符串order by ============================== ©Copyright 蕃薯耀 2017年9月30日 http ...

  3. python 与rabbitmq

    一.rabbitmq简介.安装 简介: MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专 ...

  4. 美国不同C段服务器,多ip服务器

    作为多IP服务器的拓展,多C段服务器,例如:IP分为4段,A段,B段,C段,D段.192.168.0.1/24代表着一个C段,可用IP段为192.168.0.1-255,一个C段有253个可用IP.一 ...

  5. mysql根据汉字首字母排序[转]

    select  areaName  from area order by   convert(areaName USING gbk)   COLLATE   gbk_chinese_ci asc 说明 ...

  6. scrapy_创建_调试

    如何创建scrapy项目? 输入命令: scrapy startproject project_name 在当前目录下创建名字叫project_name的scrapy项目 命令格式:scrapy st ...

  7. SSM与jsp传递实体类

    jsp传controller Controller: @RequestMapping({"/user"}) public void registerUser(User uu) th ...

  8. 亲测可用的国内maven镜像

    maven作为一个项目管理工具确实非常好用,之前oschina的中央仓库可用,现在oschina的maven服务器关了,于是自己倒腾了一个nexus,苦于自己的服务器是入门级的,下载速度实在让人着急. ...

  9. 【nginx】nginx解决跨域详解

    使用场景:本地运行一个项目,但是要访问外域的api接口,存在跨域问题,解决方式有很多,但我尝试用nginx解决,搜索了网上文章后再加上尝试终于成功, 其中一些注意事项和大家分享一下. 一.window ...

  10. 日常API之图灵聊天机器人

    机器人是什么?可以吃吗? 嗯,他可以和你聊天,不能吃哦. 首先需要到www.tuling123.com注册一只KEY,你才能调用机器人API哦 一.布局 (控制台程序可以跳过这一步)本文以WPF为示例 ...