Debugger 一個動態配置代碼異步加載引起的狀態錯誤問題,想起之前在某廠學習的一個解決問題的方法論:html
最後從 Webpack 的角度利用靜態代碼分析的能力來解決問題。webpack
父組件 kitten.tsxweb
componentDidMount() {
console.log('cc kitten didMount');
setTimeout(
() => {
console.log('cc kitten render_content before');
this.render_content();
console.log('cc kitten render_content after');
},
0,
);
}
render_content() {
console.log('cc action_fetch_bcm_by_url before');
// 一個異步操做,拉取到在線資源後會調用 workspace 上的方法
this.props.action_fetch_bcm_by_url();
console.log('cc action_fetch_bcm_by_url after');
}
複製代碼
子組件 BKWorkspaceContainer.tsxajax
async componentDidMount() {
console.log('cc BKWorkspaceContainer didMount');
console.log('cc BKBridge.init before');
// 初始化 workspace,初始化完成前爲 null
await BKBridge.init();
console.log('cc BKBridge.init after');
}
複製代碼
cc BKWorkspaceContainer didMount
cc BKBridge.init before
cc kitten didMount
cc kitten render_content before
cc kitten render_content after
cc action_fetch_bcm_by_url before
cc action_fetch_bcm_by_url after
cc BKBridge.init after
複製代碼
上面順序可能一個個看會看得眼花,並且這只是最外層的函數,裏面還很深很雜,描述一下現象:bash
setTimeout 0
企圖將 render 任務推到 task 中,甚至這是個 ajax
請求操做,可是在 ajax 請求完成後仍是比 BKBridge.init()
完成得要早,ajax 後面的操做用到了 workspace,可是它是 nullcc BKBridge.init after
打印出來了,workspace 初始化完成了// 簡化後的調用過程
if (config().enable_test_mode) {
await register_test_block();
}
const workspace_panel = new WorkspacePanel(block_xml);
// register_test_block 代碼
async function register_test_block(registry:Registry) {
return new Promise((resolve) => {
require.ensure([], function(require){
const { register_test_blocks } = require('../acceptance_test');
register_test_blocks(registry);
registry.load_all_block_definitions_into_bk(BK);
resolve();
});
});
}
複製代碼
用了 require.ensure
這種方式來異步加載代碼,代碼執行到這一段的時候纔去拉 JS,因此會出現比 ajax 還慢的狀況,它是異步的。直接阻塞了後面 new WorkspacePanel(block_xml)
的執行。異步
componentDidMount() {
setTimeout(
() => {
this.render_content();
},
100,
);
}
複製代碼
經過父組件設置 100ms 的延時,問題就不存在了,可是若是 require('../acceptance_test')
的時間超過了 100ms,怎麼辦呢?async
代碼寫成這樣子,最初是爲了解決動態加載代碼的問題,若是 config().enable_test_mode
設置成 true
才接入 acceptance_test
相關的代碼,不然就連代碼都不要進入到打出來的包中。函數
因此咱們的初衷是爲了讓某段代碼能夠經過配置決定是否打包進 boundle。這就好辦了,能夠不用把精力放在如何溝通父子組件上面,不用想相似代碼暫停這種複雜化操做,只要利用 Webpack 打包時候的靜態分析便可。post
const runtime_cfg = require('../config')();
module.exports = {
// ...
plugins: [
new webpack.DefinePlugin({
'__TEST_MODE__': runtime_cfg.client.enable_test_mode
}),
],
// ...
}
複製代碼
爲全局定義一個 __TEST_MODE__
變量,在代碼的任何地方均可以使用,固然若是是 ts 代碼的話須要配置:學習
// global.d.ts
declare var __TEST_MODE__:boolean;
複製代碼
if (__TEST_MODE__) {
const { register_test_blocks } = require('../acceptance_test');
register_test_blocks(registry);
registry.load_all_block_definitions_into_bk(BK);
}
複製代碼
// config.ts
"enable_test_mode": true
複製代碼
// config.ts
"enable_test_mode": false
複製代碼
能夠看到,搜索 register_test_blocks
模塊裏面的相關代碼已經搜索不到了。