SolidJS:控制台日志中有 "computations created outside a `createRoot` or `render` will never be disposed" 条消息

SolidJS: "computations created outside a `createRoot` or `render` will never be disposed" messages in the console log

在处理 SolidJS 项目时,您可能会开始在 JS 控制台中看到以下警告消息:

computations created outside a `createRoot` or `render` will never be disposed

SolidJS 的 Github 存储库问题中提供了一些这方面的信息。但是在阅读它们之后我仍然不太确定这是怎么回事以及我的代码是否真的做错了什么。

我设法找到了它的来源,并根据文档找到了修复方法。所以我正在为那些谷歌搜索此警告消息的人提供解释和解决方案。

本质上,这是一个关于内存泄漏可能性的警告,因为在没有适当上下文的情况下创建了反应式计算,当不再需要时会处理它。

可以通过几种不同的方式创建适当的上下文。以下是我所知道的:

  • 通过使用 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 调用 returns 出错时产生上述警告,但其他时候不会。为什么?

原因是 SolidJS 认为 JSX 中的代码插入是反应性的,即:需要观察和重新评估。因此,从 API 调用中插入错误消息会创建一个反应式计算。

解决方案

我在 SolidJS 文档的最后找到了解决方案。这是一个特殊的 JSX 修饰符:/*@once*/

它可以用在大括号表达式的开头,它告诉 SolidJS 编译器明确不要将其设为反应式表达式。换句话说:当 DOM 节点是从 JSX 创建时,它只会被评估一次。

在上面的例子中,这里是如何使用它:

setMsg(<p>There was a problem saving your data! <br>Error: </p><pre>{/*@once*/ result.error}</pre> )

此后将不再有警告消息:)


在我的例子中,我有一个输入,当输入改变时,我重新创建了一个 SVG 绘图。因为 SVG 创建是一项昂贵的操作,所以我在输入更改时 运行 的 createEffect 函数中添加了去抖动。 debounce 是一种延迟处理直到输入停止变化至少 X 时间的技术。它涉及 运行 setTimeout 函数内的 SVG 生成代码,因此在主要上下文之外。在生成的 JSX 中插入表达式的任何地方使用 /*@once*/ 修饰符都解决了这个问题。