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
模块里面的相关代码已经搜索不到了。