SolidJS:控制台日志中的“在`createRoot`或`render`之外创建的计算将永远不会被释放”消息

Ben*_*ida 17 warnings solid-js

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

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

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

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

Ben*_*ida 14

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

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

  • 通过使用该render功能。
  • 通过使用该createRoot功能。在引擎盖下render使用这个。
  • 通过使用该createContext功能。

第一种是迄今为止最常见的方式,因为每个应用程序至少有一个render函数调用来启动整个节目。

那么是什么让代码“断章取义”呢?

最常见的方式可能是通过异步调用。仅当代码的同步部分完成运行时,才会创建上下文及其依赖关系树。这包括export default模块中的所有功能和主应用程序功能。

setTimeout但是,由于 a或 位于函数中而稍后运行的代码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>

Run Code Online (Sandbox Code Playgroud)

当 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,因此位于主上下文之外。在生成的 JSX 中插入表达式的所有位置使用/*@once*/修饰符已经解决了问题。