Ember JS中{{did-update}}与<div {{did-update}}>的差异及循环问题
问题重现
当直接在模板中使用{{did-update}}而不包裹在DOM元素上时,会触发action无限循环:
无法正常运行的代码
{{did-update this.updateStates this.current.locationId}}
当this.current.locationId变化时,this.updateStates会被无限执行。
可正常运行的代码
<div {{did-update this.updateStates this.current.locationId}}> </div>
对应的组件JS代码:
@tracked property; @action updateStates() { // 使用this.current.locationId执行业务操作 // 修改@tracked标记的property值 // property变化会触发模板重新渲染 ... }
核心原因:追踪帧的上下文边界差异
这个问题确实和Ember的**追踪帧(Tracking Frame)**机制直接相关,关键在于did-update的执行上下文边界:
当
{{did-update}}作为模板顶层的独立表达式使用时,它没有绑定到任何DOM元素,属于无宿主修饰符,其执行逻辑会被纳入模板的根追踪帧中。在
updateStates中修改@tracked的property后,会触发模板的根追踪帧重渲染。此时根追踪帧会重新执行所有顶层内容,包括再次评估did-update的触发条件——哪怕this.current.locationId没有变化,也会重新调用updateStates。而updateStates又会再次修改property,进而再次触发根帧重渲染,形成无限循环。当用
<div>包裹后,did-update是作为元素修饰符绑定到具体DOM元素上的,此时它拥有独立的追踪帧边界。这个追踪帧只会监听依赖项this.current.locationId的变化,只有当该依赖项改变时才会触发updateStates。而updateStates中修改property只会触发模板中依赖property的部分重渲染,不会影响did-update所在的元素追踪帧(因为依赖项没变化),因此不会形成循环。
简单来说:无宿主的did-update和模板根帧绑定,任何根帧重渲染都会重复执行它;绑定到元素的did-update有独立追踪上下文,只响应指定依赖的变化。
内容的提问来源于stack exchange,提问作者KingStar1001

