一、React 技术栈
所有的软件都构建在一系列的技术栈上,你需要足够理解构建你 app 的技术栈。React 的技术栈看起来很庞大的原因在于它总是被按照错误的顺序解释了。
你应该按下列的顺序来学习,不要跳过或者同时学习它们:
- React 基础
- npm
- JavaScript “bundlers”(webpack)
- ES6
- Routing
- Flux
你不需要一次性学完它们。仅仅在遇到需要解决的问题时进行下一步的学习。
额外地,有一些经常在 React 社区中被提及的前沿技术,它们非常有趣,但很难理解。和上面的主题比起来没有那么受欢迎并且在大部分 app 开发中也用不上。
- Inline styles
- Server rendering
- Immutable.js
- Relay, Falcor, etc
二、React 只是一个视图库
React 不是 MVC 框架,也不同于其他任何框架。它只是一个用来渲染你视图的库。如果你来自 MVC 的世界,你需要意识到 React 只是’V’,且是部分等于。你需要在其他地方找到你的 ‘M’ 和 ‘C’。不然你终将会在令人生厌的 React 代码前止步。
三、让组件尽可能的小
这点是显而易见的,但也值得讨论。每一个优秀的开发者都知道小的类或模块更容易理解,测试和维护。对于 React 组件来说也是一样。在开始使用 React 时会有一个疑问,那就是我的组件到底该要多小?显然,确定的尺寸取决于很多因素(包括你和你的团队成员的偏好),但通常我的建议是让你的组件比你认为的还要小很多。比如下面这个用于展示我最近博客推送的组件:
1 | const LatestPostsComponent = props => ( |
这个组件本身是一个 <section>
,仅有2个<div>
在里面。第一个是标题,第二个映射了一些数据,为每一个元素渲染了 <PostPreview>
。我认为这是一个组件适合的尺寸。
四、写函数组件
之前有定义 React 组件有两种选择,第一种是 React.createClass()
:1
2
3
4
5const MyComponent = React.createClass({
render: function() {
return <div className={this.props.className}/>;
}
});
另一种是 ES6 的类:1
2
3
4
5class MyComponent extends React.Component {
render() {
return <div className={this.props.className}/>;
}
}
React 0.14 引入了寻得定义组件的语法:1
2
3const MyComponent = props => (
<div className={props.className}/>
);
这是至今我最喜欢的定义 React 组件的方法。除了更加简洁的语法,这种方法对组件需要被分离时很有帮助。让我们看一个例子,假设我们没有进行组件分离:
1 | class LatestPostsComponent extends React.Component { |
这种写法并不是很糟糕。我们已经从 render()
方法中抽取了大量方法,并保证每一块代码都尽可能小并很好地命名。我们已经将 renderPostPreviews()
方法封装地足够好了。让我们使用函数语法重写这段代码:
1 | const LatestPostsComponent = props => { |
两段段代码几乎一致,只是后面的没有了类的声明。然而对我来说这是很大的区别。在基于类的例子中,我会看到 class LatestPostsComponent {
,并自然地向下扫直到大花括号,也就是类定义结束的地方,也是组件定义结束的地方。而当我看函数组件时,,我看到const LatestPostsComponent = props => {
,仅仅扫到函数定义结束的地方。“函数定义在这里结束了,所以组件定义也结束了”。我这样想着,“但是等等,其他在组件外且在同一模块下的代码呢?哦!!这是其它用于获取数据和渲染视图的函数,我可以将它们全部提取到一个组件中。”
这是一种让人非常舒服的,让人遵守“让组件尽可能小”的方式。
在未来会有对 React 的进一步优化,这会让函数组件比基于类的组件更加高效。
值得注意的是函数组件有一些’限制’,我认为这让它变得更强大。第一点是函数组件无法赋予 ref
属性。ref
是组件查询子节点并与之通讯的便捷的方式,而我却觉得这是写 React 代码的一种错误方式。ref
鼓励一种非常命令式地,类似于 jquery 的方式来写组件,把我们带离了最初选择 React 的函数式的,单向数据流的哲学。
另一点是函数组件没有与之相关联的 state,这也是一个非常大的优点。
五、写无状态组件
可以这么说,至今为止我写 React 代码的几乎所有痛苦都来自于组件有太多的状态。
状态让组件很难测试
实践表明,没有什么比测试纯粹的、data-in data-out 函数更容易的了,所以我们不要为组件定义那么多状态。当测试有状态的组件时,我们需要让组件进入“合适”的状态以测试它们的行为。我们同样需要考虑所有状态(组件可以在任何时候被修改)和属性(组件无法控制)的组合,并决定测试哪一个。当组件只是输入属性的函数时,测试会变得简单许多。
状态使得在组件中添加业务代码变得很容易
让组件来决定程序的行为是我们在任何情况下都不应该考虑的。记住,React 只是一个视图库,在组件中写渲染逻辑是没问题的,但不能写业务逻辑(业务逻辑意味着大量代码)。但是当你的组件中有大量应用程序的状态并且可以轻易地通过 this.state
访问时,你就会不自觉地将计算或者验证类的代码写入组件。这会让测试的工作变得非常困难。
状态使得在 app 的其他部分共享信息变得困难
当一个组件有一些状态时,它很容易在组件间传递状态,但向其它方向传递会变得很棘手。
六、使用 Redux.js
React 是一个视图库,那么问题来了:“我在哪里放我的状态和逻辑呢?”
没错, Redux.js 可以替你做这些。下面是 React 的工作流程:
1.组件被给予了用做回调函数的属性,在 UI 事件触发时调用。
2.这些回调函数根据事件来创建并分发动作(action)。
3.reducer执行动作,并计算新的状态。
4.整个应用得新的状态将会保存在 single store。
5.组件接受新的状态并在它们必要时重新渲染。
Redux 的几点好处:
- reducer 是纯函数,简单地执行
oldState + action = newState
。每一个 reducer 计算一部分状态并将它们组合到一起生成整个应用。这使得你的业务逻辑和状态变换很容易测试。 - API 很小,很简单并且很好文档化。我可以迅速地进行查找并很容易学习全部的原理,之后就能更容易理解我的工程的动作和信息流。
- 如果你按推荐的方式使用它,仅仅只有很少的一部分组件需要依赖 Redux。其他组件只需要接受属性。这使得组件很简单。
还有一些用于补充 Redux 的库如下: - Immutable.js - 用于 JavaScript 的不可变数据结构。将你的状态保存在这里,使得状态在它不应该被改变的时候不改变,并确保 reducer 的纯净。
- redux-thunk - 用于你的动作不改变状态的时候。例如:调用一个 REST API 或者设置路由,或者分发其他动作。
七、总是使用 propTypes
propTypes 为我们提供了一种非常容易的方式来为组件更安全地添加类型。1
2
3
4
5
6
7
8
9
10
11
12
13
14const ListOfNumbers = props => (
<ol className={props.className}>
{
props.numbers.map(number => (
<li>{number}</li>)
)
}
</ol>
);
ListOfNumbers.propTypes = {
className: React.PropTypes.string.isRequired,
numbers: React.PropTypes.arrayOf(React.PropTypes.number)
};
它有一下几点好处:
- 更容易捕获 bugs,以避免愚蠢的错误。
- 如果你使用
isRequired
,你就不必总是检查undefined
和null
。 - 它类似于文档,其他开发者就不必看完整个组件来看属性是否必须了。
八、使用 React 和 Redux dev tools
React 和 Redux dev tools 都是非常棒的工具。React dev tool 是你能够查阅已经渲染的 React 元素树,这对于查看浏览器中的视图很有帮助。Redux dev tool 更加出色,使你能查看发生的每一个动作,以及动作造成的状态改变,甚至给你能够回溯的能力。
你也可以设置热模块来替代 webpack,使你的页面在你的代码改变时就更新-不需要浏览器主动刷新。这使得反馈循环更加地高效。