React初学者,你需要知道这些

一、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
2
3
4
5
6
7
8
const LatestPostsComponent = props => (
<section>
<div><h1>Latest posts</h1></div>
<div>
{ props.posts.map(post => <PostPreview key={post.slug} post={post}/>) }
</div>
</section>
);

这个组件本身是一个 <section>,仅有2个<div>在里面。第一个是标题,第二个映射了一些数据,为每一个元素渲染了 <PostPreview>。我认为这是一个组件适合的尺寸。

四、写函数组件

之前有定义 React 组件有两种选择,第一种是 React.createClass():

1
2
3
4
5
const MyComponent = React.createClass({
render: function() {
return <div className={this.props.className}/>;
}
});

另一种是 ES6 的类:

1
2
3
4
5
class MyComponent extends React.Component {
render() {
return <div className={this.props.className}/>;
}
}

React 0.14 引入了寻得定义组件的语法:

1
2
3
const MyComponent = props => (
<div className={props.className}/>
);

这是至今我最喜欢的定义 React 组件的方法。除了更加简洁的语法,这种方法对组件需要被分离时很有帮助。让我们看一个例子,假设我们没有进行组件分离:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class LatestPostsComponent extends React.Component {
render() {
const postPreviews = renderPostPreviews();

return (
<section>
<div><h1>Latest posts</h1></div>
<div>
{ postPreviews }
</div>
</section>
);
}

renderPostPreviews() {
return this.props.posts.map(post => this.renderPostPreview(post));
}

renderPostPreview(post) {
return (
<article>
<h3><a href={`/post/${post.slug}`}>{post.title}</a></h3>
<time pubdate><em>{post.posted}</em></time>
<div>
<span>{post.blurb}</span>
<a href={`/post/${post.slug}`}>Read more...</a>
</div>
</article>
);
}
}

这种写法并不是很糟糕。我们已经从 render() 方法中抽取了大量方法,并保证每一块代码都尽可能小并很好地命名。我们已经将 renderPostPreviews()方法封装地足够好了。让我们使用函数语法重写这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const LatestPostsComponent = props => {
const postPreviews = renderPostPreviews(props.posts);

return (
<section>
<div><h1>Latest posts</h1></div>
<div>
{ postPreviews }
</div>
</section>
);
};

const renderPostPreviews = posts => (
posts.map(post => this.renderPostPreview(post))
);

const renderPostPreview = post => (
<article>
<h3><a href={`/post/${post.slug}`}>{post.title}</a></h3>
<time pubdate><em>{post.posted}</em></time>
<div>
<span>{post.blurb}</span>
<a href={`/post/${post.slug}`}>Read more...</a>
</div>
</article>
);

两段段代码几乎一致,只是后面的没有了类的声明。然而对我来说这是很大的区别。在基于类的例子中,我会看到 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
14
const 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,你就不必总是检查 undefinednull
  • 它类似于文档,其他开发者就不必看完整个组件来看属性是否必须了。

八、使用 React 和 Redux dev tools

React 和 Redux dev tools 都是非常棒的工具。React dev tool 是你能够查阅已经渲染的 React 元素树,这对于查看浏览器中的视图很有帮助。Redux dev tool 更加出色,使你能查看发生的每一个动作,以及动作造成的状态改变,甚至给你能够回溯的能力。
你也可以设置热模块来替代 webpack,使你的页面在你的代码改变时就更新-不需要浏览器主动刷新。这使得反馈循环更加地高效。

# React

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×