LiveView Code Review
Quick Reference
| Issue Type | Reference |
|---|
| mount, handleparams, handleevent, handleasync | references/lifecycle.md |
| When to use assigns vs streams, AsyncResult |
references/assigns-streams.md |
| Function vs LiveComponent, slots, attrs |
references/components.md |
| Authorization per event, phx-value trust |
references/security.md |
Review Checklist
Critical Issues
- - [ ] No socket copying into async functions (extract values first)
- [ ] Every handle_event validates authorization
- [ ] No sensitive data in assigns (visible in DOM)
- [ ] phx-value data is validated (user-modifiable)
Lifecycle
- - [ ] Subscriptions wrapped in INLINECODE0
- [ ] handleparams used for URL-based state
- [ ] handleasync handles :loading and :error states
Data Management
- - [ ] Streams used for large collections (100+ items)
- [ ] temporary_assigns for data not needed after render
- [ ] AsyncResult patterns for loading states
Components
- - [ ] Function components preferred over LiveComponents
- [ ] LiveComponents preserve :inner_block in update/2
- [ ] Slots use proper attr declarations
- [ ] phx-debounce on text inputs
Valid Patterns (Do NOT Flag)
- - Empty mount returning {:ok, socket} - Valid for simple LiveViews
- Using assigns for small lists - Streams only needed for 100+ items
- LiveComponent without update/2 - Default update/2 assigns all
- phx-click without phx-value - Event may not need data
- Inline function in heex - Valid for simple transforms
Context-Sensitive Rules
| Issue | Flag ONLY IF |
|---|
| Missing debounce | Input is text/textarea AND triggers server event |
| Use streams |
Collection has 100+ items OR is paginated |
| Missing auth check | Event modifies data AND no auth in mount |
Critical Anti-Patterns
Socket Copying (MOST IMPORTANT)
CODEBLOCK0
Missing Authorization
CODEBLOCK1
Before Submitting Findings
Use the issue format: [FILE:LINE] ISSUE_TITLE for each finding.
Load and follow review-verification-protocol before reporting any issue.
LiveView 代码审查
快速参考
references/assigns-streams.md |
| 函数组件与 LiveComponent、slots、attrs |
references/components.md |
| 每个事件的授权、phx-value 信任 |
references/security.md |
审查清单
关键问题
- - [ ] 异步函数中不复制 socket(先提取值)
- [ ] 每个 handle_event 都验证授权
- [ ] assigns 中不包含敏感数据(在 DOM 中可见)
- [ ] phx-value 数据经过验证(用户可修改)
生命周期
- - [ ] 订阅包裹在 connected?(socket) 中
- [ ] handleparams 用于基于 URL 的状态
- [ ] handleasync 处理 :loading 和 :error 状态
数据管理
- - [ ] 大型集合(100+ 项)使用 Streams
- [ ] 渲染后不需要的数据使用 temporary_assigns
- [ ] 加载状态使用 AsyncResult 模式
组件
- - [ ] 函数组件优先于 LiveComponents
- [ ] LiveComponents 在 update/2 中保留 :inner_block
- [ ] Slots 使用正确的 attr 声明
- [ ] 文本输入使用 phx-debounce
有效模式(不要标记)
- - 空的 mount 返回 {:ok, socket} - 对简单 LiveView 有效
- 对小列表使用 assigns - 只有 100+ 项才需要 Streams
- 没有 update/2 的 LiveComponent - 默认 update/2 会分配所有内容
- 没有 phx-value 的 phx-click - 事件可能不需要数据
- heex 中的内联函数 - 对简单转换有效
上下文相关规则
| 问题 | 仅在以下情况标记 |
|---|
| 缺少防抖 | 输入是文本/文本域且触发服务器事件 |
| 使用 streams |
集合有 100+ 项或已分页 |
| 缺少授权检查 | 事件修改数据且 mount 中没有授权 |
关键反模式
Socket 复制(最重要)
elixir
错误 - socket 被复制到异步函数中
def handle
event(load, , socket) do
Task.async(fn ->
user = socket.assigns.user # Socket 被复制!
fetch_data(user.id)
end)
{:noreply, socket}
end
正确 - 先提取值
def handle
event(load, , socket) do
user_id = socket.assigns.user.id
Task.async(fn ->
fetch
data(userid) # 只复制了原始值
end)
{:noreply, socket}
end
缺少授权
elixir
错误 - 信任 phx-value 而没有授权
def handle_event(delete, %{id => id}, socket) do
Posts.delete_post!(id) # 任何人都可以删除任何帖子!
{:noreply, socket}
end
正确 - 验证授权
def handle_event(delete, %{id => id}, socket) do
post = Posts.get_post!(id)
if post.userid == socket.assigns.currentuser.id do
Posts.delete_post!(post)
{:noreply, stream_delete(socket, :posts, post)}
else
{:noreply, put_flash(socket, :error, 未授权)}
end
end
提交发现结果前
对每个发现结果使用问题格式:[文件:行号] 问题标题。
在报告任何问题之前,加载并遵循 审查验证协议。