应该有一个容器将组件名称映射到应该动态使用的所有组件。组件类应该在容器中注册,因为在模块化环境中,没有一个地方可以访问它们。如果不明确指定组件类,则无法通过其名称来识别它们,因为函数 name 在生产中被缩小了。
组件图
可以是普通对象:
class Foo extends React.Component { ... }
...
const componentsMap = { Foo, Bar };
...
const componentName = 'Fo' + 'o';
const DynamicComponent = componentsMap[componentName];
<DynamicComponent/>;
或Map 实例:
const componentsMap = new Map([[Foo, Foo], [Bar, Bar]]);
...
const DynamicComponent = componentsMap.get(componentName);
普通对象更适合,因为它受益于属性简写。
桶模块
具有命名导出的barrel module 可以充当这样的映射:
// Foo.js
export class Foo extends React.Component { ... }
// dynamic-components.js
export * from './Foo';
export * from './Bar';
// some module that uses dynamic component
import * as componentsMap from './dynamic-components';
const componentName = 'Fo' + 'o';
const DynamicComponent = componentsMap[componentName];
<DynamicComponent/>;
这适用于每个模块代码样式一个类。
装饰器
装饰器可以与类组件一起使用作为语法糖,这仍然需要明确指定类名并在映射中注册它们:
const componentsMap = {};
function dynamic(Component) {
if (!Component.displayName)
throw new Error('no name');
componentsMap[Component.displayName] = Component;
return Component;
}
...
@dynamic
class Foo extends React.Component {
static displayName = 'Foo'
...
}
装饰器可以用作带有功能组件的高阶组件:
const Bar = props => ...;
Bar.displayName = 'Bar';
export default dynamic(Bar);
使用non-standard displayName 代替随机属性也有利于调试。