
在 qwik 中,直接对 `usesignal` 的数组调用 `.push()` 不会触发 ui 更新,必须通过赋值新数组(如展开运算符)来改变引用,才能让框架检测到变化并重新渲染 dom。
Qwik 的响应式系统基于引用变更检测(reference-based reactivity),而非深度监听或代理劫持。这意味着:只有当 useSignal.value 被赋予一个全新的引用(例如新数组、新对象)时,Qwik 才会标记该信号为“已变更”,进而触发依赖它的 JSX 表达式重新求值与 DOM 更新。
在你的代码中:
sectionMenu.value.push(...sec); // ❌ 不会触发更新!
虽然 sectionMenu.value 内容确实改变了(console.log 可验证),但数组本身仍是同一个对象引用,Qwik 无法感知内部项的变化 —— 因此
- 中的 map 渲染逻辑不会重新执行,DOM 保持空白。
✅ 正确做法是替换整个数组引用:
sectionMenu.value = [...sectionMenu.value, ...sec]; // ✅ 触发更新
这创建了一个全新数组,Qwik 检测到 sectionMenu.value 引用变更,立即重运行 return 中的 JSX,sectionMenu.value.map(...) 得以执行,列表项正常渲染。
? 补充建议与注意事项:
- 避免原地修改:除 push() 外,pop()、splice()、sort()、reverse() 等所有原地数组方法均不触发更新;
-
推荐函数式更新模式(尤其在异步任务中):
useVisibleTask$(async () => { const sec = await extractSegments(articleContent.value as HTMLElement); sectionMenu.value = [...sectionMenu.value, ...sec]; // 明确、安全、可预测 }); - 若需清空数组,也应使用 sectionMenu.value = [],而非 sectionMenu.value.length = 0;
- 对于嵌套对象或深层结构变更,同样需确保路径上每一级引用都更新(例如 state.value = { ...state.value, items: [...state.value.items, newItem] })。
总结:Qwik 的 useSignal 是轻量、高性能的响应式原语,其设计哲学是「显式优于隐式」——你掌控何时更新,框架高效响应。牢记「变更引用即更新视图」这一核心原则,就能写出稳定、可维护的 Qwik 组件。










