关于 liveview-code-review
面向 Phoenix LiveView 项目的代码审查技能,覆盖生命周期、数据管理、组件和安全检查,并通过审查门槛要求问题有经核实的证据支持。
主要功能
- 01LiveView 生命周期检查
- 02assigns、streams 与异步状态指导
- 03函数组件和 LiveComponent 审查
- 04授权与客户端数据校验检查
- 05证据及情境化审查门槛
适用场景
- 01审查 LiveView 事件的授权检查
- 02检查异步加载与错误处理
- 03评估大型集合的数据处理方式
- 04审查组件和 slot 的用法
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ existential-birds/ beagle --skill liveview-code-review安装后在 Claude Code 中输入/liveview-code-review确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/assigns-streams.mdcomponents.mdlifecycle.mdsecurity.md详细介绍
LiveView 代码审查
快速参考
| 问题类型 | 参考资料 |
|---|---|
| mount、handle_params、handle_event、handle_async | references/lifecycle.md |
| 何时使用 assigns 而非 streams、AsyncResult | references/assigns-streams.md |
| 函数组件与 LiveComponent、插槽、属性 | references/components.md |
| 按事件进行授权、信任 phx-value | references/security.md |
审查清单
严重问题
- 不要将 socket 复制到异步函数中(先提取值)
- 每个 handle_event 都会验证授权
- assigns 中没有敏感数据(这些数据会显示在 DOM 中)
- phx-value 数据经过验证(用户可以修改)
生命周期
- 订阅由
connected?(socket)包裹 - 使用 handle_params 管理基于 URL 的状态
- handle_async 处理 :loading 和 :error 状态
数据管理
- 大型集合(100 项以上)使用 streams
- 对渲染后不再需要的数据使用 temporary_assigns
- 使用 AsyncResult 模式处理加载状态
组件
- 优先使用函数组件,而非 LiveComponents
- LiveComponents 在 update/2 中保留 :inner_block
- 插槽使用恰当的 attr 声明
- 文本输入使用 phx-debounce
有效模式(不要标记)
- 空 mount 返回 {:ok, socket} - 对简单的 LiveView 有效
- 使用 assigns 保存小型列表 - 只有 100 项以上才需要使用 streams
- LiveComponent 未实现 update/2 - 默认的 update/2 会分配所有属性
- phx-click 未使用 phx-value - 事件可能不需要数据
- heex 中的内联函数 - 用于简单转换时有效
依上下文而定的规则
| 问题 | 仅在以下情况下标记 |
|---|---|
| 缺少防抖 | 输入为 text/textarea,且会触发服务端事件 |
| 使用 streams | 集合有 100 项以上,或采用分页 |
| 缺少授权检查 | 事件会修改数据,且 mount 中没有授权检查 |
严重反模式
复制 Socket(最重要)
# BAD - socket copied into async function
def handle_event("load", _, socket) do
Task.async(fn ->
user = socket.assigns.user # Socket copied!
fetch_data(user.id)
end)
{:noreply, socket}
end
# GOOD - extract values first
def handle_event("load", _, socket) do
user_id = socket.assigns.user.id
Task.async(fn ->
fetch_data(user_id) # Only primitive copied
end)
{:noreply, socket}
end
缺少授权
# BAD - trusts phx-value without auth
def handle_event("delete", %{"id" => id}, socket) do
Posts.delete_post!(id) # Anyone can delete any post!
{:noreply, socket}
end
# GOOD - verify authorization
def handle_event("delete", %{"id" => id}, socket) do
post = Posts.get_post!(id)
if post.user_id == socket.assigns.current_user.id do
Posts.delete_post!(post)
{:noreply, stream_delete(socket, :posts, post)}
else
{:noreply, put_flash(socket, :error, "Unauthorized")}
end
end
严格门槛(按顺序)
只有在每个通过条件都客观成立后才能继续(避免在没有证据的情况下报告问题):
| 门槛 | 通过条件 |
|---|---|
| G1 — 证据中的文件 | 你有明确的待审查路径列表(例如 *.ex、*.heex,或用户指定的路径)。每一项发现都要标明该列表中的文件。 |
| G2 — 验证协议 | 在将某项内容视为发现之前,你已加载 review-verification-protocol,并应用其报告前验证流程(以及相关的问题类型章节)。 |
| G3 — 行号锚点 | 每项发现均使用 [FILE:LINE],且该行确实存在于当前文件中(通过读取或 grep 输出确认,而非推断)。 |
| G4 — 有效模式筛查 | 你已根据有效模式(不要标记)和依上下文而定的规则检查该发现;如果它符合“不要标记”的情形,或不满足“仅在以下情况下标记”的条件,则不要报告。 |
问题格式
每项发现使用 [FILE:LINE] ISSUE_TITLE。
评论
支持 Markdown