Mithril中如何异步重绘指定DOM并避免onbeforeupdate触发无限循环
无限循环根因
onbeforeupdate钩子设计为同步返回布尔值决定是否跳过本次重绘,定义为async函数后同步执行阶段直接返回Promise,隐式判定为true,组件会先执行一次默认重绘。- 异步请求完成后手动调用
m.redraw()会触发新一轮重绘,再次进入onbeforeupdate钩子。 - 代码中没有在属性匹配变更后更新
this.prev的取值,导致每次进入钩子都会命中this.prev !== vnode.attrs.after的判断逻辑,反复触发异步请求和重绘,形成死循环。
修复方案
将异步逻辑从onbeforeupdate中抽离,属性变更后先更新状态标记避免重复触发,按需控制重绘时机:
oninit(vnode) { // 初始化时存储初始属性值 this.prev = vnode.attrs.after // 可按需触发初始化数据加载 this.loadData(vnode.attrs.after) } // 仅做同步属性比对,不处理异步逻辑 onbeforeupdate(vnode) { if (this.prev !== vnode.attrs.after) { // 第一时间更新状态标记,避免重复命中 this.prev = vnode.attrs.after // 触发异步数据加载 this.loadData(this.prev) // 跳过本次无意义重绘,等数据加载完成后再更新视图 return false } return true } async loadData(afterVal) { // 可选:增加加载态标记,方便视图展示加载状态 this.loading = true // 如果使用Mithril自带的m.request,请求结束后会自动触发重绘,无需手动调用m.redraw() this.listData = await asyncRequest(afterVal) this.loading = false // 仅当使用原生fetch/axios等第三方请求工具时,需要手动触发重绘 // m.redraw() } view() { if (this.loading) return m('p', '数据加载中...') // 渲染子组件,传入最新的listData return m('div', this.listData.map(item => m(ChildComponent, item))) }
额外优化点
- 可以在
loadData方法中增加请求防抖/中断逻辑,避免父组件属性快速变化时触发大量无效请求 - 加载状态可以根据业务需求替换为骨架屏等UI组件,提升用户体验
内容的提问来源于stack exchange,提问作者COLEAN
相关产品推荐
相关产品推荐

