真实项目 2026-10-02 踩坑 4:v2 壳给 /login 做「全屏直渲染」特例,写成
if (router.currentRoute.value.path === '/login') return <LoginPage />
构建通过,但登录成功跳 /designer 后壳不出现 —— 读信号发生在组件体,值在挂载时固化。
## 核验推翻了用户的对策,也暴露了我 C104 的误导
用户的对策是「根级两分支三元且两支均 JSX,JSX 求值期编译成 getter」。**实测编译产物:**
① return cond.value ? <A/> : <B/> → 无 insertDynamic,求值一次
② if (cond.value) return <A/>; return <B/> → 同上
③ return cond.value ? <A/> : null → 同上
**三者坏得一模一样。** 因为组件顶层 return 处**没有 parent/anchor**,而响应式条件渲染
需要一个能换内容的位置 —— 返回值就是那个节点本身。所以「编译成 getter」在顶层
return 位置不成立。
而 C104 只认 ③(判据是空字面量),**默许了 ① 与 ②** —— 它们同样冻结。
这就是踩坑 4 的形态,也是必须另立一条规则的原因。
## VOBS_C107
判据:**在 return 表达式里、且不在任何 JSX 子树内**读到了 .value。
return cond.value ? <A/> : <B/> // ❌ 冻结
return <div>{cond.value ? <A/> : <B/>}</div> // ✅ 子节点位置由编译期生成条件工厂
两者长得像、后果相反,所以「走进 JSX 就停」是这条规则的核心。也覆盖 if 早退式
(读取在 if 的 test 上,不在 return 表达式里,需要单独走一遍 IfStatement)。
仍是 **warning**(静态判不出「这个函数是不是组件」),仍只认 JSX 语法。
全仓 268 个文件零误报。
## C104 的 fix 文案不再暗示「提到顶层 return 可用」
原文案只说「放进 JSX 子节点位置」与 Show/classList,没有点明"提到顶层不管用"——
照着改的人可能正好写出 ①。现在明说:**把三元提到组件顶层 return 处不管用**。
(同时保留上一轮为「不依赖全局工具类」加的 classList 提示;中途我挤掉了它,
被既有的 c104-scope 测试拦住 —— 测试起到了作用。)
## 验证
- frozen-reactive-return.test.ts(13 例):① ② ③④⑤ 六种应报形态 ·
severity=warning · fix 文案含 RouterView/Show/"不管用";
**不该报**:JSX 子节点位置、JSX 属性位置、return 节点但没读信号、
读信号但返回数据、条件里无信号读取
- 11 条真实形态探针实测 **11/11 正确**
- 全仓 check:source **0 错误**(268 个文件零误报)
- 门禁曾因我改 C104 fix 文案而变红 1 条(我挤掉了 classList),修回后全绿
全量门禁 1382 测试全绿 · typecheck 0 · check:source 0。
## 已知边界(写进 CHANGELOG)
`if (cond.value) return null; return <B/>` 两条规则都不报(C104 需要条件表达式、
C107 需要 JSX 分支)。要检测它必须知道外层函数是不是组件,那会放宽对数据函数守卫的
误报 —— 权衡后不报。