You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SolidJS中For与Index组件的渲染差异及更新机制疑问

SolidJS For与Index组件的行为疑问

一、Index组件渲染结果与预期不符的问题

测试中发现,用Index组件渲染数组并更新后,实际渲染结果和预期不一致。

测试代码

import { render } from 'solid-js/web';
import { createSignal, For, Index } from 'solid-js';

function ForCats() {
  const [cats, setCats] = createSignal([
    'Keyboard Cat',
    'Maru',
    'Henri The Existential Cat'
  ]);
     
  setTimeout(() => setCats(['Keyboard Cat', 'Keyboard Cat', 'Keyboard Cat', 'New Cat']), 2000)

  return (
    <ul>
      <For each={cats()}>{(name, i) => {
          console.log(`For: rendered ${i()}:${name} whole cat`);
          return <li><a target="_blank" href="">1: {name}</a></li>
        }}</For>
    </ul>
  );
}

function IndexCats() {
  const [cats, setCats] = createSignal([
    'Keyboard Cat',
    'Maru',
    'Henri The Existential Cat'
  ]);
     
  setTimeout(() => setCats(['Keyboard Cat', 'Keyboard Cat', 'Keyboard Cat', 'New Cat']), 2000)

  return (
    <ul>
      <Index each={cats()}>{(name, i) => {
          console.log(`Index: rendered ${i}:${name()} whole cat`);
          return <li><a target="_blank" href="">1: {name()}</a></li>
        }}</Index>
    </ul>
  );
}

render(() => <> <ForCats /> <IndexCats/></>, document.getElementById('app'))

运行日志

For: rendered 0:Keyboard Cat whole cat
For: rendered 1:Maru whole cat
For: rendered 2:Henri The Existential Cat whole cat
Index: rendered 0:Keyboard Cat whole cat
Index: rendered 1:Maru whole cat
Index: rendered 2:Henri The Existential Cat whole cat
For: rendered 1:Keyboard Cat whole cat
For: rendered 2:Keyboard Cat whole cat
For: rendered 3:New Cat whole cat
Index: rendered 3:New Cat whole cat

预期与实际结果

  • 预期结果:Index组件保留原有的前三个元素内容(Keyboard Cat、Maru、Henri),新增第四个元素New Cat
  • 实际结果:Index组件前三个元素全部变为Keyboard Cat,和For组件渲染结果一致

原因解析

SolidJS中For和Index的核心差异在于元素追踪逻辑:

  • For组件:基于元素的身份追踪,通过引用或值的唯一性判断是否需要重新创建元素。当数组前三个元素替换为相同的"Keyboard Cat"时,For会认为索引1、2的元素身份发生变化,因此重新执行回调生成新元素。
  • Index组件:基于数组的索引位置追踪,只要索引存在,就复用该位置已创建的元素,仅更新元素内部依赖的响应式值。更新后的数组中,索引0、1、2依然存在,只是对应的值变成了"Keyboard Cat",因此Index会直接更新这三个位置元素的内容,不会保留原来的Maru和Henri,最终渲染结果和For一致。

二、IndexComp点击更新界面正常但无console.log输出的问题

测试中点击IndexComp的列表项时,界面能正确更新状态,但回调函数中的console.log没有二次输出。

测试代码

import { For, createSignal, Index } from "solid-js";

const initialTodos = [
  { content: "TODO A", complete: true },
  { content: "TODO B", complete: true },
  { content: "TODO C", complete: true },
];

function ForComp() {
  const [todos, setTodos] = createSignal(initialTodos);

  return (
    <For each={todos()}>
      {(todo, todoIdx) => {
        console.log("todo", todo, "todoIdx", todoIdx);
        return (
          <div>
            <h5
              style={{ cursor: "pointer" }}
              onClick={() => {
                const newTodos = [...todos()];
                newTodos[todoIdx()] = {
                  ...newTodos[todoIdx()],
                  complete: !newTodos[todoIdx()].complete,
                };
                setTodos(newTodos);
              }}
            >
              {todo.content}:{todo.complete.toString()}
            </h5>
          </div>
        );
      }}
    </For>
  );
}

function IndexComp() {
  const [todos, setTodos] = createSignal(initialTodos);

  return (
    <div>
      <Index each={todos()}>
        {(todo, todoIdx) => {
          console.log("todo", todo, "todoIdx", todoIdx);
          return (
            <div>
              <h5
                style={{ cursor: "pointer" }}
                onClick={() => {
                  const newTodos = [...todos()];
                  newTodos[todoIdx] = {
                    ...newTodos[todoIdx],
                    complete: !newTodos[todoIdx].complete,
                  };
                  setTodos(newTodos);
                }}
              >
                {todo().content}:{todo().complete.toString()}
              </h5>
            </div>
          );
        }}
      </Index>
    </div>
  );
}

function App() {
  return (
    <>
      <h1>For</h1>
      <ForComp />
      <hr />
      <h1>Index</h1>
      <IndexComp />
    </>
  );
}

export default App;

原因解析

SolidJS采用细粒度响应式更新,Index组件的回调函数仅在初始化时为每个索引位置执行一次,用于创建对应的DOM元素。当点击列表项更新数组中某个索引的值时:

  1. Index组件检测到对应索引位置的响应式值(todo())发生变化
  2. 直接更新该DOM元素内部绑定的响应式内容(todo().content、todo().complete)
  3. 不会重新执行回调函数,因此console.log不会再次触发

这种机制保证了更新的高效性,避免不必要的组件重建,仅更新变化的部分。


内容的提问来源于stack exchange,提问作者seongkuk han

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 15:17:33