仓库源文站点原文


layout: post title: 面试系列之三:你真的了解React吗(中)组件间的通信以及React优化 modified: 2017-07-06 tags: [javascript, react, interview] image: feature: abstract-3.jpg credit: dargadgetz creditlink: http://www.dargadgetz.com/ios-7-abstract-wallpaper-pack-for-iphone-5-and-ipod-touch-retina/ comments: true

share: true

本系列文章旨在完善你可能忽略的React相关的知识点,通过回答以下问题:

本文是本系列的中篇,之前的内容可以参考之前的本系列的前一篇文章:

组件间的通信和架构设计问题

在上一篇的文章中,我们主要聊了如何去设计一个好的组件。然而那个视角是独立的,甚至说是狭隘的,只考虑了的单个组件自己。接下来我们将考虑更复杂的情况,例如组件间的交互等等。

其实Facebook官方关于React最佳实践已经写的非常好的了,但可惜的是这些实践和工作原理、教程都混杂在一起,并且长篇累牍,让人阅读起来非常没有重点;另一方面如果你是个新手,或许你会看完所有的这些文档,但你还没有实践的经验,你满脑子想的其实是如何开始写你的第一个Hello World应用,所以这些最佳实践你也很快就忘了。

这一小节的主要内容都来自上面所说的Facebook关于React的官方文档,还有一部分来自一篇我认为很有价值,理念和官方文档一致但又写的更好的一篇文章:Flux in Depth. Overview and Components

组件间的通信问题

首先假设我们有一个用React构建的单页面应用,组件间的关系如下图所示:

sample-state-change

我们首先假设每一个组件都在维护自己的状态(state),而不是使用传递下来的props。那么问题来了,如果右侧的组件E想改变左侧的组件B的状态,应该怎么办?

这个例子其实想表达的是一类通用的业务场景,就是组件间状态的修改,也有可能是子组件修改父组件的状态。上面的这个例子是一类极端的情况,被修改的组件与发出修改的组件不在同一个父组件中。

Facebook的官方推荐办法(曾经是)是使用事件机制(现在这个页面已经跳转到另一个解决方案了,也就是下面要叙述的)。但我和绝大多数人都不认为这是一个好的机制。你可以想象一旦应用中这样的需求增多,事件和回调函数满天飞,则情况右回到了Flux之前的原地状态:调试和维护代码都很难进行,这会是一个灾难。

第二个方案是传递接口。如果E想改变B的状态,那么B要传递给E组件一个修改状态的接口。不过因为Flux属性是从上至下传递的关系,所以接口的传递应该是如下图所示:

sample-state-change-callbacks

所以事实上我们要从祖先元素Root传递两个接口分别给E和B。当E想改变B的状态时,E调用root传给它的那个接口,而后那个接口又调用root传给B的接口……听上去这也不是什么好办法。

Stateless Component

在介绍正确的姿势之前,我们需要引入一个概念,Stateless Component,你可以把它翻译为“无状态组件”。什么是无状态组件呢,它和 pure function(以下我们译为“纯粹函数”)的概念很相似,根据维基百科,一个纯粹函数应该具有以下特征:

根据上面的描述不难推断出我们的无状态组件其实也具有相同的特征:

正确的Flux架构

所以目前官方推荐的React组件和Flux架构的最佳实践是:

这样的原则有没有眼熟?这正是Vuex的架构思想,Vuex架构受Flux启发,同时也针对Flux的一些缺陷做出了改进,在下一篇文章中我会聊到Vuex与Flux的差异。

当然Stateless也不是绝对的,比如一些第三方组件,比如React-DND,就需要维护自己的状态。

state里应该有什么

最后提一句state里应该有什么。这个题目也是有标准答案的,在Interactivity and Dynamic UIs这篇文章里。

如何对组件进行优化

这一题也是有Facebook官方标准答案的:Optimizing Performance,扼要地摘取官方的建议由以下几点:

最后一条建议摘自官方描述的关于Virtual DOM的工作原理Reconciliation

Component与Element与Instance的区别

一般在编码中大部分人应该不会有这三个概念的疑惑。但如果阅读一些React相关的文章,可能就免不了和这三个概念打交道。既然我也被问到过,那么也就在这里普及一下。

Element

Element其实就是一个纯粹的Object对象,用于描述你在屏幕上看到的DOM结点。这个对象的属性包括type, props, ref, key,并不包括DOM的方法。

通常我们在开发中使用的都是JSX语法,同时利用Babel则将JSX语法转化为原生的React语法。

JSX语法中<div>Hello</div>即意味着一个元素,而Babel将它转化为原生的React语法脚本:

var helloWorld = React.createElement(
  "div",
  null,
  "Hello"
);

而事实上结果只是一个Javascript对象:

var helloWorld = {
    key: null,
    props: {
        children: "Hello"
    },
    ref: null,
    type: "div"
};

以上这三种格式都代表element

Component

Component就是组件级别的类或者说是Element的模:一个接收参数并且返回React元素的函数或者类。通常我们使用ES6的语法定义组件:

class CustomForm extends React.Component {

而以一个函数的形式语法可以是这样:

function CustomForm ({ addFriend }) {
  return {
    type: 'form',
    props: {
      onClick: addFriend,
      children: 'Add Friend'
    }
  }
}

当你定义完CustomForm之后,在页面上使用<CustomForm>标签即完成了从组件到元素的使用。

Instance

最后当你调用ReactDOM.render()把一个组件渲染到一个具体的DOM元素中,返回的值即使一个实例(instance):

var componentInstance = ReactDOM.render(<CustomForm />, document.getElementById("root"));

参考文章

https://www.site2share.com/folder/20020521

你可能会喜欢