阅读更多

1顶
0踩

Web前端
现在为止我使用 React.js 已经6 个月了。6 个月 放长远看一点也不长。但是,在 JavaScript 框架层出不穷的今天,6 个月可以称为老前辈了。最近指点了几个新人入门 React ,所以想总结一下写篇文章启发更多的人。下面总结的这些点,一些点是我希望在自己入门的时候就已经知道的,另外一些则是让我真正的理解 React。

本文假定你已经有了一下基本的概念。如果你不熟悉 component、props 或者 state 这些名词,你最好先去阅读下官方起步和手册。下面的代码示例我将使用 JSX 作演示,因为使用 JSX 语法写组件更为简洁,也更具表达力。

1.React.js 只是一个视图库

我们从最基本的开始。React 不是一个 MVC 框架,好吧,它根本就不是一个框架。它只是一个渲染视图的库。如果你对 MVC 熟悉的话,你就会意识到 React.js 只对应了V 这部分,如果它插手了 M 或 C 的逻辑,你就需要考虑用其它方法来解了。否则,到最后,你的代码很可能会变成一坨翔。这部分后面会细说。

2.组件尽可能的小

这一点有些显而易见,但是有必要强调一下。每个良好的程序员都知道,较小的类、模块更容易理解、测试和维护,对于组件来说也是一样。我起初犯的错误是低估了 React 组件合适的大小。当然,合适的大小取决于很多不同的因素(包括个人与团队偏好),但是,一般来说,我建议,让组件明显小于你本认为的必需大小。举个例子,我的个人网站主页上的这个组件,用于展示我的最新博文:



这个组件本身是一个 <section>,里面只有两个 <div>。第一个<div>有一个标题,第二个<div>只是映射一些数据,使用数据中的每个元素渲染 <PostPreview>。还有一部分抽取<PostPreview>作为独立组件,这点很重要。我认为这是一个组件最合适的大小。
3. 写函数式组件

首先,我们有两种定义 React 组件的方式,第一种是用 React.createClass():



另一种是 ES6 class 写法:



React 0.14 引入了一个新语法来定义组件,使用属性作为参数的函数:



这是我最喜欢的定义 React 组件的方式。除了语法上简洁,这种方法还能帮助你界定什么时候需要拆分组件了。我们来回顾下之前的例子,假设下面是没拆分之前的代码:



这个 class 还凑合。我们已经从 render 方法中抽取了几个方法,方法足够小,命名合理。我们来试着用函数式的语法重写一下:



