第 28 课:任务页排序偏好与默认工作视图
第 28 课:任务页排序偏好与默认工作视图
这一课,我们继续沿着任务管理页主线往下走,把它再往真实后台系统推进一步:
让用户不只是临时切换排序,还能把当前排序保存成“默认工作视图”。
这件事看起来只是多了一个“记住排序”的功能,但它背后其实牵涉一个很关键的前端设计问题:
- 当前页面状态,应该怎么和 URL 协同?
- 个人使用偏好,又应该放在哪里?
- 当两者同时存在时,谁的优先级更高?
这一课一句话在做什么?
这一课我们完成了 6 件事:
- 给任务页增加“默认排序偏好”本地持久化。
- 让用户可以把“当前排序”保存成默认工作视图。
- 让用户可以一键恢复系统默认排序。
- 让任务页在 URL 没有显式
sort参数时,自动恢复用户自己的默认排序。 - 继续保持“真实当前视图”同步到 URL。
- 补上单元测试、查询同步测试、E2E 和文档。
这一课最重要的设计结论
这一课最重要的不是按钮本身,而是下面这条优先级规则:
显式 URL sort > localStorage 默认工作视图 > 系统默认排序
为什么这样设计?
1. URL 里的 sort 是“当前场景状态”
如果用户现在访问的是:
/tasks?sort=dueDateDesc
那就说明这次访问已经明确指定了当前视图应该怎么排。
这种状态的特点是:
- 可以刷新恢复
- 可以分享给别人
- 可以被浏览器前进后退保留
所以它的优先级应该最高。
2. localStorage 里的默认排序是“个人习惯”
如果用户平时习惯先看:
- 截止日期从远到近
那这是一种个人使用偏好。
这种状态的特点是:
- 更像“我的默认打开方式”
- 不一定要分享给别人
- 更适合保存在本机
所以它应该放进localStorage,而不是直接当成全局业务状态。
3. 系统默认排序只是兜底规则
如果:
- URL 没给
sort - 用户也没保存过默认工作视图
这时候才回退到系统默认排序,也就是:
default
这一课在useTasksPage里做了什么?
文件:
src/composables/useTasksPage.ts
这一课的核心仍然放在任务页组合式函数里,因为:
- 当前排序是页面状态
- 默认排序偏好也是页面级用户偏好
- 它们都不适合散落到多个组件里各自管理
新增了默认排序持久化能力
这一课新增了本地存储 key:
TASK_TABLE_DEFAULT_SORT_STORAGE_KEY
同时补了几类辅助函数:
- 排序合法性判断
- 排序标准化函数
- 默认排序读取函数
- 默认排序写回函数
- 排序值转中文文案函数
这说明我们已经开始在项目里重复练一种很重要的工程模式:
读取本地偏好 -> 做合法性清理 -> 写回标准化结果 -> 暴露给页面使用
把“当前排序”和“默认排序偏好”拆成了两份状态
这是这一课最关键的一点。
现在任务页里有两份排序相关状态:
sortOptiondefaultSortOption
它们不是一回事。
sortOption
表示:
- 当前这一次页面访问实际使用的排序方式
defaultSortOption
表示:
- 用户希望以后直接打开任务页时优先使用的默认排序
这两份状态拆开以后,逻辑会清晰很多:
- 你可以临时切换当前排序
- 也可以决定要不要把它保存成默认工作视图
- 还可以恢复系统默认
而不会把“当前场景状态”和“个人长期偏好”混成一份状态。
新增了几个更有业务语义的计算状态
例如:
currentSortOptionLabeldefaultSortOptionLabelisCurrentSortSavedAsDefaulthasCustomizedDefaultSort
这些状态的意义不是“为了少写几行模板”,而是:
- 让界面层更容易表达业务语义
- 让消息提示更清楚
- 让按钮显隐和禁用规则更直接
新增了两个真正有业务意图的动作函数
例如:
saveCurrentSortAsDefaultView()resetDefaultSortPreference()
这两个函数很值得你注意。
因为它们不再是简单的“修改某个 ref”,而是在表达明确的业务动作:
- 把当前排序保存成默认工作视图
- 恢复系统默认排序
这就是组合式函数越来越像“页面领域层”的表现。
这一课在 URL 同步层做了什么?
文件:
src/composables/useTaskFilterQuerySync.ts
这一课最关键的升级是:
sort 的默认回退值,不再被硬编码成 'default'。
现在它会变成:
- 先读 URL 里的
sort - 如果 URL 没有合法
sort - 就回退到
defaultSortOption
这一步非常重要,因为它真正把:
- URL 当前场景状态
- localStorage 默认工作视图
接到一起了。
为什么 URL 里最终还是要写回 sort?
这里很多初学者会疑惑:
- 既然默认排序来自 localStorage,为什么还要把它重新同步到 URL?
答案是:
因为一旦它已经成为“当前真实页面视图”,那它就不再只是一个隐形偏好,而是:
- 当前页面真正生效的状态
所以它应该被同步进 URL,这样才具备:
- 刷新恢复
- 链接分享
- 浏览器历史记录一致性
这也是这一课最值得建立的心智模型之一:
localStorage 可以决定初始值,但当前真实页面状态仍然应该由 URL 明确表达。
这一课在界面层做了什么?
文件:
src/components/tasks/TaskFilterBar.vuesrc/views/TasksView.vue
这一课在排序下拉框下面补了一块“默认工作视图”说明区。
它做了三件事:
- 展示当前默认排序是什么。
- 告诉你当前排序是否已经保存成默认工作视图。
- 提供“设为默认视图”和“恢复系统默认”按钮。
这里要重点理解一个分层原则:
TaskFilterBar只负责展示和抛出事件TasksView负责消息提示useTasksPage负责真正的状态和持久化逻辑
这和我们前面反复练习的分层方式是一致的。
这一课补了哪些测试?
1.useTasksPage.spec.ts
新增覆盖:
- 默认排序偏好恢复
- 非法默认排序清理
- 保存默认工作视图
- 恢复系统默认排序
- 当前排序状态与默认偏好状态分离
2.useTaskFilterQuerySync.spec.ts
新增覆盖:
- URL 没有
sort时,使用本地默认排序 - 显式
sort查询参数覆盖本地默认偏好 - 非法
sort查询参数回退到本地默认排序
这一层测试非常关键,因为这一课最容易出问题的地方其实不是按钮,而是:
- URL 和本地偏好的优先级
3.e2e/app.spec.ts
新增覆盖:
- 先切换排序
- 再保存默认工作视图
- 重新进入不带查询参数的
/tasks - 断言默认排序被自动恢复
- 再恢复系统默认
- 再次进入干净任务页地址
- 断言页面回到系统默认顺序
这能验证真正浏览器里最完整的一条用户路径。
这一课改了哪些文件?
src/composables/useTasksPage.tssrc/composables/useTaskFilterQuerySync.tssrc/components/tasks/TaskFilterBar.vuesrc/views/TasksView.vuesrc/composables/__tests__/useTasksPage.spec.tssrc/composables/__tests__/useTaskFilterQuerySync.spec.tse2e/pages/TasksPage.tse2e/app.spec.tsdocs/28-task-sort-preference-and-default-view.mddocs/README.md
这一课最值得你真正学会什么?
如果你只记住“多了一个默认排序按钮”,那还不够。
你更应该记住下面这 6 点:
- 当前页面状态和个人默认偏好,往往不是同一份状态。
- URL 更适合表达当前真实场景,本地存储更适合表达个人长期习惯。
- 当两者同时存在时,一定要提前定义清楚优先级。
- localStorage 里的值不能直接信任,读取后一定要标准化。
- 一个本地偏好一旦真正生效成当前页面视图,就应该同步回 URL。
- 这种“状态分层 + 优先级规则”的能力,一定要同时用单元测试和 E2E 验证。
这一课的验证结果
这一课相关改动完成后,应至少通过:
npm run test:unit -- --runnpm run type-checknpm run lintnpm run test:e2e -- --project=chromium
