本质上,这是一个关于内存泄漏可能性的警告,因为在没有适当的上下文的情况下创建了反应式计算,当不再需要时会对其进行处理。
正确的上下文可以通过几种不同的方式创建。以下是我所知道的:
- 通过使用
render
功能。
- 通过使用
createRoot
功能。在引擎盖下render
使用这个。
- 通过使用
createContext
功能。
第一种是迄今为止最常见的方式,因为每个应用程序至少有一个render
函数调用让整个节目开始。
那么是什么让代码“断章取义”呢?
最常见的方式可能是通过异步调用。仅当代码的同步部分完成运行时,才会创建上下文及其依赖树。这包括所有export default
模块中的函数和主应用程序函数。
但是由于以下原因而稍后运行的代码setTimeout
或者通过处于async
函数将在此上下文之外,并且创建的任何反应式计算都不会被跟踪,并且可能会保留下来而不会被垃圾收集。
一个例子
假设您有一个数据输入屏幕并有一个Save
按钮,向您的服务器发出 API 调用以保存数据。您希望通过漂亮的 HTML 格式消息向用户提供操作成功与否的反馈。
[msg,setMsg] = createSignal(<></>)
async function saveForm(){
...
setMsg(<p>Saving your data.<i>Please stand by...</i></p>)
const result=await callApi('updateUser',formData)
if(result.ok){
setMsg(<p>Your changes were <b>successfully</b> saved!</p> )
} else {
setMsg(<p>There was a problem saving your data! <br>Error: </p><pre>{result.error}</pre> )
}
}
...
<div>
...
<button onClick={saveForm} >Save</button>
{msg()}
</div>
当 API 调用返回错误时,这将产生上述警告,但其他时候则不会。为什么?
原因是 SolidJS 认为 JSX 中的代码插入是反应性的,即:需要监视和重新评估。因此,从 API 调用插入错误消息会创建反应式计算。
解决方案
我在 SolidJS 文档的最后找到了解决方案。这是一个特殊的 JSX 修饰符:/*@once*/
它可以用在大括号表达式的开头,并告诉 SolidJS 编译器明确不要使其成为反应式表达式。换句话说:当从 JSX 创建 DOM 节点时,它将评估一次且仅一次。
在上面的示例中,以下是如何使用它:
setMsg(<p>There was a problem saving your data! <br>Error: </p><pre>{
/*@once*/
result.error}</pre> )
此后将不再有警告消息:)
就我而言,我有一个输入,当该输入更改时,我重新创建了 SVG 绘图。因为 SVG 创建是一项昂贵的操作,所以我在createEffect
输入更改时运行的函数。debounce
是一种推迟处理直到输入停止变化至少 X 时间的技术。它涉及在内部运行 SVG 生成代码setTimeout
函数,因此位于主要上下文之外。使用/*@once*/
我在生成的 JSX 中插入表达式的所有地方的修饰符都解决了该问题。