代码基本一样,无非是将类里的方法暴露为函数。但是,对我来说,区别可大了。在基于类的例子中,我看到的是class LatestPostsComponent {,自然而然会往下扫描闭合括号,然后在心中默想“在这儿这个类结束了,这个组件也到这”。对比函数式组件,我看到 const LatestPostsComponent = props => {,看到函数结束,就已经知道“这个函数结束,组件也在这结束”。“但是,等等,这个组件外面的代码是些什么鬼?还在同一个模块,哦,这是另一个函数,接收数据然后渲染视图,我把它抽取到出来就行了”

我就不再啰嗦函数式组件有助于我们遵循上面第二点。

以后,React 也会做一些优化,会使函数式组件比基于类的组件更为高效。(更新:函数式组组件的性能影响比我想象的要复杂。但是,如果性能不是大的问题,我依然推荐尽可能的写函数式组件,你应该好阅读下这个和这个,选择一个合适自己的)

还有个重要的店,函数式组件有几个 ’限制‘ ,我个人认为是大优点。第一个是它不会有 ref 赋给它,ref 在查找子组件并与之通信上非常方便,我的感受是 这是使用 React 的 错误方式。refs 鼓励一种非常直接,近于 jqeury 的方式写组件,远离了 函数式,单向数据流哲学理念,这些理念恰恰是我们选择 React 的初衷!

另一个大的区别是函数式组件不会有状态依附,我下一个点就是讲…

4. 写无状态组件

不得不说,到目前为止,我觉得写 React 应用,最让我头疼的事都是由包含很多状态的组件引起的。

状态让组件很难测试

单纯的输入输出函数是最容易测试的,这点可以作为抛弃状态定义组件的理由吗?当我们测试很多状态的组件时,为了测试预期行为,我们必须先将组件设置为“正确的状态”。我们还必须考虑到所有的状态(因为组件可能在任意时刻改变这些状态)和属性(不受组件控制)组合,然后再去考虑那个组合需要测试,怎么测试。如果组件只是一个输入属性的处理函数,测试简直是不能更简单了。(关于测试,后面会讲)。

状态让组件很难推理(定位预期)

当你读一段代码中包含很多状态的组件,特别费劲,你需要在脑海中记录组件的状态。这些问题:”状态有没有初始化?”,“如果我在这儿改变状态将会发生什么?”,“有几个地方改变了这个状态”,“这个状态是否存在条件竞争?”,这几个问题非常普遍。跟踪组件变化太蛋疼了。

状态让组件很容易引入业务逻辑

我们不应该搜索组件然后才能确定行为。记住,React 只是一个视图库,所以,把渲染逻辑丢在组件里面没问题,但是业务逻辑也丢里面就有问题了。但是呢,如果你的应用状态都在组件里面,那在组件内部访问这些状态就会很方便,这样就会诱使你把业务逻辑也丢在里面。回顾下刚说的那点,这么做单元测试怎么办 - 没有业务逻辑你没法测试渲染逻辑,反之亦然。

状态让组件很难与应用其它部分共享信息

父层组件的状态很容易传给下层组件,反过来就费事了。

当然,有时一个组件独立维护部分状态也是有必要的。在这种情况下,尽管放心使用 this.setState 。它也是 React 组件 API 合理的一部分,我并不想是让你觉得应该禁用它。比如,在用户输入时,不需要把每个按键都暴露给整个应用,应该保存自己的状态,在失去焦点之后,输入值会被派发到其它地方存储起来。这种场景是最近一个同事提到的,我觉得这个例子非常恰当。

为组件添加状态还是需要慎重。一旦你开始了,就很容易再加一个状态,不知不觉就不受你控制了。

5.使用 Redux.js

上面第一点就已经说过,React 只是一个视图库。那么问题来了,“状态和逻辑放哪儿?” 我很高兴你会这么问!
你可能已经知道 Flux ,一种设计 web 应用的模式,在 React 开发中较为普遍。已经有几个基于 Flux 思想的实现,但是毫无疑问我推荐使用 Redux.js 。

我在考虑写一篇单独的博客,关于 Redux 的特性和优点。目前我推荐你读下官方文档,在这儿我只简单描述下它的工作原理:
  • 组件上的 UI 事件触发时,它们执行属性上传入的回调函数。
  • 这些基于事件创建的回调函数派发 actions
  • Reducers 处理 actions 并计算新的状态
  • 整个应用的新状态流入单一的 store
  • 组件接收新状态作为属性,在需要时重绘

上面的这些概念并非 Redux 独创,但是 Redux 的实现比较清晰简单。从 Alt.js 切换到 Redux ,减少了很多代码量,这儿简单列出比较突出的优点:
  • reducers 是纯函数,简单的 oldState + action = newState。每个 reducer 只处理一部分状态,这些状态可以组合起来。这么做,所有的业务逻辑和状态转换很容易测试。
  • API 很少,很简单,文档清晰。非常容易学习这些概念,因此很容易理解项目中 actions 和 数据 的流动过程。
  • 按照推荐的方式使用,只有很少的组件依赖 Redux ; 其它的组件只接收状态和回调作为属性。这么做可以保持组件非常简单,减少框架同步。

这儿有几个库配合 Redux 非常爽,我也推荐你使用:
  • Immutable.js JavaScript 不可变数据结构!用它存储你的状态,可以确保状态不会在不该改变的时候改变,并且能够保障 reducer 足够纯洁。
  • redux-thunk 当你的 actions 不只是更新应用状态,还有其他副作用时,就派上用场。比如,调用 REST API,设置路由或者派发其他 actions
  • reselect 用于可组合的,懒计算的情形。例如,对于部分组件,你可能想要:没有必要在最开始的时候就把这些全部引进来。当你开始有状态时,就可以引入 Redux 和 Immutable.js,有派生状态时引入 reselect,有路由或异步 actions 时引入 redux-thunk。尽早在必要时引入可以省去之后重构的时间。

只注入整个状态树的相关部分,而非整个
注入额外的衍生数据,比如总数或验证状态,而不需放在 store 中
Redux 是不是真正的 Flux,每个人都有自己的见解。个人觉得它符合 Flux 框架的核心思想,不过这个争论只是个语义问题。

6.一直使用propTypes

propTypes很容易为组件添加类型安全保障。他们看起来像这样:



在开发阶段(生产不会),如果任何组件没有给到必需的属性,或者所给的属性与声明的不匹配,React 会打印这些错误信息通知你。这有几点好处:
  • 防止低级错误,捕获 bugs
  • 如果你使用 isRequired,你就不需要检查 undefined 或 null
  • 就像文档所说,列出组件的所有属性,省去阅读代码的人搜索整个组件。

上面的这些点,你可能似曾相识,静态类型支持者的论点。个人来讲,我通常喜欢动态类型带来的开发速度和舒适,但是我发现 propTypes 可以毫不费力的为我的组件添加一些安全感。坦白讲,没有理由不一直用它们。
最后一点是,任何 propType 错误时,让你的测试用例失败。下面这个例子有点简单粗暴的,不过可行:



7. 使用浅渲染

测试 React 组件依然是有点棘手的话题。不是因为太难,而是因为还在发展,还没有出现一个最佳方案。目前来看,我的 go-to 方法是使用 浅渲染和属性断言。

浅渲染很好用,它允许你完整的渲染一个单一组件,而不涉及子元素的渲染。也就是说,结果对象只会告诉你子元素的类型和属性。这样子单一组件单一时间点可以提供很好的隔离。这儿有三种类型的组件单元测试,我自己也经常这么做:

渲染逻辑

假定一个组件,因条件不同,可能会显示一张图片,或者一个加载图标:



我们可以这么测试:



非常简单!当然,浅渲染的API 略微比我展示的复杂。上面使用的浅渲染函数是我们自己的 辅助方法,这个辅助方法包装了真正的 API,使用起来更简单一些。
回头看下我们的 ListOfNumbers 组件,下面是我们如何测试映射结果确实正确:



属性转换

在最后的例子中,我们深入测试组件的子元素,确保它们被正确渲染。我们不止断言组件是否存在,同时检查所给的属性是否正确。当组件确实在传递属性之前根据属性做些转换时,这点特别有用。例如,下面这个组件接受一个字符串数组作为 CSS 类名,往下传递一个单引号空白分割的字符串:



对这样方法最多的批判是激增的 props.children.props.children 。当然这不是最完美的代码,个人觉得如果一个测试中的props.children 多的让人受不了,这说明这个组件太大了,太复杂了,嵌套太深。它可能需要拆分。
另外一点,我经常听说的是,你的测试太依赖你的组件内部实现,以至于稍微改变你的 DOM 结构都能导致你所有的测试崩溃。这的确是一个很公正的评论,脆弱的测试套件是每个人想要的最好件事。管理这些最好的方式是保持你的组件足够小,足够简单,应该控制因组件变更引起的测试崩溃数目。

用户交互

当然,组件不止展示,还有交互:



这是我最喜欢的测试方法:



这只是一个例子,希望你能受到启发。

集成测试

上面我内容只覆盖到组件的独立的单元测试,但是你可能想确保你的应用各个部分协同工作,想在测试上走的更远。我对这部分了解的不够深入,但是列出一些基本点:
1.渲染你的整个组件树(而非浅渲染)
2.访问 DOM (使用 React TestUtils 或 jQuery 等等)找到你最关系的元素,然后
  • 断言元素的 HTML 属性和内容
  • 模拟 DOM 事件,然后断言产生的效果(DOM 或 路由变化,AJAX 调用等等)

关于测试驱动开发

一般情况下,写React 组件时我并不使用测试驱动开发。

在开发组件的时候,我发现我经常会去改动它的结构,我需要使用最简单的 HTML 和 CSS,在需要支持的浏览器上保持一致。因为我的组件单元测试方法大多会断言组件的结构,而测试驱动开发会使我在修改DOM时,忙于修复测试用例,这看起来有点浪费时间。
另外一个因素是组件足够简单以至于测试优先的有点基本消失。所有复杂的逻辑和转换都会丢在 action creators 和 reducers,这些地方我能真正享受到测试驱动开发带来的便利。
关于测试还有最后一点需要说明。整节内容我都在讨论测试组件,是因为测试基于 Redux 应用的其它部分没有特别之处。作为一个框架,Redux 背后还有些 ‘魔力’ ,可以减少对 mock 和其它测试模板的依赖。每个函数只是一个普通的函数(大多数是纯函数),测试起来真是如沐春风。

8.使用JSX, ES6, Babel, Webpack和NPM

只有 JSX 是 React 特有的。对我来说,JSX 是 React.createElement 的无脑操作。唯一的不足是增加了构建的复杂度,这个问题可以用 Babel 轻松搞定。
既然用了 Babel,那没理由不用 ES6 特性,像 常量,箭头函数,默认参数,数组和对象解构,延展和 rest 操作,字符串模板,迭代器和生成器,模块系统,等等。只要你花一点时间设置这个工具,你就能感受到 JavaScript 语言越来越成熟。
让我们做的更全面一些,使用 Webpack 打包代码,使用 NPM 管理包。现在我们完全赶上了 JavaScript 的潮流

9.使用React和Redux开发工具

谈到工具,React和Redux的开发工具太赞了。React dev tools让你审查 React 元素的渲染树,在查看浏览器中结果时相当有用。Redux dev tools 更是让人眼前一亮,让你看到每个 已经发生的 action ,它们引起的状态变化,甚至给你回退的能力!你可以作为开发依赖,或者浏览器扩展的形式使用。
你也可以用webpack设置热切换,保存代码时你的页面也会跟着更新-浏览器无需刷新。在调整组件和 reducers 时可以立即看到效果,大大提高开发效率。

来自:fedeoo

原文地址:9 things every reactjs beginner should know
  • 大小: 65 KB
  • 大小: 47.6 KB
  • 大小: 46.3 KB
  • 大小: 30.9 KB
  • 大小: 214.6 KB
  • 大小: 197.5 KB
  • 大小: 99.3 KB
  • 大小: 37.7 KB
  • 大小: 49.4 KB
  • 大小: 117.6 KB
  • 大小: 78.6 KB
  • 大小: 165.3 KB
  • 大小: 32.9 KB
1
0
评论 共 2 条 请登录后发表评论
2 楼 Fireman_duck 2016-07-27 16:34
自从改了域名以后。网站的用户就飞流直下。现在几乎没得人来了。。。

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • TreeSet 自定义排序

    TreeSet 自定义排序

  • 14、TreeSet 自定义排序

    TreeSet自定义排序

  • TreeSet自定义排序规则

    import java.util.Comparator; import java.util.Iterator; import java.util.TreeSet;... //根据person的 ID排序 public static TreeSet&lt;Person&gt; personTreeSet=new TreeSet&lt;&gt;(new MyCompartor()); .

  • 踩坑记录:TreeSet自定义排序导致数据丢失变少

    TreeSet自定义排序导致数据丢失变少

  • Java TreeSet自定义类排序

    想要使用TreeSet存储自定义类型,需要制定排序规则,具体实现如下。让自定义的类(如学生类)实现Comparable接口重写里面的compareTo方法来定制比较规则。Test.java Studen.java 方法二 TreeSet集合有参数构造器,...

  • TreeSet自定义排序

     * TreeSet 的自定义排序:  *   *   * 构造方法:  * TreeSet(Comparator&lt;? super E&gt; comparator)   构造一个新的空 TreeSet,它根据指定比较器进行排序。 自定义排序      Comparator 接口:    int...

  • java treeset实现_【Java】Treeset实现自定义排序

    还有一个学生排序类,重写compare函数,自定义排序规则是先比较出生日期,如果相同再比较姓名字母package birthday;import java.util.Calendar;public class Student {private String name;private Calendar ...

  • 解析treeSet集合进行自定义类的排序

    解析treeSet集合进行自定义类的排序

  • java122-treeset自定义排序

    //自定义排序 import java.util.*; public class test62 { public static void main(String[] args){ TreeSet tree=new TreeSet();//创建一个采用默认树形自然排序的对象 tree.add(new Integer(50)); tree.add(new ...

  • TreeSet中自定义类的排序方法

    TreeSet 自定义类排序

  • Treeset 如何设置自定义的排序规则

    : 进行自定义规则排序 咋样做 两种方式 方式1 要排序的类实现接口Comparable 并且重写方法compareTo,其中返回值分为三种 正数,0,负数, 正数添加到右边 负数添加到左边 0 会把原有数据覆盖. 方式2:匿名内部类 TreeSet...

  • 7-13 TreeSet自定义排序

    自定义排序

  • 练习TreeSet的自定义类型的排序规则

    继续探讨存入TreeSet集合中元素的排序规则: 假设有一个用户类,存储int age和String name。 自定义比较规则: 先按照年龄的从小到大排序。 如果年龄相同,按照名字的从小到大排序。 class User implements ...

  • Java集合类之Set的TreeSet之自定义排序规则

    自定义 TreeSet 的排序规则(按电话号码降序),整理给定的电话簿。 编程要求 接收给定的一行字符串(该字符串属于电话簿,包含多个电话号码,如:13545453432,13678909808); 自定义 TreeSet的排序规则(按...

  • Java中集合TreeSet的自定义排序问题

    TreeSet是属于Set接口中的实现类,对于存入的元素,有着不可重复、无序,但可根据一定排序规则自动排序的特性。TreeSet放入的元素,...什么是自定义类型时的排序呢?比如我创建了一个Person类,设定成员数据、构造方

  • 制定TreeSet排序的几种方法

    一、对int、String类型进行降序输出 1、存储int类型时 ... TreeSet&lt;Integer&gt; intSet = new TreeSet&lt;&gt;(); intSet.add(23); intSet.add(68); intSet.add(33); intSet.add(15); intSet.add(2

  • JAVA集合,TreeSet排序

    JAVA集合,TreeSet排序。

  • java TreeSet,TreeSet集合中自定义类型的排序方法

    自定义类型的排序 放到TreeSet或者TreeMap集合key部分的元素要想做到排序,包括两种方式: 第一种:放在集合中的元素实现java .lang. Comparable接口。 第二种:在构造TreeSet或者TreeMap集合的时候给它传一个比较器...

  • node-v12.16.3-x86.msi

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

  • 云计算基础课件—架构dr.pptx

    云计算基础课件—架构dr.pptx

Global site tag (gtag.js) - Google Analytics