摘要:扩展单一职责原则又称单一功能原则,面向对象五个基本原则之一。马丁表示此原则是基于汤姆狄马克和的著作中的内聚性原则发展出的。
[解读]Thinking in React
前言原文:http://facebook.github.io/react/docs/thinking-in-react.html
Thought is the seed of action
这是放置在官方的QUICK START中的一篇博文,文章的目的是教会我们用React的方式去思考如何构建一个应用。
本文并非为了翻译,而是注重表达自己学习过程中的解读,加深对React组件化开发方式的认知,如果需要查看原文的翻译,可以戳这里
原文的翻译有点坑,个人觉得译文有些地方并没有准确地表达原文的意思,甚至有些错误
理解React的组件化开发假如我们要构建一个这样的应用
后台已经有JSON API提供这样的数据
[ {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"}, {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"}, {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"}, {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"}, {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"}, {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"} ];
接下来,我们分为5步来构建这样的一个商品搜索的应用。
步骤1:将UI拆分成组件树在此步骤,我们要完成这样的一个过程:
那么问题来了,如何划分组件?文章给出了两个思考这个问题的角度。
1. 单一功能原则
举例来说,在写程序的时候,通常为了实现某一单一的功能而创建一个函数或者一个对象,划分组件也是类似的一个思路。单一功能原则,指的是在理想状态下一个组件应该只做一件事情。当一个组件的功能变多了,就应该拆分成若干个小的组件。
> 扩展:单一职责原则(SRP:Single responsibility principle)又称单一功能原则,面向对象五个基本原则(SOLID)之一。它规定一个类应该只有一个发生变化的原因。该原则由罗伯特·C·马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中给出的。马丁表示此原则是基于汤姆·狄马克(Tom DeMarco)和Meilir Page-Jones的著作中的内聚性原则发展出的。
笔者认为,运用这一原则可以定位到应用的最小功能模块,从而划分出最低层的组件。然而,这一原则并不能完全概况组件化开发的理念,单一职责原则实质上提供的是模块化的思想,指导开发者编写低耦合、高内聚的代码。组件化则是一个更为复杂的概念:组件有层级关系,父子组件之间还会涉及数据传递(有时候是双向的)。如图所示:
2. 数据与UI的对应关系
用户的界面和数据模型在信息构造(information architecture)方面具有一致性,即用户界面可以很好地映射到一个构建正确的JSON数据模型上。因此在将用户界面划分成组件的时候,就是将其划分成能与数据模型一一对应的部分。
知道了如何划分组件,我们就对原型进行划分
在这个APP中,有5个组件,他们分别是
FilterableProductTable(橙色):包含整个例子的容器
SearcBar(蓝色):接收用户的输入
ProductTable(绿色):展示并且根据用户的输入过滤商品
ProductCategoryRow(青色):显示每个类别信息
ProductRow(红色):展示一行产品信息
实际上对ProductTable的划分是不够完美的,因为表格的头部(即Name、Price一行)并不是它的一部分,而是可以多带带划分出来的组件。由于表格的头部目前相对的简单,就简单地处理了。但是当表格的头部变得复杂起来的时候,讲道理的话,应该将其多带带划分成组件ProductTableHeader。
这样,我们也可以很容易得到组件树:
FilterableProductTable
SearchBar
ProductTable
ProductCategoryRow
ProductRow
步骤2:创建静态的版本为了节省篇幅,就不在此处贴出代码了,如果需要可以参照原文中的代码或者参照本人自己写的demo
按照步骤1的分析,我们可以很快地建立应用的静态版本,每个组件目前只有一个render函数,返回每个组件的html结构,同时,建立了组件之间的层级关系。不过要注意两点:
在render函数中使用了JSX,它是JavaScript的一种语法糖,使用的目的是为了编程的便利,可以很清晰看到我们创建的HTML的标签结构,当然还有其他的好处,在后面会提到。
render中创建的是虚拟的DOM节点,而非真实的DOM节点,理解这一点对理解React的高效渲染特点很重要。
props vs state
首先,这两个指的都是数据(类似于数据驱动的思想)。关于这两个,在这里做一个简单的分辨。
1.我们在初始化组件的时候,使用的是props。这就是props很典型的使用方法,它是父组件向子组件传递数据的方式,在React中,数据的传递是单向的,也正是基于props来实现。
在顶层组件FilterableProductTable会把后台的JSON数据作为props,从顶层组件一直传递下去,实现数据的单向流动。
2.state是什么?state只存在于React组件的内部,React中创建的组件,实际上是一个状态机,当组件的state改变的时候,会触发组件进行重选渲染(这一过程还会涉及到虚拟DOM的差分算法,最小化DOM操作)。大致的流程(如图所示):
明确组件的状态的改变,是编写组件的核心部分,在接下来的步骤3就做这样一件事情。
步骤3:识别出最小(但是完整的)代表UI的状态集构建一个正确的应用,首先需要做的就是找出应用的最小的状态集。
何谓最小?状态集中的任意一个 state都不能由其他的state计算得出。用数学来描述就是状态集中的元素线性无关。比如说一个TODO List的应用,一个state使用数组保存了条目数据,那么就不用再使用一个state来保存条目数了,因为条目数就是数组的长度。
回到我们的应用中来,在我们的例子中有这么些数据(state和props都是数据)
原始产品信息列表
用户输入的搜索数据
checkbox的值
搜索过滤之后的产品信息列表
理解了最小之后,我们来确定状态集。可以从几个角度排除掉非state的情况:
数据是否是通过props从父组件传递过来的?如果是,那么很有可能不是state
数据是否会随时间变化?如果不会,那么也很有可能不是state
是否能通过组件中的props或者其他的state计算出该数据?如果是,那就不是state
分析
原始的产品列表信息是通过props进行传递,因此不是state
用户输入的搜索信息和checkbox都是随时间变化而且不能通过其他进行计算,应该是state
搜索过滤之后的产品信息列表可以通过原始产品信息列表、输入框信息和checkbox计算得出,因此不是state
到这里,我们得到了应用的状态集
输入框的值
checkbox的值
步骤4:确认state存在哪个组件中拥有了状态集之后,接下来就要确认哪些组件拥有哪些state。
这里是译文的一个错误的地方,并非确认state的生命周期
我们可以从几个方面来解决这个问题:
找出那些需要基于该state进行渲染的组件
找到这些组件的共同的祖先组件
要么是共同的祖先组件,要么是另外一个在组件树中位于更高层级的组件应该拥有这个 state
如果找不出拥有这个 state 的组件,可以创建一个新的组件来维护这个state,并将这个组件添加到所有需要基于该state进行渲染的组件的上面。
回到我们的应用中来:
ProductTable需要通过state来展示过滤产品信息,SearchBar需要基于state来显示输入的文本和checked的状态。
它们共同的祖先组件是FilterableProductTable
结合以上所述,可以很容易得出FilterableProductTable拥有应用的两个state:输入框的值和checkbox的值
步骤5:添加反向的数据流这里可以思考3个问题:
什么叫反向的数据流?
为什么要有反向的数据流?
怎么实现反向的数据流?
首先,在React中,数据是从顶层传递到底层的。如果是底层的组件通过某种方式更新了上层的组件的state,这样就叫做反向的数据流。
结合我们的应用来讲为什么要有反向的数据流。在步骤4中,我们得出了组件FilterableProductTable中有两个state:输入框的值和checkbox的值,但是这两个状态的改变是由在组件SearchBar中的输入框的enter事件和checkbox的change事件来触发的,同时state的值需要从输入框的输入文本和checkbox的值中获取,这就要求数据从SearchBar传递到FilterableProductTable中。
“理都懂”之后,来谈谈实现。我们结合一下代码来讲解一下(代码只是大致的实现):
var FilterableProductTable = React.createClass({ handleSearch : function(search) { //searchText为输入框的值,在此函数内可以改变state this.setState(search) //之后的逻辑省略 } render : function () { return (); } }); 写在最后); } }); var SearchBar = React.createClass({ handleEnter : function(e) { //...省略前面的判断逻辑 /*获取到输入框和checkbox的值之后,利用props传递到父组件, 在这里实现了反向的数据流 */ this.props.onSearch({searchText : searchText,isChecked : false}) }, handleChange : function(e) { }, render : function() { return (
到此结束。在发布这篇博客的时候,demo并没有全部完成,估计要过些日子才能完成了。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/79613.html
摘要:前言本文是学习这一章后的记录,并且用实现其中的示例。因此得到如下结构而数据则从顶层组件往下流动,各层提取各自数据进行渲染。而交互的意思是,对的操作会影响应用数据,从而刷新。更新值更新值注意中使用时,需要定义一个返回函数的高阶函数来实现。 前言 本文是学习Thinking in React这一章后的记录,并且用Reagent实现其中的示例。 概要 构造恰当的数据结构 从静态非交互版本...
摘要:,谷歌给的一份性能指南和最佳实践。目前而言,前端社区有三大框架和。随后重点讲述了和两大前端框架,给出了大量的文章教程和相关资源列表。我认为,使用函数式编程方式,更加符合后端程序员的思路,而是更符合前端工程师习惯的框架。 showImg(https://segmentfault.com/img/bVbjQAM?w=1142&h=640); 这个是我订阅 陈皓老师在极客上的专栏《左耳听风》...
摘要:,谷歌给的一份性能指南和最佳实践。目前而言,前端社区有三大框架和。随后重点讲述了和两大前端框架,给出了大量的文章教程和相关资源列表。我认为,使用函数式编程方式,更加符合后端程序员的思路,而是更符合前端工程师习惯的框架。 showImg(https://segmentfault.com/img/bVbjQAM?w=1142&h=640); 这个是我订阅 陈皓老师在极客上的专栏《左耳听风》...
摘要:本篇开始介绍自定义组件是如何渲染的。组件将自定义组件命名为,结构如下经过编译后,生成如下代码构建顶层包装组件跟普通元素渲染一样,第一步先会执行创建为的。调用顺序已在代码中注释。先看图,这部分内容将在下回分解 前言 React 是一个十分庞大的库,由于要同时考虑 ReactDom 和 ReactNative ,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常深,阅读其源码是一个非...
摘要:在学习源码的过程中,给我帮助最大的就是这个系列文章,于是决定基于这个系列文章谈一下自己的理解。到此为止,首次渲染就完成啦总结从启动到元素渲染到页面,并不像看起来这么简单,中间经历了复杂的层级调用。 前言 React 是一个十分庞大的库,由于要同时考虑 ReactDom 和 ReactNative ,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常深,阅读其源码是一个非常艰辛的过...
阅读 1352·2021-11-22 09:34
阅读 1361·2021-09-22 14:57
阅读 3376·2021-09-10 10:50
阅读 1318·2019-08-30 15:54
阅读 3667·2019-08-29 17:02
阅读 3452·2019-08-29 12:54
阅读 2587·2019-08-27 10:57
阅读 3303·2019-08-26 12:24