借助 Webpack 静态分析能力实现代码动态加载
栏目: JavaScript · 发布时间: 5年前
内容简介:Debugger 一个动态配置代码异步加载引发的状态错误问题,想起以前在某厂学习的一个解决问题的方法论:最后从 Webpack 的角度利用静态代码分析的能力来解决问题。父组件 kitten.tsx
Debugger 一个动态配置代码异步加载引发的状态错误问题,想起以前在某厂学习的一个解决问题的方法论:
- 现象背后真实问题是啥?
- 真实问题背后原因是啥?
- 对策是要基于原因的,不是基于现象的。
最后从 Webpack 的角度利用静态代码分析的能力来解决问题。
现象
父组件 kitten.tsx
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.tsx
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 复制代码
上面顺序可能一个个看会看得眼花,而且这只是最外层的函数,里面还很深很杂,描述一下现象:
- 方法调用workspace.xxx 报错了,因为 workspace 还是 null
- 子组件先 render 没毛病,但是子组件里面的 init 方法似乎没有执行完就把控制权交回给父组件了
- 父组件 componentDidMount 中使用
setTimeout 0
企图将 render 任务推到 task 中,甚至这是个ajax
请求操作,但是在 ajax 请求完成后还是比BKBridge.init()
完成得要早,ajax 后面的操作用到了 workspace,但是它是 null - 最后
cc BKBridge.init after
打印出来了,workspace 初始化完成了
init 阻塞
// 简化后的调用过程 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,怎么办呢?
现象背后的问题
代码写成这样子,最初是为了解决动态加载代码的问题,如果 config().enable_test_mode
设置成 true
才接入 acceptance_test
相关的代码,否则就连代码都不要进入到打出来的包中。
问题背后的原因
所以我们的初衷是为了让某段代码可以通过配置决定是否打包进 boundle。这就好办了,可以不用把精力放在如何沟通父子组件上面,不用想类似代码暂停这种复杂化操作,只要利用 Webpack 打包时候的静态分析即可。
对策:Webpack 静态分析
webpack 读 config 文件
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; 复制代码
代码中直接写 require
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
模块里面的相关代码已经搜索不到了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。