Rust in Everything
基于 dioxus 0.7.10

常见坑

每一章对着一个报错或一个现象:先用最小的程序把它复现出来(输出是 M2 Max、macOS 27、rustc 1.99.0 上 dioxus 0.7.10 的原文,浏览器里的现象在 Chrome 156 上确认),再到源码里找它从哪一行来,最后给出修法和检查办法。机制在前面几部分讲过的,这里只链过去,不再重复。有几个坑是 dioxus 自己的问题,章里写明在哪个版本上复现、上游有没有修。

  1. Failed to borrow because the value was dropped:子组件把自己的 signal 交给父组件,子组件卸载后再读就是 ValueDroppedError;状态放进共同祖先。
  2. AlreadyBorrowed / AlreadyBorrowedMut:读守卫还活着时写同一个 signal,循环里或跨 .await 最常见;SyncSignal 上是死锁。先拷出值再写。
  3. 渲染时写 signal:组件不停重跑:渲染里读了又写同一个 signal,无限重跑且没有警告;派生值用 use_memo。
  4. Unable to retrieve the hook that was initialized at this index:hook 放进 if、match、循环,类型不同 panic,类型相同静默串值;dx check 查不出 rsx! 里的闭包。
  5. props 变了,子组件里的状态没跟着变:use_signal(|| props.x) 只在挂载时读一次;用 use_reactive!、ReadSignal props 或 key。
  6. 列表不写 key:删掉一项,状态错到下一行:没有 key 或用下标当 key,本地状态按位置对;key 用数据自己的 id。
  7. Must be called from inside a Dioxus runtime.:在 tokio::spawn、普通线程、JS 回调里调 hook 或 GlobalSignal;用 dioxus 的 spawn 或 RuntimeGuard。
  8. Suspended on a task that no longer exists:suspend()? 的资源被 cancel() 后整个 VirtualDom 崩掉;加载中 pause() 读值时 unwrap None。
  9. unexpected cfg condition value: server:server function 靠用户 crate 的 server feature 分出服务端;没打开时静默不注册,POST 得 405。
  10. server function 的错误都是 500:ServerFnError::new 和参数解析失败都是 500;要别的状态码得返回 HttpError、StatusCode 这类类型。
  11. 服务端错误原文送进了浏览器:ServerFnError::new(e.to_string()) 把内部错误原样发出去;SSR 时它成了 500 页的正文。
  12. 没登录也能直接 curl server function:每个 server function 都是公开路由,跨站 text/plain 表单也调得到;鉴权和来源检查写在函数体或中间件里。
  13. 请求体超过 2 MiB 返回 500:读 body 时对上限 unwrap,panic 成 500;按 Content-Length 拒绝挡不住分块请求。
  14. tried to serialize a value before serializing key:catch-all 查询配基本类型时客户端 panic;GET 留着 body 参数时浏览器拒绝发出。
  15. 渲染里阻塞,别的请求跟着慢:渲染和 server function 共用本地线程池,同步睡、同步 IO 卡住同线程上的其他请求;用 spawn_blocking。
  16. static 里的状态在请求之间串了:GlobalSignal 每个请求一份,static 是进程一份;用户状态按用户存。
  17. 打开增量缓存后,bob 看到了 alice 的页面:ISR 按 path 加 query 缓存整页,不看 cookie;命中时状态码变 200,内存缓存不过期。
  18. 首屏数据用了 use_resource:服务端不等它,首屏是空的;加 .suspend()? 又让水合崩掉。首屏数据用 use_server_future。
  19. 水合时两边渲染得不一样:两边第一次渲染的 hook 或内容不同,可能抛 TypeError、静默互换值;差异放进 use_effect。
  20. 乱序流式下晚设的状态码和响应头丢了:Suspense 边界里等完数据才设的状态码、响应头已经来不及;放在第一个边界之前等。
  21. prevent_default 没拦住跳转或提交:.await 之后才调已经晚了,0.7 的 onsubmit 不会自动拦;在第一个 .await 之前同步调用。
  22. TypeError: Cannot convert undefined or null to object:同一元素上去掉两个监听器中的一个,剩下的失效或整页停止渲染;监听器常驻,条件写进闭包。
  23. document::eval 的三个坑:JS 里 return 后通道不释放、语法错误抛穿 wasm、SSR 时只得到 Err(Finished);在 use_effect 里调用,用 dioxus.send 回传。
  24. script 里的动态文本不转义:SSR 不转义 script、style 里的文本,</script> 能注入标签;JSON 编码后用 dangerous_inner_html 写入。
  25. 自定义 404 页面返回 200:默认的 404 正文是列出全部路由的纯文本;加 catch-all 路由或 ErrorBoundary 后又变成 200。在那个组件里 commit_http_status(NOT_FOUND)。
  26. 站内跳转整页刷新,状态丢了:a { href } 或解析不了的 Link 字符串让浏览器整页请求,signal 归零;嵌套路由的根丢了高亮。用 Link 配路由枚举,高亮用 use_route 比较。

按出在哪里分:

出在哪里坑
signal 与所有权:GenerationalBox、借用守卫、订阅1 值已释放、2 重复借用、3 渲染时写
组件与 hook:hook 序号、props、key、diff4 hook 顺序、5 props 不更新、6 key
runtime 与异步任务:作用域栈、任务、Suspense7 runtime 之外、8 资源挂起、18 use_resource 首屏
server function:路由、编解码、错误9 feature、10 状态码、11 错误泄露、12 公开端点、13 请求体上限、14 参数
服务端渲染:线程池、每请求的 VirtualDom、缓存、流式15 阻塞、16 全局状态、17 增量缓存、20 晚设的响应头
水合与 web 渲染器:interpreter、事件、eval19 水合不一致、21 prevent_default、22 去掉监听器、23 eval、24 script 转义
路由:Routable、Link、首块提交25 404 状态码、26 整页刷新
flowchart TB
  q{"碰到了什么"}
  q -->|编译错误| ce{"哪一句"}
  ce -->|"unexpected cfg condition"| p9["9 server feature"]
  ce -->|"其他"| p14c["14 参数不能序列化"]
  q -->|panic 或整页错误| pn{"消息里有什么"}
  pn -->|"value was dropped"| p1["1 值已释放"]
  pn -->|"AlreadyBorrowed"| p2["2 重复借用"]
  pn -->|"hook ... at this index"| p4["4 hook 顺序"]
  pn -->|"inside a Dioxus runtime"| p7["7 runtime 之外"]
  pn -->|"task that no longer exists"| p8["8 资源挂起"]
  pn -->|"浏览器控制台 TypeError"| p1922["19 或 22"]
  q -->|HTTP 响应不对| hr{"哪种"}
  hr -->|"全是 500"| p10["10 或 13"]
  hr -->|"正文里有内部信息"| p11["11 错误泄露"]
  hr -->|"状态码或响应头丢了"| p20["20 或 17"]
  hr -->|"404 列出全部路由或 404 变 200"| p25["25 404 状态码"]
  hr -->|"别人的页面"| p17["17 增量缓存"]
  q -->|"没报错但行为不对"| sl{"哪种"}
  sl -->|"不停重跑"| p3["3 渲染时写"]
  sl -->|"显示旧值或错行"| p56["5 或 6"]
  sl -->|"首屏空白"| p18["18 use_resource"]
  sl -->|"请求互相拖慢"| p15["15 阻塞"]
  sl -->|"用户之间串数据"| p1617["16 或 17"]
  sl -->|"表单照样提交"| p21["21 prevent_default"]
  sl -->|"跳转后状态归零"| p26["26 整页刷新"]

💬 关联讨论

发起讨论
加载中...