問題描述
由於項目工程特別大,使用了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
是不可寫的?