一次打包错误 引起的对 React Components, Elements, Instance三者的认识

在一次项目重构中,我将filter组件需要的InputSelect之类通过Connect包裹后,通过<MyFromItem />这种形式当作参数传入。

大致的数据结构如下,component可以传入自定义组件或者直接传入<Input />这种形式;

1
2
3
4
5
6
7
8
[
{
name: 'name',
label: '姓名',
component: <StaffFormItem />,
}
...
]

在本地跑的时候,并没有发现问题;然后打包之后就发现跑不通,报错n is not a function之类的错误;

打开sourcemap之后定位到这个n是rc-form中的getFieldDecorator函数,遂寻找这个值未传入的原因。

调试了多次(打包实在是太慢了……),终于发现是判断类型的时候发生了问题:

1
2
3
4
5
6
// 这里传入了<StaffFormItem />
const type = component.type.name;
swith(type) {
case 'Connect': // 判断受控组件
return //...
}

打包之后,这里的type会从Connect变为n, j这种混淆后的形式,万万没想到。

解决问题之后去搜索了一下,出来了3个概念:

  • Components

  • Component Instances

  • Elements

在其他UI框架中的,一般只有模板(Class)和实例(new Class())这样的概念;而在React中,Component InstancesElements不是同一种类型的东西,它们之间没有一对一的关系;

看下面一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React, { Component } from 'react';

class MyComponent extends Component {
constructor(props) {
super(props);
console.log('这时候是Component Instance', this);
}

render() {
const another_element = <div>Hello World!</div>;
console.log('这里是一个element', another_element);
return another_element;
}
}

console.log('这是一个Component', MyComponent);

const element = <MyComponent />;

console.log('这是一个element', element);

上述这段代码,可以得出:

  • MyComponent(这个class自身)是一个Component

  • element是一个对象,但是它不是MyComponent的一个实例。它仅仅描述了一个对象实例(Component Instance)被创建了,它是一个拥有keypropsreftype这些属性的一个对象。在这里,keyref的值为null,props是一个空对象,type即为MyComponent字符串。

  • 一个MyComponent的实例将会在element被render之后被创建(在上述代码中,实例在构造函数中打印)

  • anthor_element也是一个对象,它的keypropsref和上面所述的一样,但是它的typediv

具体可以看React Components, Elements, and Instances

总结一下,可以看出React官方对这几个概念的定义非常清楚:

  • An element is a plain object describing a component instance or DOM node and its desired properties.

  • A ReactElement is a light, stateless, immutable, virtual representation of a DOM Element

Component可以被用来创建一个Instance,当Instance被render之后就创建了一个Element

创建实例的过程不需要我们手动进行;
多个Element可以描述同一个实例(比如<Parent />组件的render()返回了<Child />,每次触发render时都会返回一个新的element,但是已经存在Child实例可能被复用);
一个Elmenet也可以用于描述多个实例(比如把一个element存储到一个变量中const a = <Child />,然后调用多次<div>{a} {a} {a}</div>