<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh_CN">
  <title>石头鱼的技术札记</title>
  <subtitle>欢迎</subtitle>
  <link href="https://stonefish.space/" rel="alternate" type="text/html"/>
  <link href="https://stonefish.space/atom.xml" rel="self" type="application/atom+xml"/>
  <id>https://stonefish.space/</id>
  <updated>2026-03-23T00:00:00.000Z</updated>
  <entry>
    <title>Git 常用简写说明书</title>
    <link href="https://stonefish.space/git-alias/" rel="alternate" type="text/html"/>
    <id>https://stonefish.space/git-alias/</id>
    <published>2026-03-23T00:00:00.000Z</published>
    <updated>2026-03-23T00:00:00.000Z</updated>
    <summary>Git 常用简写说明书</summary>
    <content type="html"><![CDATA[<p>这份文档只讲你平时最常用的这几个：</p>
<ul>
<li><code>gst</code></li>
<li><code>gco</code></li>
<li><code>grb</code></li>
<li><code>gm</code></li>
<li><code>gp</code></li>
<li><code>gpf</code></li>
<li><code>gl</code></li>
<li><code>grh</code></li>
</ul>
<p>目标不是“背命令”，而是让你真的知道：</p>
<ol>
<li>这个缩写到底等于什么命令</li>
<li>它到底会对仓库做什么</li>
<li>常见参数分别是什么意思</li>
<li>什么时候该用，什么时候别乱用</li>
<li>实战里一般怎么连起来用</li>
<li>哪些地方最容易翻车</li>
</ol>
<hr />
<h1>1. 先给你一个总览</h1>
<table>
<thead>
<tr>
<th>缩写</th>
<th>原命令</th>
<th>核心动作</th>
<th>危险等级</th>
<th>常用指数</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>gst</code></td>
<td><code>git status</code></td>
<td>看状态</td>
<td>低</td>
<td>★★★★★</td>
</tr>
<tr>
<td><code>gco</code></td>
<td><code>git checkout</code></td>
<td>切分支 / 恢复文件（旧方式）</td>
<td>中</td>
<td>★★★★☆</td>
</tr>
<tr>
<td><code>grb</code></td>
<td><code>git rebase</code></td>
<td>变基、整理历史</td>
<td>中高</td>
<td>★★★☆☆</td>
</tr>
<tr>
<td><code>gm</code></td>
<td><code>git merge</code></td>
<td>合并分支</td>
<td>中</td>
<td>★★★★☆</td>
</tr>
<tr>
<td><code>gl</code></td>
<td><code>git pull</code></td>
<td>拉取并合并远程更新</td>
<td>中</td>
<td>★★★★★</td>
</tr>
<tr>
<td><code>gp</code></td>
<td><code>git push</code></td>
<td>推送到远程</td>
<td>中</td>
<td>★★★★★</td>
</tr>
<tr>
<td><code>gpf</code></td>
<td><code>git push --force-with-lease</code></td>
<td>更安全的强推</td>
<td>高</td>
<td>★★☆☆☆</td>
</tr>
<tr>
<td><code>grh</code></td>
<td><code>git reset</code></td>
<td>重置提交/暂存状态</td>
<td>中高</td>
<td>★★★☆☆</td>
</tr>
</tbody>
</table>
<h2>这张表怎么理解</h2>
<h3>危险等级</h3>
<ul>
<li><strong>低</strong>：几乎不会改坏东西</li>
<li><strong>中</strong>：会改变分支状态、远程状态或合并历史，需要看清楚再敲</li>
<li><strong>高</strong>：可能覆盖历史、丢改动、影响队友</li>
</ul>
<h3>常用指数</h3>
<p>这是按<strong>日常团队开发里的主观实用度</strong>打的，不是官方排名。</p>
<ul>
<li>★★★★★：几乎天天会用</li>
<li>★★★★☆：非常常用</li>
<li>★★★☆☆：比较常用，但不是每次都用</li>
<li>★★☆☆☆：场景性很强</li>
</ul>
<hr />
<h1>2. 先弄懂 4 个基本概念</h1>
<p>在看这些命令前，你先记住下面四个位置：</p>
<h2>2.1 工作区（Working Tree）</h2>
<p>你正在编辑的文件。</p>
<h2>2.2 暂存区（Staging Area）</h2>
<p>你打算放进下一次提交的改动。</p>
<h2>2.3 本地提交历史（Commit History）</h2>
<p>你本地已经提交过的历史记录。</p>
<h2>2.4 远程仓库（Remote）</h2>
<p>GitHub / GitLab 上的仓库内容。</p>
<p>很多 Git 命令，本质上就是在这四个位置之间“搬东西”或者“对齐状态”。</p>
<hr />
<h1>3. <code>gst</code> —— 看当前到底是什么状态</h1>
<h2>原命令</h2>
<pre><code>git status
</code></pre>
<h2>作用</h2>
<p>这是 Git 里最重要的查看命令之一。</p>
<p>它不会改任何东西，只负责告诉你：</p>
<ul>
<li>你现在在哪个分支</li>
<li>当前分支和远程比，是领先还是落后</li>
<li>哪些文件已经暂存</li>
<li>哪些文件只是修改了、还没暂存</li>
<li>哪些文件是新文件</li>
<li>有没有冲突</li>
</ul>
<h2>常用指数</h2>
<p><strong>★★★★★</strong></p>
<h2>危险等级</h2>
<p><strong>低</strong></p>
<h2>你会看到什么</h2>
<p>典型输出会包括：</p>
<ul>
<li><code>On branch feat/login</code></li>
<li><code>Your branch is ahead of 'origin/feat/login' by 1 commit</code></li>
<li><code>Changes to be committed</code></li>
<li><code>Changes not staged for commit</code></li>
<li><code>Untracked files</code></li>
</ul>
<h2>常见使用场景</h2>
<h3>场景 1：提交前先确认</h3>
<p>你改完代码之后，不要急着提交，先 <code>gst</code> 看一眼。</p>
<h3>场景 2：切分支前确认工作区是否干净</h3>
<p>切分支、rebase、merge 前，先看有没有没处理的改动。</p>
<h3>场景 3：推送前确认有没有漏提交</h3>
<p>你以为自己都提交完了，<code>gst</code> 一看还有没 add 的东西。</p>
<h2>常见搭配</h2>
<pre><code>gst
gco main
gst
gl
gst
</code></pre>
<h2>实战例子</h2>
<h3>例子 A：开发完准备提交</h3>
<pre><code>gst
</code></pre>
<p>你看到：</p>
<ul>
<li><code>modified: src/login.ts</code></li>
<li><code>modified: src/api.ts</code></li>
</ul>
<p>说明你改了两个文件，但还没暂存。</p>
<h3>例子 B：你准备推送</h3>
<pre><code>gst
</code></pre>
<p>看到：</p>
<ul>
<li><code>Your branch is ahead of 'origin/feat/login' by 2 commits</code></li>
</ul>
<p>这就说明你本地有 2 个提交还没推上去。</p>
<h2>提醒</h2>
<h3>1. <code>gst</code> 不会修任何问题</h3>
<p>它只负责“告诉你发生了什么”。</p>
<h3>2. 一切危险操作前都先跑一次 <code>gst</code></h3>
<p>尤其是：</p>
<ul>
<li><code>grb</code></li>
<li><code>gm</code></li>
<li><code>gl</code></li>
<li><code>gpf</code></li>
<li><code>grh</code></li>
</ul>
<h3>3. 小白最容易忽略的一点</h3>
<p>很多人“以为自己知道当前状态”，结果其实不知道。<code>gst</code> 就是防止这种自信翻车的第一道保险。</p>
<hr />
<h1>4. <code>gco</code> —— 切分支，或者恢复文件（旧时代万能刀）</h1>
<h2>原命令</h2>
<pre><code>git checkout
</code></pre>
<h2>作用</h2>
<p><code>checkout</code> 是 Git 早期特别常用的一个命令，功能很多，主要两大类：</p>
<ol>
<li><strong>切换分支</strong></li>
<li><strong>把文件恢复到某个版本</strong></li>
</ol>
<p>也正因为它功能太多，所以后来 Git 才拆出了更清晰的：</p>
<ul>
<li><code>git switch</code>：专门切分支</li>
<li><code>git restore</code>：专门恢复文件</li>
</ul>
<h2>常用指数</h2>
<p><strong>★★★★☆</strong></p>
<h2>危险等级</h2>
<p><strong>中</strong></p>
<h2>最常见的两种用法</h2>
<h3>用法 1：切换分支</h3>
<pre><code>gco main
</code></pre>
<p>意思：切到 <code>main</code> 分支。</p>
<h3>用法 2：恢复某个文件</h3>
<pre><code>gco -- src/login.ts
</code></pre>
<p>意思：把这个文件恢复成当前提交里的版本，丢掉你在工作区的改动。</p>
<p>这第二种用法很危险。</p>
<h2>常见参数</h2>
<h3><code>gco &lt;branch&gt;</code></h3>
<p>切换到某个已存在分支。</p>
<p>例子：</p>
<pre><code>gco develop
</code></pre>
<h3><code>gco -b &lt;new-branch&gt;</code></h3>
<p>创建并切换到新分支。</p>
<p>例子：</p>
<pre><code>gco -b feat/user-center
</code></pre>
<p>不过在 oh-my-zsh 里通常更常写成：</p>
<pre><code>gcb feat/user-center
</code></pre>
<h3><code>gco -- &lt;file&gt;</code></h3>
<p>恢复单个文件到当前提交版本。</p>
<p>例子：</p>
<pre><code>gco -- package.json
</code></pre>
<h3><code>gco &lt;commit&gt; -- &lt;file&gt;</code></h3>
<p>从某个历史提交里拿回一个文件版本。</p>
<p>例子：</p>
<pre><code>gco a1b2c3d -- src/config.ts
</code></pre>
<h2>应用场景</h2>
<h3>场景 1：切到主分支拉最新代码</h3>
<pre><code>gco main
gl
</code></pre>
<h3>场景 2：切回自己的功能分支继续开发</h3>
<pre><code>gco feat/login-refactor
</code></pre>
<h3>场景 3：某个文件改乱了，想撤回</h3>
<pre><code>gco -- src/login.ts
</code></pre>
<h2>实战例子</h2>
<h3>例子 A：你要开始新功能</h3>
<pre><code>gco main
gl
gco -b feat/payment-page
</code></pre>
<p>意思是：</p>
<ol>
<li>切到主分支</li>
<li>拉最新代码</li>
<li>基于最新主分支开一个新功能分支</li>
</ol>
<h3>例子 B：你误改了一个文件</h3>
<pre><code>gst
gco -- src/mock.ts
gst
</code></pre>
<p>恢复后再看状态，就会发现这个文件不再显示修改。</p>
<h2>提醒</h2>
<h3>1. <code>gco -- 文件名</code> 会直接丢掉你没提交的修改</h3>
<p>如果你还想留着，就别直接用。</p>
<h3>2. 现在新手更建议学 <code>git switch</code> 和 <code>git restore</code></h3>
<p>因为语义更清楚，不容易混。</p>
<h3>3. 切分支失败时，通常不是 Git 坏了</h3>
<p>大概率是：</p>
<ul>
<li>你有未提交改动</li>
<li>目标分支不存在</li>
<li>你当前改动会和目标分支冲突</li>
</ul>
<hr />
<h1>5. <code>grb</code> —— 变基，把你的提交“重新排队”</h1>
<h2>原命令</h2>
<pre><code>git rebase
</code></pre>
<h2>作用</h2>
<p><code>rebase</code> 的核心是：</p>
<blockquote>
<p>把当前分支上的提交，重新接到另一个基底后面。</p>
</blockquote>
<p>你可以把它理解成：</p>
<p>“我这条分支原来是从旧的主分支上长出来的，现在我想把它挪到最新的主分支后面。”</p>
<h2>常用指数</h2>
<p><strong>★★★☆☆</strong></p>
<h2>危险等级</h2>
<p><strong>中高</strong></p>
<h2>为什么要用它</h2>
<p>主要两个原因：</p>
<h3>1. 同步主分支最新代码</h3>
<p>你的功能分支开发了一半，主分支已经前进了。你可以把自己的分支 rebase 到最新主分支上。</p>
<h3>2. 整理提交历史</h3>
<p>把历史弄得更直、更干净，方便 review。</p>
<h2>最常见用法</h2>
<h3>用法 1：把当前分支 rebase 到 main</h3>
<pre><code>grb main
</code></pre>
<h3>用法 2：把当前分支 rebase 到最新远程主分支</h3>
<pre><code>grb origin/main
</code></pre>
<h3>用法 3：交互式 rebase</h3>
<pre><code>grbi HEAD~3
</code></pre>
<p>意思：整理最近 3 个提交。</p>
<h2>常见参数</h2>
<h3><code>grb &lt;branch&gt;</code></h3>
<p>把当前分支变基到某个分支上。</p>
<h3><code>grbi</code></h3>
<p>对应：</p>
<pre><code>git rebase --interactive
</code></pre>
<p>作用：交互式编辑提交历史。</p>
<h3><code>grba</code></h3>
<p>对应：</p>
<pre><code>git rebase --abort
</code></pre>
<p>作用：rebase 出问题了，放弃本次 rebase。</p>
<h3><code>grbc</code></h3>
<p>对应：</p>
<pre><code>git rebase --continue
</code></pre>
<p>作用：解决冲突后继续。</p>
<h3><code>grbs</code></h3>
<p>对应：</p>
<pre><code>git rebase --skip
</code></pre>
<p>作用：跳过当前这个冲突提交。</p>
<h2>应用场景</h2>
<h3>场景 1：提 PR 前同步主分支</h3>
<pre><code>gco main
gl
gco feat/login
grb main
</code></pre>
<h3>场景 2：让提交历史更干净</h3>
<p>你做了 5 个零碎提交，准备合并前用交互式 rebase 整理。</p>
<h3>场景 3：团队要求“不要 merge main，统一 rebase main”</h3>
<p>很多团队会有这种规范。</p>
<h2>实战例子</h2>
<h3>例子 A：同步主分支最新提交</h3>
<p>你在 <code>feat/login</code> 分支上开发两天了，这两天 <code>main</code> 又进了很多提交。</p>
<p>你做：</p>
<pre><code>gco main
gl
gco feat/login
grb main
</code></pre>
<p>结果：</p>
<ul>
<li>你的功能分支看起来像是基于最新 <code>main</code> 开始写的</li>
<li>历史通常会比 merge 更直</li>
</ul>
<h3>例子 B：rebase 过程中冲突</h3>
<pre><code>grb main
</code></pre>
<p>报冲突了。</p>
<p>你需要：</p>
<ol>
<li>打开冲突文件解决冲突</li>
<li><code>git add</code> 把冲突解决结果标记好</li>
<li>运行：</li>
</ol>
<pre><code>grbc
</code></pre>
<p>如果你不想继续了：</p>
<pre><code>grba
</code></pre>
<h2>提醒</h2>
<h3>1. rebase 本质上是在“改写提交历史”</h3>
<p>这意味着同一段改动会生成新的提交 ID。</p>
<h3>2. 已经推送给团队、别人也在基于它工作的分支，不要轻易 rebase 后强推</h3>
<p>否则别人会很痛苦。</p>
<h3>3. 小白的基本原则</h3>
<ul>
<li><strong>自己本地分支</strong>：可以 rebase</li>
<li><strong>团队共享分支</strong>：慎重 rebase</li>
</ul>
<h3>4. rebase 不是更高级的 merge</h3>
<p>它只是另一种整合历史的方法，没有绝对谁更高级。</p>
<hr />
<h1>6. <code>gm</code> —— 合并分支，把另一条线并进来</h1>
<h2>原命令</h2>
<pre><code>git merge
</code></pre>
<h2>作用</h2>
<p><code>merge</code> 是把别的分支改动并入当前分支。</p>
<p>你可以理解成：</p>
<blockquote>
<p>“把那条开发线的成果接进我现在站的这条线上。”</p>
</blockquote>
<h2>常用指数</h2>
<p><strong>★★★★☆</strong></p>
<h2>危险等级</h2>
<p><strong>中</strong></p>
<h2>最常见用法</h2>
<h3>用法 1：把主分支合进当前功能分支</h3>
<pre><code>gm main
</code></pre>
<h3>用法 2：把功能分支合进主分支</h3>
<pre><code>gco main
gm feat/login
</code></pre>
<h2>常见参数</h2>
<h3><code>gm &lt;branch&gt;</code></h3>
<p>合并指定分支到当前分支。</p>
<h3><code>gma</code></h3>
<p>对应：</p>
<pre><code>git merge --abort
</code></pre>
<p>合并冲突时，放弃这次 merge。</p>
<h3><code>gmc</code></h3>
<p>对应：</p>
<pre><code>git merge --continue
</code></pre>
<p>解决冲突后继续。</p>
<h3><code>gms</code></h3>
<p>对应：</p>
<pre><code>git merge --squash
</code></pre>
<p>把对方分支的多个提交压成一个待提交改动。</p>
<h3><code>gmff</code></h3>
<p>对应：</p>
<pre><code>git merge --ff-only
</code></pre>
<p>只允许快进合并，不允许产生 merge commit。</p>
<h2>merge 和 rebase 的直观区别</h2>
<h3>merge</h3>
<ul>
<li>不改已有提交历史</li>
<li>会把分支合并痕迹保留下来</li>
<li>更适合保留真实开发轨迹</li>
</ul>
<h3>rebase</h3>
<ul>
<li>改写当前分支提交历史</li>
<li>历史更直</li>
<li>更适合整理后再合并</li>
</ul>
<h2>应用场景</h2>
<h3>场景 1：你想保留真实合并关系</h3>
<p>那就用 merge。</p>
<h3>场景 2：团队不要求线性历史</h3>
<p>merge 很正常。</p>
<h3>场景 3：把 <code>main</code> 合进功能分支解决兼容问题</h3>
<pre><code>gco feat/order
gm main
</code></pre>
<h2>实战例子</h2>
<h3>例子 A：把主分支最新改动并进功能分支</h3>
<pre><code>gco main
gl
gco feat/payment
gm main
</code></pre>
<p>这样做完后，你的功能分支就包含主分支最新改动。</p>
<h3>例子 B：merge 冲突处理</h3>
<pre><code>gm main
</code></pre>
<p>冲突了。</p>
<p>你需要：</p>
<ol>
<li>编辑冲突文件</li>
<li><code>git add</code> 标记冲突已解决</li>
<li>如果 Git 要求继续：</li>
</ol>
<pre><code>gmc
</code></pre>
<p>不想继续：</p>
<pre><code>gma
</code></pre>
<h2>提醒</h2>
<h3>1. merge 不等于安全无脑</h3>
<p>它虽然不改历史，但照样会产生冲突。</p>
<h3>2. 你当前站在哪个分支，非常重要</h3>
<p><code>gm xxx</code> 的意思是：<strong>把 xxx 合进当前分支</strong>。</p>
<p>很多人会把方向搞反。</p>
<h3>3. 小白口诀</h3>
<p>先 <code>gst</code>，再看自己站在哪个分支，再 merge。</p>
<hr />
<h1>7. <code>gl</code> —— 拉远程更新到本地</h1>
<h2>原命令</h2>
<pre><code>git pull
</code></pre>
<h2>作用</h2>
<p><code>pull</code> 本质上约等于：</p>
<ol>
<li><code>git fetch</code></li>
<li>再把拉下来的东西整合进当前分支</li>
</ol>
<p>默认整合方式通常是 <strong>merge</strong>。</p>
<p>所以你可以把 <code>gl</code> 理解成：</p>
<blockquote>
<p>“从远程把最新东西拿下来，并试着和我当前分支合到一起。”</p>
</blockquote>
<h2>常用指数</h2>
<p><strong>★★★★★</strong></p>
<h2>危险等级</h2>
<p><strong>中</strong></p>
<h2>最常见用法</h2>
<h3>用法 1：更新主分支</h3>
<pre><code>gco main
gl
</code></pre>
<h3>用法 2：更新你当前分支对应的远程分支</h3>
<pre><code>gco feat/login
gl
</code></pre>
<h2>常见参数</h2>
<h3><code>gl</code></h3>
<p>默认 pull。</p>
<h3><code>gpr</code></h3>
<p>对应：</p>
<pre><code>git pull --rebase
</code></pre>
<p>作用：拉取后用 rebase 方式整合，而不是 merge。</p>
<h3><code>gpra</code></h3>
<p>对应：</p>
<pre><code>git pull --rebase --autostash
</code></pre>
<p>作用：有未提交改动时，先自动 stash，再 pull rebase，最后恢复改动。</p>
<h3><code>ggpull</code></h3>
<p>对应：</p>
<pre><code>git pull origin 当前分支
</code></pre>
<p>更明确地拉当前分支的远程对应分支。</p>
<h2>应用场景</h2>
<h3>场景 1：每天开工先更新主分支</h3>
<pre><code>gco main
gl
</code></pre>
<h3>场景 2：切回老分支前先同步</h3>
<pre><code>gco feat/profile
gl
</code></pre>
<h3>场景 3：你准备 rebase 前，先把主分支拉到最新</h3>
<pre><code>gco main
gl
gco feat/order
grb main
</code></pre>
<h2>实战例子</h2>
<h3>例子 A：早上第一件事</h3>
<pre><code>gco main
gl
</code></pre>
<p>这是很典型的日常动作。</p>
<h3>例子 B：pull 出现冲突</h3>
<p>说明远程更新和你本地提交/改动发生了冲突，需要你手动解决。</p>
<h2>提醒</h2>
<h3>1. <code>gl</code> 不是“只下载”</h3>
<p>它是下载 + 整合。</p>
<p>如果你只想下载不合并，用的是：</p>
<pre><code>gf
</code></pre>
<h3>2. 工作区不干净时直接 <code>gl</code>，更容易冲突</h3>
<p>先 <code>gst</code>。</p>
<h3>3. 团队如果偏好线性历史，可能更推荐你用 <code>gpr</code></h3>
<p>也就是 pull 后 rebase。</p>
<hr />
<h1>8. <code>gp</code> —— 把本地提交推上远程</h1>
<h2>原命令</h2>
<pre><code>git push
</code></pre>
<h2>作用</h2>
<p>把你本地分支的提交上传到远程仓库。</p>
<p>这是“让别人看见你改动”的关键动作之一。</p>
<h2>常用指数</h2>
<p><strong>★★★★★</strong></p>
<h2>危险等级</h2>
<p><strong>中</strong></p>
<h2>最常见用法</h2>
<h3>用法 1：把当前分支推上去</h3>
<pre><code>gp
</code></pre>
<p>前提是当前分支已经有 upstream（上游分支）配置。</p>
<h3>用法 2：新分支第一次推送</h3>
<p>通常你会用：</p>
<pre><code>gpsup
</code></pre>
<p>意思是第一次推送并建立跟踪关系。</p>
<h2>常见参数</h2>
<h3><code>gp</code></h3>
<p>普通推送。</p>
<h3><code>gpd</code></h3>
<p>对应：</p>
<pre><code>git push --dry-run
</code></pre>
<p>先演练，不真正推。</p>
<h3><code>gpv</code></h3>
<p>对应：</p>
<pre><code>git push --verbose
</code></pre>
<p>更详细输出。</p>
<h3><code>ggp</code></h3>
<p>对应：</p>
<pre><code>git push origin 当前分支
</code></pre>
<p>显式推当前分支到 <code>origin</code>。</p>
<h2>应用场景</h2>
<h3>场景 1：本地提交后推上去备份</h3>
<pre><code>gp
</code></pre>
<h3>场景 2：推上去让 CI 跑起来</h3>
<p>很多团队 push 后自动触发构建、测试、部署检查。</p>
<h3>场景 3：推上去开 PR</h3>
<p>推送是开 PR 前的前置动作。</p>
<h2>实战例子</h2>
<h3>例子 A：你刚做完一轮提交</h3>
<pre><code>gst
gp
</code></pre>
<h3>例子 B：第一次推分支</h3>
<pre><code>gco -b feat/refactor-header
# 开发、提交以后
gpsup
</code></pre>
<h2>提醒</h2>
<h3>1. <code>gp</code> 只能推“提交”，不能推你还没 commit 的工作区改动</h3>
<p>很多小白以为 push 会把所有改动一起传上去，不会。</p>
<h3>2. push 失败很常见，不一定是你出错</h3>
<p>常见原因：</p>
<ul>
<li>远程有新提交，你本地落后了</li>
<li>没有权限</li>
<li>当前分支没配置 upstream</li>
<li>钩子检查没过</li>
</ul>
<h3>3. 推之前最好看一眼 <code>gst</code></h3>
<p>确认没有一堆你以为已经提交、其实还躺在工作区里的改动。</p>
<hr />
<h1>9. <code>gpf</code> —— 更安全的强制推送</h1>
<h2>原命令</h2>
<p>在较新的 Git 里通常等价于：</p>
<pre><code>git push --force-with-lease --force-if-includes
</code></pre>
<p>在较旧的 Git 里通常等价于：</p>
<pre><code>git push --force-with-lease
</code></pre>
<h2>作用</h2>
<p>当你做了这些操作后，经常需要强推：</p>
<ul>
<li><code>rebase</code></li>
<li><code>commit --amend</code></li>
<li>交互式 rebase 改历史</li>
<li>squash 提交后替换原远程历史</li>
</ul>
<p>因为这些操作改写了本地提交历史，普通 <code>gp</code> 往往会被拒绝。</p>
<p>这时你就需要“强制推送”。</p>
<h2>常用指数</h2>
<p><strong>★★☆☆☆</strong></p>
<h2>危险等级</h2>
<p><strong>高</strong></p>
<h2>为什么 <code>gpf</code> 比 <code>gpf!</code> 好一些</h2>
<h3><code>gpf!</code></h3>
<p>相当于：</p>
<pre><code>git push --force
</code></pre>
<p>它比较蛮横。</p>
<h3><code>gpf</code></h3>
<p>相当于：</p>
<pre><code>git push --force-with-lease
</code></pre>
<p>它会先确认远程分支没有被别人偷偷推进新内容。</p>
<p>所以它比纯 <code>--force</code> 更安全。</p>
<h2>常见参数含义</h2>
<h3><code>--force-with-lease</code></h3>
<p>如果远程分支状态不是你以为的那个状态，就拒绝强推。</p>
<h3><code>--force-if-includes</code></h3>
<p>在较新 Git 中进一步约束强推条件，降低误覆盖风险。</p>
<h2>应用场景</h2>
<h3>场景 1：你 rebase 过自己的功能分支</h3>
<pre><code>grb main
gpf
</code></pre>
<h3>场景 2：你 amend 了最近一次提交</h3>
<pre><code>git commit --amend
gpf
</code></pre>
<h3>场景 3：你用交互式 rebase 把 5 个提交压成 1 个</h3>
<pre><code>grbi HEAD~5
gpf
</code></pre>
<h2>实战例子</h2>
<h3>例子 A：你自己的功能分支，自己在维护</h3>
<pre><code>gco feat/login
grb main
gpf
</code></pre>
<p>这是相对常见且合理的用法。</p>
<h3>例子 B：共享分支乱强推</h3>
<p>如果某个分支是多人一起推的，你 <code>gpf</code> 可能直接把别人的历史覆盖掉。</p>
<h2>提醒</h2>
<h3>1. <code>gpf</code> 不是日常 push 替代品</h3>
<p>只有在“历史被你改写过”的时候才考虑它。</p>
<h3>2. 能不用强推，就别用</h3>
<p>尤其不要把强推当日常习惯。</p>
<h3>3. 最适合 <code>gpf</code> 的地方</h3>
<ul>
<li>你自己的个人功能分支</li>
<li>还没被别人基于它开发</li>
<li>你明确知道自己刚刚 rebase / amend 过</li>
</ul>
<h3>4. 不适合 <code>gpf</code> 的地方</h3>
<ul>
<li>公共主分支</li>
<li>多人共享开发分支</li>
<li>你并不确定远程发生过什么</li>
</ul>
<hr />
<h1>10. <code>grh</code> —— reset，重新摆放 HEAD / 暂存区 / 工作区</h1>
<h2>原命令</h2>
<pre><code>git reset
</code></pre>
<h2>作用</h2>
<p><code>reset</code> 是 Git 里最容易让新手迷糊、也最值得学会的命令之一。</p>
<p>它主要是在做三件事里的某几件：</p>
<ol>
<li>移动当前分支指针（HEAD）</li>
<li>调整暂存区</li>
<li>有时连工作区也一起改</li>
</ol>
<h2>常用指数</h2>
<p><strong>★★★☆☆</strong></p>
<h2>危险等级</h2>
<p><strong>中高</strong></p>
<h2>先记一句最重要的话</h2>
<blockquote>
<p><code>grh</code> 本身不一定危险，危险的是它后面跟的参数。</p>
</blockquote>
<h2>最常见的几种 reset</h2>
<h3>1. <code>grh &lt;commit&gt;</code></h3>
<pre><code>git reset &lt;commit&gt;
</code></pre>
<p>作用：把当前分支指针移到某个提交，通常也会重置暂存区，但保留工作区改动。</p>
<h3>2. <code>grhs &lt;commit&gt;</code></h3>
<pre><code>git reset --soft &lt;commit&gt;
</code></pre>
<p>作用：回退提交，但保留暂存区和工作区。</p>
<p>适合：</p>
<ul>
<li>想撤销提交</li>
<li>但想把改动继续保留下来重新提交</li>
</ul>
<h3>3. <code>grh</code> / <code>gru &lt;file&gt;</code></h3>
<p>常用于取消暂存。</p>
<p>例子：</p>
<pre><code>git add src/login.ts
gru src/login.ts
</code></pre>
<p>意思：把这个文件从暂存区拿下来。</p>
<h3>4. <code>grhh &lt;commit&gt;</code></h3>
<pre><code>git reset --hard &lt;commit&gt;
</code></pre>
<p>作用：回退提交、重置暂存区、连工作区也一起回退。</p>
<p>这个非常危险。</p>
<h2>应用场景</h2>
<h3>场景 1：你 add 多了，想取消暂存</h3>
<pre><code>gru src/login.ts
</code></pre>
<h3>场景 2：你刚提交，想把提交撤回但保留改动</h3>
<pre><code>grhs HEAD~1
</code></pre>
<h3>场景 3：你本地改乱了，想彻底回到某个提交</h3>
<pre><code>grhh HEAD
</code></pre>
<p>或</p>
<pre><code>grhh origin/main
</code></pre>
<h2>实战例子</h2>
<h3>例子 A：撤销最近一次提交，但保留改动</h3>
<pre><code>grhs HEAD~1
</code></pre>
<p>结果：</p>
<ul>
<li>最近一次 commit 没了</li>
<li>改动还在</li>
<li>暂存区状态也还在</li>
</ul>
<h3>例子 B：取消暂存某个文件</h3>
<pre><code>git add src/a.ts src/b.ts
gru src/b.ts
</code></pre>
<p>结果：</p>
<ul>
<li><code>src/a.ts</code> 还在暂存区</li>
<li><code>src/b.ts</code> 被拿回工作区</li>
</ul>
<h3>例子 C：硬回退</h3>
<pre><code>grhh HEAD~1
</code></pre>
<p>结果：</p>
<ul>
<li>最近一次提交没了</li>
<li>相关工作区改动也没了</li>
</ul>
<h2>提醒</h2>
<h3>1. 小白最常用、最该先会的是：取消暂存</h3>
<p>也就是类似：</p>
<pre><code>gru 文件名
</code></pre>
<h3>2. <code>--soft</code> 比 <code>--hard</code> 安全很多</h3>
<ul>
<li><code>--soft</code>：保留改动</li>
<li><code>--hard</code>：可能直接清空改动</li>
</ul>
<h3>3. reset 和 revert 不是一回事</h3>
<ul>
<li><code>reset</code>：改你的本地历史指针</li>
<li><code>revert</code>：新建一个“反向提交”去抵消旧提交</li>
</ul>
<h3>4. 已经推送共享出去的历史，不要随便 reset 后再强推</h3>
<p>因为最后往往会演变成你要 <code>gpf</code>，然后影响别人。</p>
<hr />
<h1>11. 这 8 个命令之间最常见的配合方式</h1>
<h2>流程 A：最日常的开发流程</h2>
<pre><code>gst
# 改代码
gst
# add / commit
gp
</code></pre>
<p>你虽然提交可能用图形化，但 <code>gst + gp</code> 还是很常见。</p>
<h2>流程 B：更新主分支再继续开发</h2>
<pre><code>gco main
gl
gco feat/login
</code></pre>
<h2>流程 C：把主分支更新同步到自己的功能分支（merge 版）</h2>
<pre><code>gco main
gl
gco feat/login
gm main
</code></pre>
<h2>流程 D：把主分支更新同步到自己的功能分支（rebase 版）</h2>
<pre><code>gco main
gl
gco feat/login
grb main
</code></pre>
<h2>流程 E：rebase 之后强推</h2>
<pre><code>gco feat/login
grb main
gpf
</code></pre>
<h2>流程 F：提交乱了，回退重整</h2>
<pre><code>grhs HEAD~1
# 重新整理后再提交
</code></pre>
<hr />
<h1>12. 你这种“提交主要靠可视化”的习惯，实际最该怎么用</h1>
<p>既然你主要是可视化提交，那命令行更多是拿来做这几件事：</p>
<h2>第一类：看</h2>
<ul>
<li><code>gst</code></li>
</ul>
<h2>第二类：切</h2>
<ul>
<li><code>gco</code></li>
</ul>
<h2>第三类：同步</h2>
<ul>
<li><code>gl</code></li>
<li><code>gm</code></li>
<li><code>grb</code></li>
</ul>
<h2>第四类：上传</h2>
<ul>
<li><code>gp</code></li>
<li><code>gpf</code></li>
</ul>
<h2>第五类：撤销 / 纠偏</h2>
<ul>
<li><code>grh</code></li>
</ul>
<p>换句话说，你现在这一套其实已经很像“命令行做仓库控制，可视化做提交细节”的工作方式了，这很正常，而且挺实用。</p>
<hr />
<h1>13. 给你一个最实用的优先级排序</h1>
<p>如果按“你这种使用方式”的实战价值排序：</p>
<h2>S 级：必须熟</h2>
<ol>
<li><code>gst</code></li>
<li><code>gl</code></li>
<li><code>gp</code></li>
<li><code>gco</code></li>
</ol>
<h2>A 级：应该熟</h2>
<ol>
<li><code>gm</code></li>
<li><code>grb</code></li>
<li><code>grh</code></li>
</ol>
<h2>B 级：会用但要克制</h2>
<ol>
<li><code>gpf</code></li>
</ol>
<hr />
<h1>14. 一句话记忆版</h1>
<ul>
<li><code>gst</code>：我现在啥状态？</li>
<li><code>gco</code>：我要切去哪？或者把哪个文件撤回来？</li>
<li><code>gl</code>：把远程最新内容拉下来</li>
<li><code>gm</code>：把另一条分支合进来</li>
<li><code>grb</code>：把我的提交重新排到新基底后面</li>
<li><code>gp</code>：把本地提交推上去</li>
<li><code>gpf</code>：我改历史了，只能更安全地强推</li>
<li><code>grh</code>：我要重新摆放提交、暂存区或工作区状态</li>
</ul>
<hr />
<h1>15. 最后的安全建议</h1>
<h2>看到这些动作先停一下</h2>
<ul>
<li><code>gpf</code></li>
<li><code>grhh</code></li>
<li><code>gco -- 文件</code></li>
<li><code>grb</code> 后准备推送时</li>
</ul>
<h2>先做这三步</h2>
<ol>
<li><code>gst</code></li>
<li>看自己当前在哪个分支</li>
<li>想清楚这个操作会影响：工作区、暂存区、本地历史，还是远程历史</li>
</ol>
<p>你只要把这三件事养成习惯，Git 翻车概率会明显下降。</p>
]]></content>
    <author><name>我叫石头鱼</name></author>
    <category term="Git"/>
  </entry>
  <entry>
    <title>前端项目架构说明书</title>
    <link href="https://stonefish.space/posts/frontend-architecture/" rel="alternate" type="text/html"/>
    <id>https://stonefish.space/posts/frontend-architecture/</id>
    <published>2026-03-19T00:00:00.000Z</published>
    <updated>2026-03-19T00:00:00.000Z</updated>
    <summary>前端项目架构说明书</summary>
    <content type="html"><![CDATA[<blockquote>
<p>本文档用于约束项目的前端目录结构、分层边界、依赖方向、模块职责与实现方式。
本规范的目标不是描述"可以怎么写"，而是明确：</p>
<ul>
<li><strong>代码必须放在哪里</strong></li>
<li><strong>各层可以依赖什么</strong></li>
<li><strong>哪些实现方式被视为推荐或强制</strong></li>
<li><strong>哪些写法属于越界或反模式</strong></li>
</ul>
<p>本规范适用于基于 Vue 的前端项目，默认技术前提包括：</p>
<ul>
<li>Vue</li>
<li>Vue Router</li>
<li>Pinia</li>
<li>请求缓存层（推荐 Query 系方案；必要时可引入 Colada）</li>
</ul>
<p>本规范为项目的长期维护基线。新功能、新页面、迁移重构与目录调整，均应遵循本文档。</p>
</blockquote>
<hr />
<h2>1. 设计目标</h2>
<p>本规范基于以下目标制定：</p>
<h3>1.1 路由入口清晰</h3>
<p>所有路由页面统一收敛在 <code>src/pages/*Page.vue</code>，保持目录扁平、入口清晰、可快速定位。</p>
<h3>1.2 业务逻辑收敛</h3>
<p>与业务域强相关的模型、查询、写入、UI、编排逻辑统一收敛在 <code>src/features/&lt;domain&gt;</code> 内，避免业务逻辑散落在页面、全局 store 或共享目录中。</p>
<h3>1.3 应用装配与业务解耦</h3>
<p><code>src/main.ts</code> 与 <code>src/App.vue</code> 仅承担应用启动与根壳装配职责，不承载业务实现细节。</p>
<h3>1.4 页面与布局解耦</h3>
<p>Layout 作为页面外壳独立存在，通过路由元信息选择，不反向依赖具体业务域。</p>
<h3>1.5 组件粒度明确</h3>
<p>Feature 内部 UI 按页面级、区块级、组件级进行区分，减少目录语义混乱。</p>
<h3>1.6 数据一致性可控</h3>
<p>读取链路、写入链路、缓存键与刷新机制统一定义，避免刷新策略分散和 UI 数据不一致。</p>
<hr />
<h2>2. 总体分层</h2>
<p>项目采用以下分层模型：</p>
<h3>2.1 App 装配层</h3>
<p>目录：</p>
<ul>
<li><code>src/main.ts</code></li>
<li><code>src/App.vue</code></li>
<li><code>src/app/**</code></li>
</ul>
<p>职责：</p>
<ul>
<li>应用启动</li>
<li>插件安装</li>
<li>Provider 装配</li>
<li>根壳装配</li>
</ul>
<h3>2.2 Router / Layout 层</h3>
<p>目录：</p>
<ul>
<li><code>src/router/**</code></li>
<li><code>src/layouts/**</code></li>
</ul>
<p>职责：</p>
<ul>
<li>路由表定义</li>
<li>守卫与跳转</li>
<li>Layout 选择与页面壳装配</li>
</ul>
<h3>2.3 Pages 层</h3>
<p>目录：</p>
<ul>
<li><code>src/pages/*Page.vue</code></li>
</ul>
<p>职责：</p>
<ul>
<li>路由页入口</li>
<li>参数适配</li>
<li>组合 feature 暴露出的页面视图</li>
</ul>
<h3>2.4 Features 层</h3>
<p>目录：</p>
<ul>
<li><code>src/features/&lt;domain&gt;/**</code></li>
</ul>
<p>职责：</p>
<ul>
<li>业务模型</li>
<li>数据读取与写入</li>
<li>页面级编排</li>
<li>Feature 内部 UI</li>
</ul>
<h3>2.5 Services 层</h3>
<p>目录：</p>
<ul>
<li><code>src/services/api/**</code></li>
</ul>
<p>职责：</p>
<ul>
<li>HTTP / SDK / DTO / API 方法</li>
</ul>
<h3>2.6 Shared 层</h3>
<p>目录：</p>
<ul>
<li><code>src/components/base/**</code></li>
<li><code>src/components/shared/**</code></li>
<li><code>src/composables/base/**</code></li>
<li><code>src/utils/**</code></li>
<li><code>src/config/**</code></li>
<li><code>src/directives/**</code></li>
<li><code>src/types/**</code></li>
<li><code>src/styles/**</code></li>
</ul>
<p>职责：</p>
<ul>
<li>通用组件</li>
<li>通用 hooks</li>
<li>工具函数</li>
<li>配置</li>
<li>样式与类型补充</li>
</ul>
<hr />
<h2>3. 依赖方向与边界约束</h2>
<h3>3.1 允许的依赖方向</h3>
<pre><code>App / Router / Layout
          ↓
        Pages
          ↓
       Features
          ↓
       Services

Shared 可被上层复用，但 Shared 不得反向依赖业务域
</code></pre>
<p>允许的导入关系：</p>
<ul>
<li><code>pages -&gt; features/&lt;domain&gt;</code></li>
<li><code>pages -&gt; features/&lt;domain&gt;/model</code></li>
<li><code>features -&gt; services/api</code></li>
<li><code>所有层 -&gt; shared</code></li>
<li><code>App -&gt; router / app / layouts</code></li>
</ul>
<h3>3.2 禁止的依赖方向</h3>
<p>禁止以下依赖：</p>
<ul>
<li><code>pages -&gt; services/api</code></li>
<li><code>pages -&gt; features/&lt;domain&gt;/内部实现路径</code></li>
<li><code>features/A -&gt; features/B/内部实现路径</code></li>
<li><code>shared -&gt; features</code></li>
<li><code>layouts -&gt; features</code></li>
<li><code>services -&gt; features</code></li>
</ul>
<h3>3.3 公开接口规则</h3>
<p>Feature 的公开接口统一通过以下入口导出：</p>
<ul>
<li><code>@/features/&lt;domain&gt;</code></li>
<li><code>@/features/&lt;domain&gt;/model</code></li>
</ul>
<p>外部模块不得绕过公开入口访问以下内部目录：</p>
<ul>
<li><code>queries/*</code></li>
<li><code>mutations/*</code></li>
<li><code>composables/*</code></li>
<li><code>ui/pages/*</code></li>
<li><code>ui/partials/*</code></li>
<li><code>ui/components/*</code></li>
</ul>
<p>该规则为本项目的基础边界规则。</p>
<h3>3.4 规则等级说明</h3>
<p>为保证本文档在评审、重构与协作中的可执行性，本文档中的规则分为以下三个等级：</p>
<h4>MUST</h4>
<p>必须遵守。</p>
<p>含义：</p>
<ul>
<li>属于架构边界规则</li>
<li>违反后会直接导致依赖方向失控、职责混乱或维护成本上升</li>
<li>Code Review 与重构评审中应视为硬性约束</li>
</ul>
<p>典型场景：</p>
<ul>
<li><code>pages</code> 不得直接依赖 <code>services/api</code></li>
<li>外部不得绕过 Feature 公开入口访问内部实现</li>
<li>Layout 不得依赖具体 Feature</li>
</ul>
<h4>SHOULD</h4>
<p>强烈建议遵守。</p>
<p>含义：</p>
<ul>
<li>属于高价值实践</li>
<li>一般情况下应执行</li>
<li>若因历史包袱、阶段性限制或技术约束无法完全满足，应在变更说明中明确原因</li>
</ul>
<p>典型场景：</p>
<ul>
<li>页面级复杂状态优先下沉到 Feature composables</li>
<li>页面主体优先下沉到 <code>features/*/ui/pages/*</code></li>
<li>共享组件命名与分层语义保持一致</li>
</ul>
<h4>MAY</h4>
<p>可选实践。</p>
<p>含义：</p>
<ul>
<li>在不破坏分层边界的前提下可按需要采用</li>
<li>不作为强制审查项</li>
</ul>
<p>典型场景：</p>
<ul>
<li>某些中性组件是否进入 <code>components/shared</code></li>
<li>是否为 Feature 局部区块继续细拆更小粒度组件</li>
</ul>
<h4>使用原则</h4>
<ul>
<li>涉及分层、依赖方向、公开接口、数据访问边界的规则，原则上应定义为 <strong>MUST</strong>。</li>
<li>涉及组织方式优化、工程一致性提升的规则，原则上定义为 <strong>SHOULD</strong>。</li>
<li>涉及实现风格偏好但不破坏架构边界的内容，可定义为 <strong>MAY</strong>。</li>
</ul>
<hr />
<h2>4. 项目目录结构</h2>
<p>推荐项目目录结构如下：</p>
<pre><code>src
├─ main.ts
├─ App.vue
│
├─ app
│  ├─ bootstrap
│  │  ├─ installApp.ts
│  │  ├─ installPlugins.ts
│  │  ├─ installDirectives.ts
│  │  └─ installErrorHandling.ts
│  ├─ providers
│  │  ├─ AppProviders.vue
│  │  ├─ ToastProvider.vue
│  │  └─ DialogProvider.vue
│  └─ shell
│     ├─ AppShell.vue
│     └─ AppRouterView.vue
│
├─ router
│  ├─ index.ts
│  ├─ routes.ts
│  └─ guards
│     ├─ auth.guard.ts
│     ├─ permission.guard.ts
│     └─ analytics.guard.ts
│
├─ layouts
│  ├─ index.ts
│  ├─ LayoutHost.vue
│  ├─ DefaultLayout.vue
│  ├─ EmptyLayout.vue
│  ├─ DashboardLayout.vue
│  └─ parts
│     ├─ AppHeader.vue
│     ├─ AppSidebar.vue
│     └─ AppBreadcrumb.vue
│
├─ pages
│  ├─ WorkspaceTasksPage.vue
│  ├─ WorkspaceOverviewPage.vue
│  ├─ ReviewListPage.vue
│  ├─ ReviewDetailPage.vue
│  ├─ InspectorProfilePage.vue
│  └─ LoginPage.vue
│
├─ features
│  ├─ workspace
│  │  ├─ index.ts
│  │  ├─ model
│  │  │  ├─ index.ts
│  │  │  ├─ types.ts
│  │  │  ├─ keys.ts
│  │  │  └─ constants.ts
│  │  ├─ queries
│  │  │  ├─ mappers.ts
│  │  │  ├─ useWorkspaceTasksQuery.ts
│  │  │  └─ useWorkspaceSummaryQuery.ts
│  │  ├─ mutations
│  │  │  ├─ useCreateTaskMutation.ts
│  │  │  └─ useUpdateTaskMutation.ts
│  │  ├─ composables
│  │  │  ├─ useWorkspaceTasksPage.ts
│  │  │  └─ useWorkspaceFilters.ts
│  │  └─ ui
│  │     ├─ pages
│  │     │  ├─ WorkspaceTasksPageView.vue
│  │     │  └─ WorkspaceOverviewPageView.vue
│  │     ├─ partials
│  │     │  ├─ WorkspaceTaskToolbar.vue
│  │     │  ├─ WorkspaceTaskFilters.vue
│  │     │  └─ WorkspaceSummaryPanel.vue
│  │     └─ components
│  │        ├─ WorkspaceTaskTable.vue
│  │        ├─ WorkspaceTaskCard.vue
│  │        └─ WorkspaceStatusTag.vue
│  ├─ review
│  │  └─ ...
│  └─ inspector
│     └─ ...
│
├─ services
│  └─ api
│     ├─ http.ts
│     ├─ auth.ts
│     ├─ workspace.tasks.ts
│     ├─ workspace.summary.ts
│     ├─ review.items.ts
│     └─ ...
│
├─ components
│  ├─ base
│  │  ├─ BaseButton.vue
│  │  ├─ BaseInput.vue
│  │  ├─ BaseModal.vue
│  │  └─ BaseTable.vue
│  └─ shared
│     ├─ ConfirmDialog.vue
│     ├─ EmptyState.vue
│     └─ LoadingBlock.vue
│
├─ composables
│  └─ base
│     ├─ useBoolean.ts
│     ├─ useDebounce.ts
│     ├─ useEventListener.ts
│     └─ usePagination.ts
│
├─ store
│  ├─ index.ts
│  ├─ app.store.ts
│  └─ auth.store.ts
│
├─ plugins
│  ├─ query.ts
│  ├─ i18n.ts
│  ├─ analytics.ts
│  └─ sentry.ts
│
├─ directives
│  ├─ v-focus.ts
│  └─ v-click-outside.ts
│
├─ utils
│  ├─ date.ts
│  ├─ format.ts
│  ├─ object.ts
│  └─ route.ts
│
├─ config
│  ├─ env.ts
│  ├─ constants.ts
│  └─ featureFlags.ts
│
├─ styles
│  ├─ index.css
│  ├─ reset.css
│  └─ tokens.css
│
├─ assets
│  ├─ icons
│  └─ images
│
└─ types
   ├─ env.d.ts
   └─ shims-vue.d.ts
</code></pre>
<hr />
<h2>5. App 装配层规范</h2>
<h3>5.1 <code>src/main.ts</code></h3>
<h4>职责</h4>
<ul>
<li>创建应用实例</li>
<li>调用统一安装方法</li>
<li>执行挂载</li>
</ul>
<h4>约束</h4>
<ul>
<li>不直接编写长串插件安装逻辑</li>
<li>不编写业务初始化逻辑</li>
<li>不编写权限逻辑</li>
<li>不编写布局逻辑</li>
</ul>
<h4>推荐写法</h4>
<pre><code>import { createApp } from 'vue'
import App from './App.vue'
import { installApp } from './app/bootstrap/installApp'

const app = createApp(App)
installApp(app)
app.mount('#app')
</code></pre>
<h3>5.2 <code>src/App.vue</code></h3>
<h4>职责</h4>
<ul>
<li>装配根级 Providers</li>
<li>装配根级 Shell</li>
</ul>
<h4>约束</h4>
<ul>
<li>不直接引入具体业务 Feature</li>
<li>不直接编写页面逻辑</li>
<li>不直接请求业务数据</li>
</ul>
<h4>推荐写法</h4>
<pre><code>&lt;template&gt;
  &lt;AppProviders&gt;
    &lt;AppShell /&gt;
  &lt;/AppProviders&gt;
&lt;/template&gt;

&lt;script setup lang="ts"&gt;
import AppProviders from '@/app/providers/AppProviders.vue'
import AppShell from '@/app/shell/AppShell.vue'
&lt;/script&gt;
</code></pre>
<h3>5.3 <code>src/app/bootstrap/**</code></h3>
<h4>职责</h4>
<ul>
<li>集中安装插件、指令、错误处理</li>
<li>作为 <code>main.ts</code> 的负载转移层</li>
</ul>
<h4>示例</h4>
<pre><code>import type { App } from 'vue'
import { router } from '@/router'
import { installPlugins } from './installPlugins'
import { installDirectives } from './installDirectives'

export function installApp(app: App) {
  app.use(router)
  installPlugins(app)
  installDirectives(app)
}
</code></pre>
<h3>5.4 <code>src/app/providers/**</code></h3>
<h4>职责</h4>
<ul>
<li>组织全局 Provider 容器</li>
<li>收纳 Toast、Dialog、主题、Query 等根级容器</li>
</ul>
<h3>5.5 <code>src/app/shell/**</code></h3>
<h4>职责</h4>
<ul>
<li>组织应用根壳</li>
<li>承接根级 RouterView、LayoutHost、全局边界容器</li>
</ul>
<hr />
<h2>6. Router 与 Layout 规范</h2>
<h3>6.1 Router 规范</h3>
<h4>Router 的职责</h4>
<ul>
<li>定义路由表</li>
<li>定义守卫</li>
<li>通过 <code>meta</code> 提供页面装配信息</li>
</ul>
<h4>Router 的强约束</h4>
<ul>
<li>路由组件必须指向 <code>src/pages/*Page.vue</code></li>
<li>路由不得直接指向 <code>features/*/ui/pages/*</code></li>
</ul>
<h4>推荐示例</h4>
<pre><code>{
  path: '/workspace/tasks',
  component: () =&gt; import('@/pages/WorkspaceTasksPage.vue'),
  meta: { layout: 'dashboard', requiresAuth: true },
}
</code></pre>
<h3>6.2 权限控制归属</h3>
<p>权限控制统一放在 Router Guard 层处理。</p>
<h4>原则</h4>
<ul>
<li>未登录跳转</li>
<li>无权限跳转</li>
<li>路由级权限判断</li>
</ul>
<p>应在 <code>router/guards/*</code> 中处理，而不应下沉到 Layout 或具体页面中。</p>
<h4>理由</h4>
<ul>
<li>Router Guard 更接近路由访问入口</li>
<li>Layout 的职责应保持为页面壳，而非访问控制</li>
<li>页面层不应承担系统级访问决策</li>
</ul>
<h3>6.3 Layout 规范</h3>
<h4>Layout 的定位</h4>
<p>Layout 是页面外壳，用于控制：</p>
<ul>
<li>Header</li>
<li>Sidebar</li>
<li>Breadcrumb</li>
<li>内容区容器</li>
<li>页面整体壳结构</li>
</ul>
<h4>Layout 的禁止事项</h4>
<ul>
<li>不得直接导入 Feature</li>
<li>不得请求 Feature 数据</li>
<li>不得承载业务编排逻辑</li>
</ul>
<h3>6.4 Layout 选择方式</h3>
<p>统一通过 <code>route.meta.layout</code> 选择布局。</p>
<h4>推荐结构</h4>
<ul>
<li><code>LayoutHost.vue</code>：读取 meta 并选择具体 Layout</li>
<li><code>DefaultLayout.vue</code>：通用布局</li>
<li><code>EmptyLayout.vue</code>：登录页、空白页</li>
<li><code>DashboardLayout.vue</code>：带导航和侧栏的后台布局</li>
<li><code>layouts/parts/*</code>：布局内部组成块</li>
</ul>
<h4>示例</h4>
<pre><code>&lt;template&gt;
  &lt;component :is="layoutComponent"&gt;
    &lt;router-view /&gt;
  &lt;/component&gt;
&lt;/template&gt;

&lt;script setup lang="ts"&gt;
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import DefaultLayout from './DefaultLayout.vue'
import EmptyLayout from './EmptyLayout.vue'
import DashboardLayout from './DashboardLayout.vue'

const route = useRoute()

const layoutMap = {
  default: DefaultLayout,
  empty: EmptyLayout,
  dashboard: DashboardLayout,
}

const layoutComponent = computed(() =&gt; {
  const key = (route.meta.layout as keyof typeof layoutMap) || 'default'
  return layoutMap[key] || DefaultLayout
})
&lt;/script&gt;
</code></pre>
<hr />
<h2>7. Pages 层规范（扁平化路由页）</h2>
<h3>7.1 目录结构要求</h3>
<p><code>src/pages</code> 必须保持扁平化。</p>
<p>允许：</p>
<pre><code>pages/
  WorkspaceTasksPage.vue
  ReviewDetailPage.vue
  LoginPage.vue
</code></pre>
<p>不允许：</p>
<pre><code>pages/
  workspace/
    tasks/
      index.vue
</code></pre>
<h3>7.2 Pages 的职责</h3>
<p>每个 <code>*Page.vue</code> 仅承担以下职责：</p>
<ul>
<li>作为路由页面入口</li>
<li>读取并适配 route params / query</li>
<li>组合一个或多个 Feature 暴露出的 page view</li>
<li>必要时透传参数给 Feature page view</li>
</ul>
<h3>7.3 Pages 的禁止事项</h3>
<ul>
<li>不直接导入 <code>services/api</code></li>
<li>不直接编写 query / mutation</li>
<li>不承载复杂业务状态编排</li>
<li>不进行 DTO 到领域模型的转换</li>
<li>不直接导入 Feature 内部目录</li>
</ul>
<h3>7.4 推荐写法</h3>
<h4>单 Feature 页面</h4>
<pre><code>&lt;template&gt;
  &lt;WorkspaceTasksPageView /&gt;
&lt;/template&gt;

&lt;script setup lang="ts"&gt;
import { WorkspaceTasksPageView } from '@/features/workspace'
&lt;/script&gt;
</code></pre>
<h4>带路由适配的页面</h4>
<pre><code>&lt;template&gt;
  &lt;ReviewDetailPageView :review-id="reviewId" /&gt;
&lt;/template&gt;

&lt;script setup lang="ts"&gt;
import { useRoute } from 'vue-router'
import { ReviewDetailPageView } from '@/features/review'

const route = useRoute()
const reviewId = String(route.params.reviewId)
&lt;/script&gt;
</code></pre>
<h3>7.5 路由页契约（Page Contract）</h3>
<p>为保证 <code>src/pages/*Page.vue</code> 长期保持轻量化，Pages 与 Feature Page View 的交接边界必须明确。</p>
<h4>Pages 允许承担的职责（MUST）</h4>
<ul>
<li>作为 Router 的直接页面入口</li>
<li>读取 <code>route.params</code> 与 <code>route.query</code></li>
<li>进行最小限度的参数适配与类型归一</li>
<li>选择并组合一个或多个 Feature 暴露出的 page view</li>
<li>向 Feature page view 透传路由级参数</li>
</ul>
<h4>Pages 不得承担的职责（MUST）</h4>
<ul>
<li>不直接调用 <code>services/api</code></li>
<li>不直接编写查询、写入逻辑</li>
<li>不承担复杂状态编排</li>
<li>不进行 DTO 到领域模型的转换</li>
<li>不处理页面核心业务规则</li>
</ul>
<h4>Feature Page View 的职责（MUST）</h4>
<p><code>features/*/ui/pages/*</code> 作为页面主体视图，负责：</p>
<ul>
<li>承载页面主要内容结构</li>
<li>组织本 Feature 的 partials 与 components</li>
<li>调用本 Feature 的页面级 composable</li>
<li>处理页面级业务展示逻辑</li>
</ul>
<h4>推荐边界理解（SHOULD）</h4>
<p>可将两者关系理解为：</p>
<ul>
<li><code>pages/*Page.vue</code> = 路由适配层</li>
<li><code>features/*/ui/pages/*</code> = 页面主体层</li>
</ul>
<h4>推荐示例</h4>
<pre><code>&lt;template&gt;
  &lt;ReviewDetailPageView :review-id="reviewId" /&gt;
&lt;/template&gt;

&lt;script setup lang="ts"&gt;
import { useRoute } from 'vue-router'
import { ReviewDetailPageView } from '@/features/review'

const route = useRoute()
const reviewId = String(route.params.reviewId)
&lt;/script&gt;
</code></pre>
<p>在该模式下，路由页仅完成参数适配，具体页面主体由 Feature 负责。</p>
<hr />
<h2>8. Features 层规范（业务域）</h2>
<h3>8.1 Feature 的定义</h3>
<p>Feature 是业务域的完整实现单元。Feature 可以是：</p>
<ul>
<li>一个独立业务域</li>
<li>一个包含多个子域的一级业务域</li>
</ul>
<p>当某个业务域规模较大时，允许在该域下继续按语义拆分为多个子域。此时：</p>
<ul>
<li>一级域负责对外公开该域的能力边界</li>
<li>子域负责各自的具体实现</li>
<li>跨子域复用的内容统一收敛到一级域下的 <code>shared/</code></li>
</ul>
<p>一级域与子域的关系应理解为：</p>
<ul>
<li>一级域 = 边界与聚合层</li>
<li>子域 = 具体能力实现层</li>
</ul>
<h3>8.2 推荐目录结构</h3>
<h4>A. 单一业务域</h4>
<p>适用于规模中等、边界清晰、无需再拆分子域的 Feature。</p>
<pre><code>features/&lt;domain&gt;/
  index.ts
  model/
  queries/
  mutations/
  composables/
  ui/
</code></pre>
<h4>B. 大型业务域（带子域）</h4>
<p>适用于一级域下包含多个稳定语义模块的场景。</p>
<pre><code>features/&lt;domain&gt;/
  &lt;subdomain-a&gt;/
  &lt;subdomain-b&gt;/
  &lt;subdomain-c&gt;/
  shared/
  index.ts
  model/
</code></pre>
<p>其中：</p>
<ul>
<li>子域承载各自的实现</li>
<li><code>shared/</code> 仅承载兄弟子域之间复用的内容</li>
<li><code>index.ts</code> 与 <code>model/</code> 仍作为一级域的对外公开边界</li>
</ul>
<h4>子域结构规则</h4>
<p>子域不强制必须同时具备 <code>model / queries / mutations / composables / ui</code> 五类子目录。</p>
<p>应根据复杂度采用渐进式组织方式。</p>
<h5>简单子域（SHOULD）</h5>
<p>当子域规模较小、实现简单时，优先采用扁平文件结构：</p>
<pre><code>&lt;subdomain&gt;/
  model.ts
  queries.ts
  mutations.ts
</code></pre>
<p>如有少量局部 UI 或编排需求，可按需增加：</p>
<pre><code>&lt;subdomain&gt;/
  model.ts
  queries.ts
  mutations.ts
  composables/
  ui/
</code></pre>
<h5>复杂子域（SHOULD）</h5>
<p>只有当子域明显变复杂时，才将其升级为完整分层目录，例如：</p>
<ul>
<li>查询与写入逻辑已经显著增多</li>
<li>页面级编排明显增多</li>
<li>子域内 UI 已出现多层级结构</li>
<li>子域内部已形成独立维护边界</li>
</ul>
<p>升级后可采用：</p>
<pre><code>&lt;subdomain&gt;/
  model/
  queries/
  mutations/
  composables/
  ui/
</code></pre>
<h4>子域 Shared 规则</h4>
<p>默认不设"子域自己的 <code>shared/</code>"。</p>
<p>原因如下：</p>
<ul>
<li>子域内部共享通常尚不足以形成稳定的模块边界</li>
<li>过早引入多层 shared 容易导致结构膨胀</li>
<li>会削弱一级域 <code>shared/</code> 的收敛作用</li>
</ul>
<p>仅当某个子域本身已经足够复杂，且其内部再次形成明确子模块边界时，才允许在该子域内继续分模块并引入更细粒度共享目录。</p>
<h4>一级域 <code>shared/</code> 的使用边界</h4>
<p>一级域下的 <code>shared/</code> 仅用于承载"跨子域复用"的内容。</p>
<p>允许进入一级域 <code>shared/</code> 的内容包括：</p>
<ul>
<li>跨子域复用的类型</li>
<li>跨子域复用的表单片段</li>
<li>跨子域复用的弹窗</li>
<li>跨子域复用的常量</li>
<li>跨子域复用的映射工具</li>
</ul>
<p>禁止将以下内容放入一级域 <code>shared/</code>：</p>
<ul>
<li>仅被单个子域使用的实现</li>
<li>尚未形成稳定复用关系的临时代码</li>
<li>本应留在子域内部的业务实现细节</li>
</ul>
<h4>一级域与子域的组织原则</h4>
<ul>
<li>一级域负责聚合边界与公开接口。</li>
<li>子域负责自己的实现细节。</li>
<li>只有跨子域复用的内容才进入一级域 <code>shared/</code>。</li>
<li>小子域优先采用扁平文件。</li>
<li>复杂后再升级为完整分层目录。</li>
</ul>
<h3>8.3 <code>index.ts</code> 规范</h3>
<h4>职责</h4>
<ul>
<li>Feature 对外统一公开入口</li>
<li>白名单式导出允许外部使用的能力</li>
<li>当 Feature 为一级域时，负责聚合其子域对外能力</li>
</ul>
<h4>强约束</h4>
<p>外部模块只应从该入口使用 Feature 能力。</p>
<h4>推荐导出内容</h4>
<ul>
<li>页面级 page view</li>
<li>必要的编排 composable</li>
<li>少量需要跨页面复用的 UI 能力</li>
<li>一级域场景下，对外聚合后的子域能力</li>
</ul>
<h3>8.4 <code>model/</code> 规范</h3>
<h4>职责</h4>
<ul>
<li>定义领域模型</li>
<li>定义 query keys</li>
<li>定义领域常量</li>
<li>提供稳定对外类型出口</li>
<li>在一级域场景下，承载该域统一对外的模型契约</li>
</ul>
<h4>说明</h4>
<p>当 Feature 为大型一级域时：</p>
<ul>
<li>一级域下的 <code>model/</code> 负责对外稳定模型边界</li>
<li>子域内部可以保留各自局部模型文件或目录</li>
<li>只有需要对一级域外部稳定暴露的模型契约，才应收敛到一级域 <code>model/</code></li>
</ul>
<h4>示例</h4>
<pre><code>export type Task = {
  id: string
  title: string
  status: 'todo' | 'doing' | 'done'
  createdAt: Date
}
</code></pre>
<h3>8.5 <code>queries/</code> 规范</h3>
<h4>职责</h4>
<ul>
<li>封装读取链路</li>
<li>调用 <code>services/api</code></li>
<li>将 DTO 映射为领域模型</li>
<li>管理缓存键与查询行为</li>
</ul>
<h3>8.6 <code>mutations/</code> 规范</h3>
<h4>职责</h4>
<ul>
<li>封装写入链路</li>
<li>写入成功后执行 invalidation</li>
</ul>
<h3>8.7 <code>composables/</code> 规范</h3>
<h4>职责</h4>
<ul>
<li>进行页面级或用例级状态编排</li>
<li>组合 queries、mutations 与局部 UI 状态</li>
</ul>
<h4>典型用途</h4>
<ul>
<li>页面筛选条件</li>
<li>分页状态</li>
<li>页面行为封装</li>
<li>Feature 页面 ViewModel</li>
</ul>
<h3>8.8 公开接口导出边界</h3>
<p>虽然 Feature 对外统一通过 <code>index.ts</code> 与 <code>model/index.ts</code> 暴露能力，但公开入口的导出范围仍需受控。</p>
<h4><code>features/&lt;domain&gt;/index.ts</code> 推荐导出内容（SHOULD）</h4>
<ul>
<li>页面级 Page View</li>
<li>面向页面层的编排 composable</li>
<li>少量明确需要跨页面复用的 Feature UI</li>
<li>必要时导出少量中性帮助类型（如确有必要）</li>
<li>一级域场景下，聚合后的子域公开能力</li>
</ul>
<h4><code>features/&lt;domain&gt;/index.ts</code> 禁止导出内容（MUST）</h4>
<ul>
<li><code>queries/*</code></li>
<li><code>mutations/*</code></li>
<li><code>mappers/*</code></li>
<li>内部 helpers</li>
<li>仅供 Feature 内部使用的 partials</li>
<li>未明确声明为公开 API 的 components</li>
<li>子域内部未收敛的实现细节</li>
</ul>
<h4><code>features/&lt;domain&gt;/model/index.ts</code> 推荐导出内容（SHOULD）</h4>
<ul>
<li>领域类型</li>
<li>query keys</li>
<li>领域常量</li>
<li>明确需要对外稳定暴露的模型契约</li>
<li>一级域统一对外的模型边界</li>
</ul>
<h4>导出控制原则</h4>
<ul>
<li>Feature 入口应被视为该业务域的"公开 API 面"。</li>
<li>入口导出数量应保持克制，避免将整个内部目录结构等价公开。</li>
<li>内部实现一旦被公开，后续重构成本会显著上升，因此入口导出必须保持审慎。</li>
<li>在大型一级域场景下，应优先由一级域入口聚合子域公开能力，而不是让外部直接面向子域内部文件结构编程。</li>
</ul>
<hr />
<h2>9. Feature UI 分层规范（pages / Partials / components）</h2>
<p>Feature 内部 UI 统一分为三层：</p>
<pre><code>ui/
  pages/
  partials/
  components/
</code></pre>
<h3>9.1 <code>ui/pages/</code></h3>
<h4>定义</h4>
<p>页面级业务视图。</p>
<h4>使用场景</h4>
<ul>
<li>对应某个路由页的主体内容</li>
<li>直接被 <code>src/pages/*Page.vue</code> 组合使用</li>
<li>可调用本 Feature 的页面级 composable</li>
</ul>
<h4>示例</h4>
<ul>
<li><code>WorkspaceTasksPageView.vue</code></li>
<li><code>ReviewDetailPageView.vue</code></li>
</ul>
<h3>9.2 <code>ui/partials/</code></h3>
<h4>定义</h4>
<p>页面中的区块级拼装单元。</p>
<h4>使用场景</h4>
<ul>
<li>工具栏</li>
<li>筛选区</li>
<li>摘要区</li>
<li>侧栏区块</li>
<li>表单区块</li>
<li>页面中的独立大板块</li>
</ul>
<h4>示例</h4>
<ul>
<li><code>WorkspaceTaskToolbar.vue</code></li>
<li><code>WorkspaceSummaryPanel.vue</code></li>
<li><code>ReviewDetailSidebar.vue</code></li>
</ul>
<h3>9.3 <code>ui/components/</code></h3>
<h4>定义</h4>
<p>更小粒度的 Feature 内业务组件。</p>
<h4>使用场景</h4>
<ul>
<li>表格</li>
<li>卡片</li>
<li>标签</li>
<li>列表项</li>
<li>状态显示组件</li>
</ul>
<h4>示例</h4>
<ul>
<li><code>WorkspaceTaskTable.vue</code></li>
<li><code>WorkspaceStatusTag.vue</code></li>
<li><code>ReviewBadge.vue</code></li>
</ul>
<h3>9.4 区分规则</h3>
<h4>放入 <code>ui/pages/</code>，如果该组件：</h4>
<ul>
<li>构成某个路由页的主要内容</li>
<li>直接被 <code>src/pages/*Page.vue</code> 使用</li>
<li>需要组织多个 partials 与 components</li>
</ul>
<h4>放入 <code>ui/partials/</code>，如果该组件：</h4>
<ul>
<li>是页面中的一个大区块</li>
<li>自身已组合多个基础组件或业务组件</li>
<li>更像页面的一段区域，而不是单一控件</li>
</ul>
<h4>放入 <code>ui/components/</code>，如果该组件：</h4>
<ul>
<li>粒度较小</li>
<li>聚焦于一个明确的业务 UI 单元</li>
<li>不承担页面区块组织职责</li>
</ul>
<h4>子域下的 UI 组织原则</h4>
<ul>
<li>小型子域可以仅保留少量 <code>ui/</code> 目录，不强制继续拆分多层结构。</li>
<li>只有当子域 UI 已明显形成页面级、区块级、组件级三种不同粒度时，才建议按 <code>pages / partials / components</code> 继续分层。</li>
<li>Feature UI 分层属于渐进式组织手段，不要求在所有子域中一次到位。</li>
</ul>
<hr />
<h2>10. 数据访问与缓存规范</h2>
<h3>10.1 Services 层职责</h3>
<p><code>src/services/api/**</code> 仅负责：</p>
<ul>
<li>HTTP 客户端</li>
<li>DTO 类型</li>
<li>API 方法</li>
</ul>
<p>不得承担：</p>
<ul>
<li>领域模型定义</li>
<li>页面状态编排</li>
<li>缓存键定义</li>
<li>刷新策略定义</li>
</ul>
<h3>10.2 DTO 与领域模型分离</h3>
<ul>
<li>DTO 只存在于 <code>services/api</code></li>
<li>领域模型定义在 <code>features/&lt;domain&gt;/model</code></li>
<li>DTO 到领域模型的映射应放在 <code>features/&lt;domain&gt;/queries/mappers.ts</code></li>
</ul>
<h3>10.3 Query Key 规范</h3>
<p>统一骨架：</p>
<pre><code>[domain, resource, scope, params]
</code></pre>
<p>示例：</p>
<pre><code>['workspace', 'tasks', 'list', { page: 1, pageSize: 20 }]
['workspace', 'tasks', 'detail', { taskId: '123' }]
</code></pre>
<h3>10.4 Mutation 刷新规则</h3>
<p>写入成功后必须执行 invalidation。</p>
<p>推荐粒度：</p>
<ul>
<li>新增：刷新 list</li>
<li>更新：刷新 detail + 相关 list</li>
<li>删除：刷新 list + 受影响 detail</li>
</ul>
<p>禁止混用以下方式作为主要数据刷新手段：</p>
<ul>
<li>事件广播刷新</li>
<li>页面局部手动 setState 刷新</li>
<li>各页面自行决定刷新逻辑</li>
</ul>
<hr />
<h2>11. Pinia / Colada 使用边界</h2>
<h3>11.1 Pinia 的定位</h3>
<p>Pinia 用于全局状态管理，但应保持边界明确。</p>
<h4>适合放入 Pinia 的状态</h4>
<ul>
<li>用户登录态</li>
<li>当前租户 / 工作区上下文</li>
<li>主题模式</li>
<li>当前语言</li>
<li>全局 feature flags</li>
<li>系统级偏好设置</li>
</ul>
<h4>不建议放入 Pinia 的状态</h4>
<ul>
<li>列表数据</li>
<li>详情数据</li>
<li>页面筛选条件</li>
<li>页面临时交互态</li>
<li>由 query 层可自然管理的数据</li>
</ul>
<p>上述数据优先放入：</p>
<ul>
<li>Feature queries</li>
<li>Feature composables</li>
</ul>
<h3>11.2 Colada 的引入时机</h3>
<p>必要时可引入 Colada，用于增强请求数据管理能力。</p>
<h4>推荐使用场景</h4>
<ul>
<li>需要更清晰的数据查询与缓存模型</li>
<li>需要替代散落的页面级请求状态管理</li>
<li>需要统一处理列表、详情、刷新、失效逻辑</li>
</ul>
<h4>约束</h4>
<p>即使引入 Colada，其使用边界仍应保持不变：</p>
<ul>
<li>查询逻辑仍放在 <code>features/&lt;domain&gt;/queries</code></li>
<li>写入逻辑仍放在 <code>features/&lt;domain&gt;/mutations</code></li>
<li>页面层仍不得直接请求 API</li>
</ul>
<p>换言之，Colada 改变的是实现工具，不改变分层归属。</p>
<h3>11.3 状态归属矩阵</h3>
<p>为避免状态落点混乱，项目中的状态按来源与生命周期分为三类，并分别归属不同层管理。</p>
<h4>A. 全局系统状态 → Pinia（MUST）</h4>
<p>适用于跨页面、跨业务域共享，且具有明显应用级意义的状态。</p>
<p>典型示例：</p>
<ul>
<li>登录态</li>
<li>当前用户信息摘要</li>
<li>当前租户 / 工作区上下文</li>
<li>主题模式</li>
<li>当前语言</li>
<li>系统级偏好设置</li>
<li>全局 feature flags</li>
</ul>
<h4>B. 服务端异步数据 → Query / Colada（MUST）</h4>
<p>适用于来源于服务端、需要缓存、刷新、失效管理的数据。</p>
<p>典型示例：</p>
<ul>
<li>列表数据</li>
<li>详情数据</li>
<li>统计汇总数据</li>
<li>服务端分页数据</li>
<li>服务端筛选结果</li>
<li>与资源状态强绑定的数据视图</li>
</ul>
<p>此类数据必须通过 Feature 内 <code>queries/</code> 管理，而不应直接进入页面或全局 store。</p>
<h4>C. 页面/组件临时交互状态 → Feature Composables 或组件本地 state（MUST）</h4>
<p>适用于生命周期较短、仅影响当前页面或局部交互的状态。</p>
<p>典型示例：</p>
<ul>
<li>当前 tab</li>
<li>弹窗开关</li>
<li>当前展开项</li>
<li>输入框临时值</li>
<li>当前排序方式</li>
<li>页面内筛选面板的本地开关状态</li>
</ul>
<h4>归属原则</h4>
<ul>
<li>只要数据来自服务端并需要与缓存、刷新联动，优先归属 Query / Colada。</li>
<li>只要状态具有全局共享属性，归属 Pinia。</li>
<li>只要状态仅服务当前页面或当前组件交互，归属 composables 或 local state。</li>
</ul>
<h4>禁止事项（MUST）</h4>
<ul>
<li>不得将列表、详情、分页结果等服务端数据滥用 Pinia 承载。</li>
<li>不得将页面临时交互状态上提为全局 store，除非确有跨页面共享需求。</li>
<li>不得在页面层直接持有服务端数据管理职责而绕过 Feature 查询层。</li>
</ul>
<h3>11.4 异步状态规范</h3>
<p>为统一页面与区块的异步交互表现，Feature 的读取与写入逻辑应显式处理以下状态：</p>
<ul>
<li>loading</li>
<li>empty</li>
<li>error</li>
<li>retry</li>
<li>submitting</li>
<li>disabled</li>
</ul>
<h4>首屏加载（SHOULD）</h4>
<p>当页面主体首次加载时，应提供明确的加载态，而不是空白页面或结构闪烁。</p>
<h4>局部加载（SHOULD）</h4>
<p>局部刷新或局部区块数据加载时，应优先使用区块级 loading，而非阻断整个页面。</p>
<h4>空态（SHOULD）</h4>
<p>当请求成功但无数据时，应提供明确 empty state，而非仅显示空白区域。</p>
<h4>错误态（MUST）</h4>
<p>当请求失败时，应至少提供：</p>
<ul>
<li>错误反馈</li>
<li>可识别的失败状态</li>
<li>合理的重试入口（如适用）</li>
</ul>
<h4>提交态（MUST）</h4>
<p>写入过程中应显式暴露 submitting / pending 状态，用于控制：</p>
<ul>
<li>按钮 disabled</li>
<li>防重复提交</li>
<li>提交中反馈</li>
</ul>
<h4>乐观更新（MAY）</h4>
<p>仅当业务一致性与回滚逻辑清晰时，允许采用乐观更新。若采用，必须明确失败回滚策略。</p>
<h4>统一原则</h4>
<ul>
<li>异步状态应优先在 Feature 层统一建模，再向 UI 暴露。</li>
<li>不应由页面层临时拼接异步状态方案。</li>
<li>相同类型的页面和区块应尽量保持一致的异步状态表达方式。</li>
</ul>
<hr />
<h2>12. 共享层规范（base / Shared / Utils / config）</h2>
<h3>12.1 <code>components/base/*</code></h3>
<h4>定义</h4>
<p>纯基础组件，无业务语义。</p>
<h4>示例</h4>
<ul>
<li><code>BaseButton.vue</code></li>
<li><code>BaseInput.vue</code></li>
<li><code>BaseModal.vue</code></li>
<li><code>BaseTable.vue</code></li>
</ul>
<h4>强约束</h4>
<p>基础组件命名不得出现业务词汇，如：</p>
<ul>
<li>task</li>
<li>review</li>
<li>workspace</li>
<li>profile</li>
</ul>
<h3>12.2 <code>components/shared/*</code></h3>
<h4>定义</h4>
<p>跨多个 Feature 复用的轻业务或中性 UI 壳组件。</p>
<h4>示例</h4>
<ul>
<li><code>ConfirmDialog.vue</code></li>
<li><code>EmptyState.vue</code></li>
<li><code>LoadingBlock.vue</code></li>
</ul>
<h4>约束</h4>
<p>若组件已带明显业务语义，应回归对应 Feature，而不是继续放在 shared。</p>
<h3>12.3 <code>composables/base/*</code></h3>
<h4>定义</h4>
<p>通用 hooks，不带业务语义。</p>
<h4>示例</h4>
<ul>
<li><code>useBoolean.ts</code></li>
<li><code>useDebounce.ts</code></li>
<li><code>usePagination.ts</code></li>
</ul>
<h3>12.4 <code>utils/*</code></h3>
<h4>定义</h4>
<p>纯工具函数。</p>
<h4>约束</h4>
<p>若工具函数名称或逻辑已明显依赖业务概念，应放回 Feature。</p>
<h3>12.5 <code>config/*</code></h3>
<h4>定义</h4>
<p>环境配置、系统常量、功能开关。</p>
<h4>约束</h4>
<p>业务规则不应伪装成全局配置。</p>
<p>若某项常量属于领域规则，应放入 <code>features/&lt;domain&gt;/model/constants.ts</code>。</p>
<hr />
<h2>13. ESLint 架构防线建议</h2>
<p>本项目建议通过 ESLint 建立基础防线，不追求过细，但应覆盖关键越界场景。</p>
<h3>13.1 最低建议规则</h3>
<h4>规则一：页面层禁止直接导入 API</h4>
<p>禁止：</p>
<ul>
<li><code>src/pages/** -&gt; @/services/api/*</code></li>
</ul>
<h4>规则二：外部禁止导入 Feature 内部实现</h4>
<p>禁止：</p>
<ul>
<li><code>@/features/*/queries/*</code></li>
<li><code>@/features/*/mutations/*</code></li>
<li><code>@/features/*/composables/*</code></li>
<li><code>@/features/*/ui/*</code></li>
</ul>
<h4>规则三：Shared 禁止依赖 Feature</h4>
<p>禁止：</p>
<ul>
<li><code>components/base -&gt; features</code></li>
<li><code>components/shared -&gt; features</code></li>
<li><code>composables/base -&gt; features</code></li>
<li><code>utils -&gt; features</code></li>
</ul>
<h3>13.2 规则目的</h3>
<p>这些规则用于保障：</p>
<ul>
<li>页面保持轻量</li>
<li>Feature 公开入口不被绕过</li>
<li>Shared 不被业务污染</li>
</ul>
<h3>13.3 示例配置（简化版）</h3>
<pre><code>'no-restricted-imports': [
  'error',
  {
    patterns: [
      {
        group: ['@/services/api/*'],
        message: '页面层禁止直接访问 services/api，请通过 feature 公开能力使用。',
      },
      {
        group: [
          '@/features/*/queries/*',
          '@/features/*/mutations/*',
          '@/features/*/composables/*',
          '@/features/*/ui/*',
        ],
        message: '禁止直接依赖 feature 内部实现，请通过公开入口导入。',
      },
    ],
  },
]
</code></pre>
<hr />
<h2>14. 命名规范</h2>
<h3>14.1 Pages 命名</h3>
<p>所有路由页统一命名为：</p>
<ul>
<li><code>XxxPage.vue</code></li>
</ul>
<p>示例：</p>
<ul>
<li><code>WorkspaceTasksPage.vue</code></li>
<li><code>ReviewDetailPage.vue</code></li>
<li><code>LoginPage.vue</code></li>
</ul>
<h3>14.2 Feature Page View 命名</h3>
<p>页面级业务视图统一命名为：</p>
<ul>
<li><code>XxxPageView.vue</code></li>
</ul>
<p>示例：</p>
<ul>
<li><code>WorkspaceTasksPageView.vue</code></li>
<li><code>ReviewDetailPageView.vue</code></li>
</ul>
<h3>14.3 Partials 命名</h3>
<p>区块级组件建议使用能够体现区块语义的后缀：</p>
<ul>
<li><code>Toolbar</code></li>
<li><code>Panel</code></li>
<li><code>Section</code></li>
<li><code>Sidebar</code></li>
<li><code>Filters</code></li>
<li><code>Summary</code></li>
</ul>
<p>示例：</p>
<ul>
<li><code>WorkspaceTaskToolbar.vue</code></li>
<li><code>ReviewDetailSidebar.vue</code></li>
<li><code>WorkspaceSummaryPanel.vue</code></li>
</ul>
<h3>14.4 Components 命名</h3>
<p>组件级单元建议使用能够体现组件角色的后缀：</p>
<ul>
<li><code>Table</code></li>
<li><code>Card</code></li>
<li><code>Item</code></li>
<li><code>Row</code></li>
<li><code>Tag</code></li>
<li><code>Badge</code></li>
<li><code>Form</code></li>
</ul>
<p>示例：</p>
<ul>
<li><code>WorkspaceTaskTable.vue</code></li>
<li><code>WorkspaceTaskCard.vue</code></li>
<li><code>ReviewStatusBadge.vue</code></li>
</ul>
<h3>14.5 Query / Mutation / Composable 命名</h3>
<ul>
<li>Query：<code>useXxxQuery.ts</code></li>
<li>Mutation：<code>useXxxMutation.ts</code></li>
<li>页面级编排：<code>use&lt;Domain&gt;&lt;UseCase&gt;Page.ts</code></li>
<li>局部业务编排：<code>use&lt;Domain&gt;&lt;UseCase&gt;.ts</code></li>
</ul>
<p>示例：</p>
<ul>
<li><code>useWorkspaceTasksQuery.ts</code></li>
<li><code>useCreateTaskMutation.ts</code></li>
<li><code>useWorkspaceTasksPage.ts</code></li>
</ul>
<hr />
<h2>15. 标准落地流程</h2>
<h3>15.1 新建一个路由页</h3>
<p>以 <code>WorkspaceTasksPage</code> 为例：</p>
<ol>
<li>在 <code>src/pages/</code> 创建 <code>WorkspaceTasksPage.vue</code></li>
<li>在 <code>features/workspace/ui/pages/</code> 创建 <code>WorkspaceTasksPageView.vue</code></li>
<li>在 <code>features/workspace/composables/</code> 创建 <code>useWorkspaceTasksPage.ts</code></li>
<li>在 <code>ui/partials/</code> 拆分工具栏、筛选区、摘要区等页面区块</li>
<li>在 <code>ui/components/</code> 放表格、卡片、标签等组件级单元</li>
<li>在 <code>features/workspace/index.ts</code> 统一导出 <code>WorkspaceTasksPageView</code></li>
<li>在 <code>router/routes.ts</code> 注册该 <code>pages/*Page.vue</code></li>
</ol>
<h3>15.2 新建一个 Feature 区块</h3>
<ol>
<li>判断其是否为完整页面主体
<ul>
<li>是：放 <code>ui/pages/</code></li>
</ul>
</li>
<li>否则判断其是否为页面中的区块
<ul>
<li>是：放 <code>ui/partials/</code></li>
</ul>
</li>
<li>否则放入 <code>ui/components/</code></li>
</ol>
<h3>15.3 从旧页面迁移到 Feature</h3>
<ol>
<li>在目标 Feature 中建立 <code>model / queries / mutations / composables / ui</code></li>
<li>将 API 直连逻辑下沉到 <code>queries / mutations</code></li>
<li>将页面状态编排下沉到 <code>composables</code></li>
<li>将页面主体下沉到 <code>ui/pages</code></li>
<li>将路由页改造为扁平 <code>pages/*Page.vue</code> 壳</li>
<li>统一从 Feature 入口导出使用能力</li>
</ol>
<hr />
<h2>16. 反模式清单</h2>
<p>以下情况视为明显越界或反模式：</p>
<h3>16.1 Pages 承载重业务</h3>
<p>表现：</p>
<ul>
<li>页面直接请求 API</li>
<li>页面管理复杂筛选/分页/写入逻辑</li>
<li>页面中充斥 DTO 处理</li>
</ul>
<h3>16.2 绕过 Feature 公开入口</h3>
<p>表现：</p>
<ul>
<li>直接导入 <code>queries/*</code></li>
<li>直接导入 <code>ui/pages/*</code></li>
<li>直接导入 <code>composables/*</code></li>
</ul>
<h3>16.3 Layout 依赖 Feature</h3>
<p>表现：</p>
<ul>
<li>Layout 中引入 Feature 页面组件</li>
<li>Layout 中请求业务数据</li>
<li>Layout 中写业务编排逻辑</li>
</ul>
<h3>16.4 Shared 被业务污染</h3>
<p>表现：</p>
<ul>
<li><code>components/shared</code> 下出现大量业务词组件</li>
<li><code>utils</code> 中出现明显业务规则工具</li>
<li><code>base</code> 目录依赖 Feature</li>
</ul>
<h3>16.5 Pinia 过度承载页面状态</h3>
<p>表现：</p>
<ul>
<li>列表数据进 Pinia</li>
<li>页面筛选条件进 Pinia</li>
<li>详情数据进 Pinia</li>
</ul>
<h3>16.6 App 装配层失控</h3>
<p>表现：</p>
<ul>
<li><code>main.ts</code> 中堆积大量安装逻辑</li>
<li><code>App.vue</code> 中开始出现业务页面逻辑</li>
</ul>
<hr />
<h2>17. 自检清单</h2>
<h3>17.1 路由与页面</h3>
<ul>
<li><code>pages/</code> 是否保持扁平，仅保留 <code>*Page.vue</code></li>
<li>路由是否统一指向 <code>pages/*Page.vue</code></li>
<li>页面是否仅负责路由适配与组合</li>
</ul>
<h3>17.2 Feature</h3>
<ul>
<li>是否统一通过 <code>features/&lt;domain&gt;</code> 或 <code>features/&lt;domain&gt;/model</code> 暴露公开接口</li>
<li>页面主体是否放在 <code>ui/pages/</code></li>
<li>页面区块是否放在 <code>ui/partials/</code></li>
<li>组件级单元是否放在 <code>ui/components/</code></li>
<li>查询、写入、编排是否均在 Feature 内部完成</li>
</ul>
<h3>17.3 App / Layout</h3>
<ul>
<li><code>main.ts</code> 是否仅负责启动与安装</li>
<li><code>App.vue</code> 是否仅负责根壳装配</li>
<li>Layout 是否仅负责页面外壳</li>
<li>权限控制是否放在 Router Guard 层</li>
</ul>
<h3>17.4 数据与状态</h3>
<ul>
<li>DTO 是否未泄漏到页面层</li>
<li>mutation 成功后是否执行 invalidation</li>
<li>页面数据是否未被滥用 Pinia 承载</li>
<li>Query / Colada 是否仍然受 Feature 边界约束</li>
</ul>
<h3>17.5 共享层</h3>
<ul>
<li><code>base</code> 是否无业务语义</li>
<li><code>shared</code> 是否未演变为业务组件仓库</li>
<li><code>utils</code> 与 <code>config</code> 是否未承载领域规则</li>
</ul>
<hr />
<h2>18. 测试规范</h2>
<p>测试规范单独列出，用于明确不同层级的测试落点与职责范围。</p>
<h3>18.1 测试目标</h3>
<p>测试的目标不是覆盖所有文件，而是覆盖最有价值的行为边界：</p>
<ul>
<li>纯逻辑是否正确</li>
<li>数据映射是否正确</li>
<li>页面级编排是否正确</li>
<li>关键交互是否正确</li>
<li>路由与布局关键路径是否正确</li>
</ul>
<h3>18.2 分层测试建议</h3>
<h4><code>utils/*</code>（SHOULD）</h4>
<p>优先编写单元测试。</p>
<p>适合验证：</p>
<ul>
<li>日期处理</li>
<li>字符串处理</li>
<li>对象转换</li>
<li>纯函数逻辑</li>
</ul>
<h4><code>composables/base/*</code>（SHOULD）</h4>
<p>优先编写单元测试。</p>
<p>适合验证：</p>
<ul>
<li>状态切换</li>
<li>debounce / pagination 等通用行为</li>
<li>副作用边界是否正确</li>
</ul>
<h4><code>features/*/queries/*</code>（SHOULD）</h4>
<p>建议编写逻辑测试或集成测试。</p>
<p>适合验证：</p>
<ul>
<li>API 返回与领域模型映射是否正确</li>
<li>query key 是否符合预期</li>
<li>查询条件变化后的结果行为是否正确</li>
</ul>
<h4><code>features/*/mutations/*</code>（SHOULD）</h4>
<p>建议编写逻辑测试或集成测试。</p>
<p>适合验证：</p>
<ul>
<li>写入参数是否正确</li>
<li>成功后 invalidation 是否被触发</li>
<li>失败路径反馈是否正确</li>
</ul>
<h4><code>features/*/composables/*</code>（MUST，关键页面场景）</h4>
<p>页面级编排逻辑应优先测试。</p>
<p>适合验证：</p>
<ul>
<li>页面筛选、分页、排序状态流转</li>
<li>页面行为封装</li>
<li>query / mutation 与页面状态组合是否正确</li>
</ul>
<h4><code>features/*/ui/components/*</code> 与 <code>ui/partials/*</code>（SHOULD）</h4>
<p>建议进行组件交互测试。</p>
<p>适合验证：</p>
<ul>
<li>props / emit 是否符合预期</li>
<li>关键交互路径是否可用</li>
<li>loading / empty / error / disabled 状态是否正确展示</li>
</ul>
<h4><code>features/*/ui/pages/*</code>（SHOULD）</h4>
<p>建议进行页面主体视图测试。</p>
<p>适合验证：</p>
<ul>
<li>页面主体是否正确组织 partials 与 components</li>
<li>页面级 ViewModel 是否正确接入</li>
<li>页面主要交互是否可达</li>
</ul>
<h4><code>pages/*Page.vue</code>（MAY）</h4>
<p>页面层通常较薄，测试优先级低于 Feature page view。</p>
<p>适合验证：</p>
<ul>
<li>路由参数适配</li>
<li>多个 Feature page view 的组合关系</li>
</ul>
<h4><code>router/*</code> 与 <code>layouts/*</code>（SHOULD）</h4>
<p>建议覆盖关键路径测试。</p>
<p>适合验证：</p>
<ul>
<li>layout 选择是否正确</li>
<li>权限路由跳转是否正确</li>
<li>关键守卫逻辑是否正确</li>
</ul>
<h3>18.3 测试原则</h3>
<ul>
<li>测试应优先覆盖"逻辑边界"与"交互关键路径"，而非机械追求覆盖率。</li>
<li>页面层越薄，越应减少对 <code>pages/*Page.vue</code> 的重复测试，转而测试 Feature page view 与 composables。</li>
<li>数据映射、失效刷新、页面级编排是高价值测试点。</li>
</ul>
<hr />
<h2>19. 后续增强项（P2）</h2>
<p>以下内容不作为当前版本的强制主规范，但建议在后续版本逐步补充，用于完善工程一致性。</p>
<h3>19.1 性能与拆包策略（MAY）</h3>
<p>建议后续补充：</p>
<ul>
<li>路由级懒加载约束</li>
<li>Feature page view 的分包策略</li>
<li>大型图表或重模块的按需加载规则</li>
<li>避免基础组件无节制全局注册的约束</li>
</ul>
<h3>19.2 样式与设计令牌边界（MAY）</h3>
<p>建议后续补充：</p>
<ul>
<li>全局 tokens 的存放方式</li>
<li>Feature 私有样式与共享样式的边界</li>
<li>业务样式是否允许进入 shared/base</li>
<li>scoped 样式与全局样式的适用范围</li>
</ul>
<h3>19.3 可观测性规范（MAY）</h3>
<p>建议后续补充：</p>
<ul>
<li>页面访问埋点落点</li>
<li>业务事件埋点落点</li>
<li>全局错误上报位置</li>
<li>日志与监控信息的分层归属</li>
</ul>
<hr />
<h2>结论</h2>
<p>本规范的核心结构可概括为：</p>
<ul>
<li><strong>扁平 Pages 负责路由入口</strong></li>
<li><strong>Features 负责业务实现闭环</strong></li>
<li><strong>App 层负责启动与根壳装配</strong></li>
<li><strong>Layout 负责页面外壳</strong></li>
<li><strong>Services 负责数据访问</strong></li>
<li><strong>Shared 只承载真正通用的基础能力</strong></li>
</ul>
<p>所有新代码均应在此框架下组织。若需例外，应明确说明原因、范围与后续收敛计划。</p>
]]></content>
    <author><name>我叫石头鱼</name></author>
    <category term="前端"/>
  </entry>
  <entry>
    <title>我的第一篇文章</title>
    <link href="https://stonefish.space/my-first-blog/" rel="alternate" type="text/html"/>
    <id>https://stonefish.space/my-first-blog/</id>
    <published>2026-01-08T00:00:00.000Z</published>
    <updated>2026-01-08T00:00:00.000Z</updated>
    <summary>第一篇文章</summary>
    <content type="html"><![CDATA[<h1>我的第一篇文章</h1>
<h2>首先 一些测试</h2>
<h3>代码</h3>
<pre><code>#include &lt;stdio.h&gt;
int main() {
    printf("Hello, World!\n");
    return 0;
}
</code></pre>
<pre><code>print("Hello, World!")
</code></pre>
<h3>图</h3>
<p><img src="./images/1.png" alt="一张图" /></p>
<h3>引言</h3>
<blockquote>
<p>一级引言段落1
一级引言段落2</p>
<blockquote>
<p>嵌套二级引言</p>
</blockquote>
<p>回到一级引言，支持嵌套列表：</p>
<ol>
<li>引言内有序列表</li>
<li>列表项2</li>
</ol>
<blockquote>
<h2>引言内标题</h2>
<p><code>引言内代码</code></p>
</blockquote>
</blockquote>
<h3>无序列表</h3>
<ul>
<li>无序列表项1</li>
<li>无序列表项2
<ul>
<li>嵌套子项（缩进 3 空格或 1 tab）</li>
<li>嵌套子项</li>
</ul>
</li>
</ul>
<h2>特殊用法</h2>
<h3>Github</h3>
<p>::github{repo="sty20030818/StoneHub"}</p>
<h3>提示框</h3>
<p>:::note
这是一个note提示框
:::</p>
<p>:::tip
这是一个tip提示框
:::</p>
<p>:::important
这是一个important提示框
:::</p>
<p>:::warning
这是一个warning提示框
:::</p>
<p>:::caution
这是一个caution提示框
:::</p>
<h3>折叠剧透</h3>
<p>这是普通文本,剧透内容 :spoiler[<strong>剧透内容</strong>]</p>
]]></content>
    <author><name>我叫石头鱼</name></author>
    <category term="Essays"/>
  </entry>
</feed>
