问题描述
由于项目工程特别大,使用了import
函数与@loadable/component
结合进行组件的动态加载,从而实现Code Splitting
的效果。
如下是一个测试组件的代码:
// 这是一个组件
import React from 'react';
interface Props {};
export default class Demo extends React.Component<Props> {
render () {
return (
<div>this is a test component</div>
);
}
};
如下是一个组件动态加载的函数:
import loadable from '@loadable/component';
export const lazyLoad = (
loader: () => Promise<any>,
componentName?: string,
) => {
return loadable(
() => loader().then((module) => {
if (componentName) {
module.default = module[componentName];
}
return module;
})
);
};
如下是进行组件动态加载:
import { lazyLoad } from './lazyLoad';
const DemoComponent = lazyLoad(() => import('./Demo'), 'default')
如此操作之后,理想是丰满的现实是骨干的,报错了,报错调用栈如下:
问题解决
刚看到问题的时候一脸懵逼,内心wtf呀,这是什么鬼,什么造成了这个加载报错,仔细看这个报错的调用栈是调用@loadable/component
的loadable
函数时报错的。原因是loadable.esm.js:272 Uncaught TypeError: Cannot set property default of #<Object> which has only a getter at lazy-load.tsx:18
,仔细看一下是说无法对#<Object>
的default
属性进行设置,因为该属性是只读的。这更懵逼了,这是什么鬼,为什么不能对default
属性进行设置呢。看了一下代码只有在lazyLoad
时执行了module.default = module[componentName]
,难道是这个module
的default
属性是不可写的嘛?写个测试代码看一下:
// utils.ts
export function DemoFunc () {};
function decorate (obj) {
obj.displayName = 'DemoClass'
return obj
}
@decorate
export default class DemoClass {};
// import
import(
'./utils'
)
.then(module => {
console.log(module);
})
console.log
出来的内容:
同样的log
一下项目里面的module
:
同样的我们都发现了一个问题module
上面的default
属性是访问器属性是只读的,不可进行更改,但是可以对具名组件进行default
挂载,也就是说,可以将组件具名导出来,然后在lazyLoad
的时进行default
挂载。
demo如下:
// utils.ts
export function DemoFunc () {};
function decorate (obj) {
obj.displayName = 'DemoClass'
return obj
}
@decorate
export class DemoClass {};
// 动态加载
import(
'./utils'
)
.then(module => {
module.default = module.DemoClass;
console.log(module);
})
log
如下所示:
这个时候我们就看到了,已经成功的为module
中default
属性挂碍上具名组件了。
提问
虽然问题解决了,但依然有个疑问,求各位大神留言解答:
1、为什么经过装饰器修饰的类组件,export default
在module
上只读,而没有装饰器修饰类组件可读性也可写?
2、为什么import
函数动态加载的具名类module
是不可写的?