普通视图

发现新文章,点击刷新页面。
昨天以前首页

🍎用 pretext 搞定输入框动态宽度:一个困扰了我三天的 CSS 问题

2026年4月13日 17:21

pretext-cover.png

通过 @chenglou/pretext 库在前端精确计算文本宽度,实现了搜索表单输入框的动态宽度适配。告别 min-width 的粗暴限制,让 UI 更精致。

背景

做企业管理系统,搜索表单是最常见的组件。一个头疼的问题是:输入框宽度怎么定?

定死了,长文本挤成省略号;用 min-width,不同字段长度不同,结果参差不齐。

pretext1.png

直到我发现了 pretext 这个库。

问题场景

看这个搜索表单:

<!-- SearchForm/index.vue -->
<template>
  <div class="search-area">
    <el-form :inline="true" :model="formData" class="search-form">
      <el-form-item v-for="field in fields" :key="field.prop">
        <el-input
            v-if="field.type === 'input'"
            v-model="formData[field.prop]"
            :placeholder="field.placeholder || `请输入${field.label}`"
            :style="getFieldStyle(field)"
        />
        <!-- 日期范围选择 -->
        <el-date-picker
            v-else-if="field.type === 'dateRange'"
            v-model="formData[field.prop]"
            type="datetimerange"
            :style="getFieldStyle(field)"
        />
        <!-- 下拉选择 -->
        <el-select
            v-else-if="field.type === 'select'"
            v-model="formData[field.prop]"
            :placeholder="field.placeholder || `请选择${field.label}`"
            :style="getFieldStyle(field)"
        />
      </el-form-item>
    </el-form>
  </div>
</template>

字段配置是动态的:

const fields = [
  { prop: 'name', label: '姓名', type: 'input' },
  { prop: 'idCard', label: '身份证号', type: 'input' },
  { prop: 'createTime', label: '创建时间', type: 'dateRange' },
  { prop: 'status', label: '状态', type: 'select', options: [...] }
]

字段有长有短:

  • "请输入姓名" → 短
  • "请输入身份证号码" → 长
  • "请选择开始时间 至 请选择结束时间" → 更长

核心问题:每个字段的 placeholder 长度不同,如何让输入框宽度刚刚好?

常见的"摆烂"方案

方案 1:固定宽度

.el-input {
  width: 200px; /* 要不挤死,要不太空 */
}

方案 2:min-width

.el-input {
  min-width: 180px;
  width: auto;
}

结果就是参差不齐——"姓名"和"身份证号"都是 200px,但明显应该不同宽度。

方案 3:后端返回宽度配置

每个字段配一个宽度值,后端告诉我该多宽。

工作量大,而且字段改了要同步改配置。

解决方案:pretext 文本测量

@chenglou/pretext 是一个纯 JS 的文本渲染库,能精确计算给定字体样式下文本的像素宽度。

核心原理:

  1. 传入文本 + 字体样式
  2. 返回每个字符的位置信息
  3. 由此计算出文本总宽度

安装

yarn add @chenglou/pretext

核心代码

// fieldStyle.js
import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext';

// 字体样式,与 Element Plus el-input 保持一致
const FONT_STYLE = '14px "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif';

/**
 * 根据 placeholder 文本计算宽度
 * @param {string} placeholderText - 占位符文本
 * @param {number} extraPadding - 额外的内边距(默认 50px)
 * @returns {number} 计算后的宽度
 */
export function getPlaceholderWidth(placeholderText, extraPadding = 50) {
  if (!placeholderText) return 200;
  const prepared = prepareWithSegments(placeholderText, FONT_STYLE);
  const result = layoutWithLines(prepared, 1000, 14);
  return Math.ceil(result.lines[0].width) + extraPadding;
}

/**
 * 获取字段宽度样式
 * @param {Object} field - 字段配置
 * @returns {Object} 宽度样式对象 { width: string }
 */
export function getFieldStyle(field) {
  let placeholderText = field.placeholder || `请输入${field.label}`;
  let extraPadding = 50;

  // dateRange 类型需要更宽(显示两个日期 + 分隔符)
  if (field.type === 'dateRange') {
    const startPlaceholder = field.startPlaceholder || `${field.label}开始时间`;
    const endPlaceholder = field.endPlaceholder || `${field.label}结束时间`;
    placeholderText = startPlaceholder + endPlaceholder;
    extraPadding = 80; // dateRange 控件本身更宽
  }

  const width = getPlaceholderWidth(placeholderText, extraPadding);
  return { width: `${width}px` };
}

使用效果

字段 placeholder 计算宽度
姓名 请输入姓名 132px
身份证号 请输入身份证号 172px
创建时间 请选择开始时间至请选择结束时间 340px

输入框宽度自适应文本长度,视觉上整齐划一。

image.png

这样看是不是舒服多了!!!

踩坑记录

坑 1:字体必须完全一致

Element Plus 的 input 使用系统字体,如果 pretext 的字体定义和它不一致,计算出来的宽度会有偏差。

解决:直接从浏览器 DevTools 抄 Element Plus 的实际字体样式:

// Chrome DevTools Elements 面板
// 检查 .el-input__inner 的 computed styles
const FONT_STYLE = '14px "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif';

坑 2:dateRange 需要特殊处理

日期范围选择器显示的是两个日期 + "至"分隔符,宽度明显比普通输入框大。

解决:针对 dateRange 类型,把开始和结束的 placeholder 拼接起来计算,并且增大 extraPadding

坑 3:计算结果偏小

单独计算每个字段后,实际渲染还是有点挤。

原因:输入框还有 padding、border 等自身宽度。

解决:加了 extraPadding = 50px 的缓冲,不同类型调整这个值。

完整组件代码

utils/fieldStyle.js

import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext';

const FONT_STYLE = '14px "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif';

export function getPlaceholderWidth(placeholderText, extraPadding = 50) {
  if (!placeholderText) return 200;
  const prepared = prepareWithSegments(placeholderText, FONT_STYLE);
  const result = layoutWithLines(prepared, 1000, 14);
  return Math.ceil(result.lines[0].width) + extraPadding;
}

export function getFieldStyle(field) {
  let placeholderText = field.placeholder || `请输入${field.label}`;
  let extraPadding = 50;

  if (field.type === 'dateRange') {
    const startPlaceholder = field.startPlaceholder || `${field.label}开始时间`;
    const endPlaceholder = field.endPlaceholder || `${field.label}结束时间`;
    placeholderText = startPlaceholder + endPlaceholder;
    extraPadding = 80;
  }

  const width = getPlaceholderWidth(placeholderText, extraPadding);
  return { width: `${width}px` };
}

components/SearchForm/index.vue

<template>
  <div class="search-area">
    <el-form :inline="true" :model="formData" class="search-form">
      <el-form-item v-for="field in fields" :key="field.prop">
        <!-- 输入框 -->
        <el-input
            v-if="field.type === 'input'"
            v-model="formData[field.prop]"
            :placeholder="field.placeholder || `请输入${field.label}`"
            clearable
            :style="getFieldStyle(field)"
        />
        <!-- 日期范围选择 -->
        <el-date-picker
            v-else-if="field.type === 'dateRange'"
            v-model="formData[field.prop]"
            type="datetimerange"
            range-separator="至"
            :start-placeholder="field.startPlaceholder || `${field.label}开始时间`"
            :end-placeholder="field.endPlaceholder || `${field.label}结束时间`"
            value-format="YYYY-MM-DD HH:mm:ss"
            format="YYYY-MM-DD HH:mm:ss"
            :style="getFieldStyle(field)"
        />
        <!-- 单个日期选择 -->
        <el-date-picker
            v-else-if="field.type === 'date'"
            v-model="formData[field.prop]"
            type="datetime"
            :placeholder="field.placeholder || `请选择${field.label}`"
            value-format="YYYY-MM-DD HH:mm:ss"
            format="YYYY-MM-DD HH:mm:ss"
            :style="getFieldStyle(field)"
        />
        <!-- 下拉选择 -->
        <el-select
            v-else-if="field.type === 'select'"
            v-model="formData[field.prop]"
            :placeholder="field.placeholder || `请选择${field.label}`"
            clearable
            :style="getFieldStyle(field)"
        >
          <el-option
              v-for="option in field.options"
              :key="option.value"
              :label="option.label"
              :value="option.value"
          />
        </el-select>
      </el-form-item>
      <el-form-item class="search-btn-group">
        <el-button type="primary" @click="handleSearch">搜索</el-button>
        <el-button @click="handleReset">重置</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script setup>
import { ref, watch, reactive } from 'vue';
import { getFieldStyle } from '@/utils/fieldStyle';

const props = defineProps({
  modelValue: { type: Object, default: () => ({}) },
  fields: { type: Array, default: () => [] }
});

const emit = defineEmits(['update:modelValue', 'search', 'reset']);

const formData = reactive({ ...props.modelValue });

const initFormData = () => {
  props.fields.forEach(field => {
    if (!(field.prop in formData)) {
      formData[field.prop] = field.type === 'dateRange' ? [] : '';
    }
  });
};

watch(() => props.fields, () => { initFormData(); }, { immediate: true, deep: true });
watch(() => props.modelValue, (val) => { Object.assign(formData, val); }, { deep: true });

const handleSearch = () => {
  emit('search', { ...formData });
};

const handleReset = () => {
  props.fields.forEach(field => {
    formData[field.prop] = field.type === 'dateRange' ? [] : '';
  });
  emit('reset');
};
</script>

<style lang="scss" scoped>
.search-area {
  background: #fff;
  padding: 10px;
  margin-bottom: 0;
  border-radius: 4px;

  .search-form {
    .el-form-item {
      margin-right: 10px;
      margin-bottom: 12px;
    }
  }
}
</style>

对比效果

方案 姓名输入框 身份证号输入框 日期范围选择器
固定 200px 200px (空旷) 200px (刚好) 200px (挤)
min-width: 180px 180px (挤) 200px+ (不统一) 200px+ (不统一)
pretext 动态宽度 132px 172px 340px

适用场景

适合用 pretext 的场景:

  • 动态表单,字段配置来自后端
  • 多语言系统,不同语言文本长度差异大
  • 需要精细控制 UI 尺寸的企业级应用

不适合用的场景:

  • 固定的几 个字段,直接配固定宽度更简单
  • 性能敏感的热路径,pretext 计算有开销
  • 响应式布局,容器宽度本身就在变

总结

pretext 解决了文本宽度计算的难题,让输入框宽度能"自适应"文本长度。核心就两个 API:

const prepared = prepareWithSegments(text, fontStyle);
const result = layoutWithLines(prepared, maxWidth, fontSize);
result.lines[0].width; // 文本宽度

配上调优的 extraPadding,基本能覆盖大部分场景。


有问题欢迎留言交流。

⚡精通Claude第3课:学会用Skills让Claude变身为专属专家

2026年4月12日 12:23

skill.png

Skills 是 Claude Code 的大招——你可以给它装上各种"技能包",让它变成代码review专家、部署达人、或者任何你需要的专业助手。一次配置,随时调用。

image.png

你有没有遇到过这种情况?

  • 每次让 Claude 帮你review代码,你都得先把评审标准说一遍。
  • 每次让它部署,你都得解释一遍流程。
  • 每次让它写文档,你都得强调一遍格式要求。

累不累.jpg

Claude Agent Skills 就是来解决这个问题的。它像是给 Claude 装了一个"技能插槽",你提前把专业知识塞进去,以后 Claude 一看到相关任务,自动就调用对应的技能——不用你重复唠叨。


什么是 Skills?说人话!

Skills = 预置的专业知识包。

你可以把它理解成:

  • 医学院的选修课:Claude 原本是个通才,你给它上一门"代码review专业课",它就成了这方面的专家
  • 厨房的调料盒:提前准备好各种调料,做菜时直接撒,不用每次都现调
  • 武侠小说的武功秘籍:把内功心法提前输入,Claude 遇到敌人自动出招

Skills 最大的特点:用的时候才加载,不用时不占地方

这就厉害了——你可以装几十个技能,Claude 也只会在真正用到的时候才把对应技能的内容调进来。不会把上下文窗口塞爆。


三层加载机制(渐进式披露)

Skills 用了一种很聪明的设计,分三层加载:

┌─────────────────────────────────────────────┐
│  第1层:元数据(约100 tokens)               │
│  - 技能名称 + 简短描述                        │
│  - Claude 启动时就知道了这些技能存在           │
└──────────────────────┬──────────────────────┘
                       │ 触发技能时
┌──────────────────────▼──────────────────────┐
│  第2层:指令(约5k tokens)                   │
│  - SKILL.md 的正文内容                        │
│  - 工作流程、指导原则                         │
└──────────────────────┬──────────────────────┘
                       │ 需要更多资源时
┌──────────────────────▼──────────────────────┐
│  第3层:资源文件(无限)                      │
│  - 模板、脚本、示例代码                        │
│  - 按需加载,不进上下文                        │
└─────────────────────────────────────────────┘

用人话讲就是:

  1. 启动时:Claude 知道你有 N 个技能,每个技能是干嘛的
  2. 触发时:Claude 发现这个任务需要某技能,才把技能说明书加载进来
  3. 需要时:Claude 发现还需要模板或脚本,才去读取对应文件

这样设计的好处:你装 100 个技能也不会变慢,因为 Claude 不是一次性全加载。


Skill 的目录结构

一个技能长这样:

my-awesome-skill/
├── SKILL.md              # 主角,必须有
├── templates/            # 模板文件夹
│   └── output-format.md
├── examples/             # 示例文件夹
│   └── sample-output.md
└── scripts/              # 脚本文件夹
    └── validate.sh

最核心的是 SKILL.md,长这样:

---
name: my-skill
description: 这个技能是干嘛的,什么时候该用它
---

# 技能标题

## 使用说明
一步一步告诉 Claude 该怎么做

## 注意事项
有哪些坑要避开

举个例子:做一个代码review专家

假设你想让 Claude 每次review代码都按照你公司的标准来:

目录结构:

~/.claude/skills/code-review/
├── SKILL.md
├── templates/
│   └── review-checklist.md
└── scripts/
    └── analyze-metrics.py

SKILL.md 写起来:

---
name: code-review-expert
description: 代码评审专家,专注安全、性能、质量分析。
              当你提到 code review、代码评审、PR review 时触发。
---

# 代码评审专家

## 评审维度

1. **安全**:认证授权、数据泄露、注入风险
2. **性能**:算法效率、数据库查询优化
3. **质量**:SOLID原则、命名规范、测试覆盖
4. **可维护性**:代码可读性、函数长度、圈复杂度

## 评审流程

1. 先通读代码,理解整体结构
2. 按上面4个维度逐一检查
3. 整理问题,按严重程度排序
4. 给出具体的修复建议

## 严重程度定义

- **Critical**:必须立即修复,有安全风险
- **High**:应该在下次迭代前修复
- **Medium**:建议修复,不紧急
- **Low**:可选的优化项

详细 Checklist  [templates/review-checklist.md](templates/review-checklist.md)

review-checklist.md 长这样:

# Code Review Checklist

## 安全性
- [ ] 是否有 SQL 注入风险
- [ ] 用户输入是否做了校验
- [ ] 敏感数据是否明文存储
- [ ] 权限校验是否完整

## 性能
- [ ] 是否有 N+1 查询问题
- [ ] 循环中是否有不必要的数据库调用
- [ ] 是否需要加缓存
- [ ] 大数据量是否有分页

## ...

现在,当你跟 Claude 说"帮我review一下这个PR",它自动就知道:

  • 要从哪几个维度评审
  • 问题怎么分类
  • 严重程度怎么定义
  • 该输出什么格式的报告

不需要你每次都解释一遍。


控制 Claude 什么时候能调用技能

Skills 有三种调用模式,通过 frontmatter 控制:

---
# 模式1:默认(你也可以调用,Claude 也可以调用)
# 不写任何额外配置就行

# 模式2:只有你能调用,Claude 不能主动用
disable-model-invocation: true
# 适合有副作用的操作,比如部署、删除数据

# 模式3:只有 Claude 能调用,你看不到(不显示在 / 菜单)
user-invocable: false
# 适合后台知识,比如解释旧系统怎么工作的

用人话讲:

  • disable-model-invocation: true = 这个技能太危险了,只有我能触发

禁止模型自动调用该技能,仅允许用户手动通过 /skill-name 触发

  • user-invocable: false = 这是 Claude 的私藏知识,不需要当命令用

用户手动不能调用,只能模型自动调用


动态内容注入

Skills 支持用反引号执行命令,把结果塞进技能内容里:

---
name: pr-summary
description: 总结 Pull Request 的内容
---

## PR 信息
- PR 差异:!`gh pr diff`
- PR 评论:!`gh pr view --comments`
- 改动的文件:!`gh pr diff --name-only`

## 你的任务
根据以上信息,生成一份 PR 总结

image.png 比如这里有一个自动生成commit信息的skill

  1. git status - 显示当前工作区的状态(有哪些文件被修改、暂存或未跟踪)
  2. git diff HEAD - 显示 HEAD 指向的提交与工作区之间的差异(已修改但未暂存的内容)
  3. git branch --show-current - 显示当前所在的分支名称
  4. git log --oneline -10 - 显示最近 10 条提交记录,每条只显示一行(哈希值前7位 + 提交信息)

这些命令组合在一起,能快速了解仓库的完整状态:当前分支、有哪些变更、以及近期提交历史。这通常用于提交前的检查或生成变更记录。

!command`` 会在技能内容加载前执行命令,输出结果直接拼进去。Claude 拿到的时候已经是展开后的完整上下文了。


用 subagent 运行技能(隔离执行)

有时候技能执行起来很复杂,你会想把它放到一个独立的子进程里跑,不占用主会话的上下文。

---
name: deep-research
description: 深入研究某个主题
context: fork        # 关键:fork 一个独立子agent
agent: Explore      # 用 Explore 类型
---

深入研究 $ARGUMENTS:
1.  Glob  Grep 找相关文件
2. 读代码,分析逻辑
3. 总结发现,附上具体文件引用
  • context: fork:创建隔离的子对话 / 子 Agent,不污染主上下文

  • agent: Explore:使用探索专用 AI,擅长遍历、搜索、分析项目结构

context: fork 会在一个独立子 agent 里执行这个技能,子 agent 有自己的上下文窗口。适合:

  • 研究任务,需要深度探索
  • 复杂任务,步骤很多
  • 你不想让主会话变乱的时候

技能放在哪?四种作用域

类型 位置 谁能用 场景
企业级 管理员配置 全公司 公司统一规范
个人 ~/.claude/skills/ 只有你 个人工作流
项目 .claude/skills/ 项目成员 团队标准
插件 插件目录 看插件配置 插件附带

团队协作推荐用项目级技能:丢进 .claude/skills/ 目录,commit 到 git,团队成员 pull 下来就能用。


实际使用场景

场景1:每次代码提交都要规范信息

name: commit-helper
description: 帮助写规范的 commit message
---

# Commit Message 助手

## 格式要求

():

[optional body]

[optional footer]


## Type 只能选这些
- feat:新功能
- fix:bug修复
- docs:文档改动
- style:格式(不影响代码)
- refactor:重构
- test:测试
- chore:构建/工具

## 示例
feat(auth): 添加微信登录支持

实现了微信 OAuth2.0 登录流程
- 扫码登录
- token 刷新
- 退出登录

## 你的任务
根据我给的改动,写出符合格式的 commit message

场景2:部署要按流程来,不能出错

name: deploy
description: 部署应用到生产环境
disable-model-invocation: true  # 太危险,Claude 不能自己触发,只能/name,手动触发
---

部署 $ARGUMENTS 到生产环境:

1. 运行测试:`npm test`
2. 构建应用:`npm run build`
3. 推送部署目标
4. 验证部署是否成功
5. 报告部署状态

如果任何步骤失败,立即停止并报告错误。

场景3:品牌调性知识(Claude 自己看)

name: brand-voice
description: 确保输出内容符合品牌调性(Claude 后台使用)
user-invocable: false
---

## 语气要求
- 友好但不随意
- 清晰简洁,不用黑话
- 自信但不傲慢
- 有同理心,理解用户需求

## 写作规范
- 用"你"称呼读者
- 用主动语态
- 句子控制在20字以内
- 先说价值,再说细节

最佳实践

1. 描述要具体,包含触发词

# ❌ 太泛
description: 帮助处理文档

# ✅ 具体,Claude 知道什么时候该用
description: 提取 PDF 中的文字和表格,填写表单,合并文档。
             当你提到 PDF、表单、文档提取时触发。

2. 一个技能做一件事

# ❌ 太宽泛
name: document-helper
description: 处理各种文档相关任务

# ✅ 专注一件事
name: pdf-extractor
description:  PDF 文件提取文字、表格、图片

3. SKILL.md 控制在 500 行以内

详细的检查清单、API 文档放到 templates/references/ 目录,Claude 需要时再加载。

4. 描述要写清楚"什么时候用"

这是 Claude 决定是否触发技能的关键依据。


常见问题

Q: Claude 不触发我的技能怎么办? A: 检查 description 是否包含了用户会说的关键词。描述越具体,触发越准确。

Q: 技能触发太频繁怎么办? A: 把 description 写窄一点,或者加上 disable-model-invocation: true

Q: 安装太多技能会变慢吗? A: 不会。Skills 是渐进式加载,Claude 只在触发时才会加载对应技能的内容。

Q: 技能冲突怎么办? A: 优先级:企业 > 个人 > 项目。同名技能,高优先级生效。


总结

Skills 是什么?提前预置的专业知识包。

为什么有用?不用重复唠叨,Claude 自动调用。

怎么用?

  1. 创建 .claude/skills/<name>/SKILL.md
  2. 写清楚 name 和 description
  3. 描述里加上触发关键词

现在去试试吧,给你常用的工作流建一个技能,你会发现 Claude 突然变得专业多了。


更多资料

⚡精通 Claude 第 1 课:掌握 Slash Commands

2026年4月8日 17:45

Slash Commands 是 Claude Code 中的快捷方式,通过 / 触发。55+ 内置命令、Skills、自定义命令、MCP 提示词都通过这种机制工作。本文覆盖常用命令、自定义 Skills 创建、以及实战技巧。


slash-command.png

什么是 Slash Commands

Slash Commands 是 Claude Code 中的核心交互机制。在对话中输入 / 开头的指令,Claude 会直接执行对应操作,而不是继续对话。

/help        → 显示帮助
/clear       → 清空对话
/plan        → 进入计划模式
/compact     → 压缩上下文

image.png 这不是对话的延续,而是命令执行。这是 Claude Code 与普通 AI 对话的本质区别。


内置命令速查

Claude Code 提供了 55+ 内置命令,覆盖日常开发全流程:

高频必备

命令 用途
/help 显示所有可用命令
/clear 清空当前对话(别名:/reset, /new
/plan 进入计划模式,让 Claude 先分析再执行
/compact 压缩上下文,保留关键信息
/diff 查看未提交的文件变更
/model 切换 AI 模型

image.png

Git 工作流

命令 用途
/pr-comments <PR号> 获取 GitHub PR 评论
/branch [name] 创建分支(别名:/fork
/resume [session] 恢复历史对话

image.png

系统状态

命令 用途
/status 版本、模型、账户信息
/cost Token 消耗统计
/stats 每日使用可视化
/context 可视化上下文占用

image.png

image.png

image.png 最近一个月用了27天,真的用了CC就回不去古法编程了😂

image.png

Claude Code 配置

命令 用途
/config 打开设置界面
/hooks 查看钩子配置
/mcp 管理 MCP 服务器
/plugin 管理插件
/theme 切换颜色主题
/permissions 调整工具权限

image.png

核心配置项(已显示)

  • Auto-compact:自动压缩冗余对话历史,防止上下文溢出
  • Show tips:显示使用提示与快捷键,辅助上手
  • Reduce motion:关闭界面动画,提升响应速度
  • Thinking mode:开启深度推理,保障复杂任务准确性
  • Fast mode:降低推理深度,快速响应(仅 Opus 4.6)
  • Rewind code:创建代码修改检查点,支持一键回退
  • Verbose output:输出详细调试日志,用于排错
  • Terminal progress bar:显示任务进度,直观查看状态
  • Show turn duration:标注单次交互的耗时,评估性能
  • Default permission mode:控制文件 / 命令操作的默认权限(手动确认 / 允许 / 拒绝)

隐藏配置项(12 more below)

  • Show line numbers:生成代码时自动显示行号
  • Show timestamps:对话历史中显示时间戳
  • Model selection:设置默认使用的模型(Sonnet/Opus/Haiku)
  • Context window limit:手动设置上下文窗口的最大 Token 数
  • Sandbox mode:开启沙箱隔离,限制危险操作
  • Custom hooks:配置自定义脚本(执行前 / 后触发)
  • Keyboard shortcuts:自定义操作快捷键,提升效率
  • Output format:设置响应内容的输出格式(文本 / Markdown/JSON)
  • Allowed tools:指定 Claude 可使用的工具列表
  • Disallowed tools:禁用 Claude 的指定工具
  • Prompt caching:开启提示词缓存,加速重复请求
  • Max thinking tokens:限制思考模式可使用的最大 Token 数
  • Status line customization:自定义终端状态栏的显示内容

image.png/hooks 就是给 Claude Code 加自定义规则、自动脚本、触发动作的地方。

你可以把它理解成:给 AI 助手装插件、设规矩、让它自动帮你干活。

举几个最容易懂的例子:

  1. 每次 Claude 改代码前,自动备份文件
  2. 代码保存后,自动跑 lint 检查格式
  3. 禁止 Claude 访问某些敏感文件
  4. 每次生成代码后,自动格式化
  5. 让 Claude 每次启动都加载你的项目规则

本质是什么?

hooks = 你给 AI 定的自动化小规则。不用你手动点、不用你重复输命令,AI 会按你的规矩自动运行。

image.png

image.png

image.png

image.png

Bundled Skills:内置技能包

Skills 是增强版的 Commands,可以打包脚本、模板和参考文件:

Skill 用途
/batch <instruction> 使用 worktree 并行执行大规模修改
/claude-api 加载项目语言的 Claude API 参考
/debug [description] 开启调试日志
/loop [interval] <prompt> 定时重复执行提示词
/simplify [focus] 检查代码质量

/batch 是 Claude Code 的批量任务执行工具,让你一次性给 AI 发一长串任务清单,它会按顺序自动跑完所有任务,不用你一次次手动发指令、等回复。 你可以把 /batch 理解成:给 Claude Code 开了个「自动流水线」

平时用 Claude Code 是「一问一答」:你发一个需求,AI 做完一个,你再发下一个,全程要手动跟进。而 /batch 就是把你所有要做的事,一次性打包成一个「任务清单」喂给 AI,它会自动按顺序、不中断地把所有任务全部执行完,中间完全不用你手动干预。

比如你要给一个前端项目做这些事:

  1. 把所有组件的 console.log 都删掉
  2. 给所有接口请求加统一的错误捕获
  3. 给所有按钮加 loading 状态防重复点击
  4. 跑一遍 eslint 修复格式问题
  5. 生成一份修改说明文档

如果不用 /batch:你要分 5 次发指令,每次等 AI 做完,再手动发下一个,全程要盯着。用了 /batch:你把这 5 个任务一次性写进 /batch,然后去喝杯咖啡,回来 Claude 已经把所有任务全做完了,直接给你最终结果。


/debug 是 Claude Code 的问题排查工具,专门用来显示后台详细日志,帮你快速找到 AI 为什么出错、卡壳、不干活。

image.png 平时用 Claude Code,你只能看到 AI 给你的最终结果:代码、回答、提示。但 AI 内部到底干了什么、调用了什么工具、读了哪些文件、哪里卡住了,你是看不见的。

/debug 就是把这些 “后台秘密” 全部亮出来给你看。

开启后,Claude Code 会显示:

  • AI 正在调用什么命令
  • 读取 / 修改了哪些文件
  • 为什么拒绝执行某个操作
  • 哪里报错、哪里超时、哪里卡住
  • 模型思考过程、工具执行结果

/loop 是 Claude Code 的自动循环重试工具,让 AI 自己反复检查、修改、运行代码,直到满足要求为止,不用你反复手动提醒。

平时你让 AI 写代码、改 Bug,经常会出现这种情况:AI 改一次 → 运行报错 → 你告诉它错了 → 它再改一次 → 又报错 → 你又得提醒……

来回折腾特别麻烦,效率很低。

/loop 就是解决这个问题的:你开启循环模式,AI 会自己 “闭环干活”

它会自动做这几件事:

  1. 改代码
  2. 运行测试 / 检查报错
  3. 自己发现哪里错了
  4. 自动再次修改
  5. 直到运行成功、没有错误,才停下来告诉你完成

简单说:你只需要说需求,剩下的反复调试让 AI 自己循环搞定,不用你插手。


/simplify 命令可以用来优化代码

image.png


自定义 Skills:打造你的命令体系

Skills 是 .claude/skills/<name>/SKILL.md 文件:

image.png

mkdir -p .claude/skills/optimize

文件:.claude/skills/optimize/SKILL.md

---
name: optimize
description: 分析代码性能问题、内存泄漏和优化机会
argument-hint: <代码片段>
allowed-tools: Bash, Read, Grep
---

# 代码优化分析

分析以下代码的性能问题:

$ARGUMENTS

检查:
1. 是否有不必要的重复计算
2. 是否存在内存泄漏风险
3. 算法复杂度是否有优化空间
4. 是否有冗余的 DOM 操作或 API 调用

变量替换

全部参数:

---
name: fix-issue
description: 修复 GitHub Issue
---

修复 Issue #$ARGUMENTS

单个参数:

---
name: review-pr
description: 评审 PR
---

评审 PR #$0,优先级 $1

动态上下文(Shell 命令):

---
name: commit
description: 创建带上下文的 Git 提交
allowed-tools: Bash(git *)
---

## 当前状态

- Git 状态:!`git status`
- 文件变更:!`git diff HEAD`
- 当前分支:!`git branch --show-current`

## 任务

基于以上变更,创建提交信息。

文件引用

请审查以下实现:
@src/utils/helpers.js
对比 @src/old-version.js 和 @src/new-version.js

Frontmatter 完整参考

---
name: my-command           # 命令名称(成为 /my-command)
description: 用途描述      # 帮助 Claude 判断何时使用
argument-hint: <参数>      # 自动补全提示
allowed-tools: Bash, Read  # 无需审批即可使用的工具
model: opus                # 指定使用的模型
disable-model-invocation: true  # 仅用户可调用
user-invocable: false      # 从 / 菜单隐藏
context: fork              # 在隔离子代理中运行
agent: general-purpose     # 子代理类型
hooks:                     # 技能级钩子
  PreToolUse: []
  PostToolUse: []
  Stop: []
---

命令执行流程

sequenceDiagram
    participant User
    participant Claude
    participant FS
    participant Shell

    User->>Claude: /optimize
    Claude->>FS: 搜索 .claude/skills/ 和 .claude/commands/
    FS-->>Claude: 返回 optimize/SKILL.md
    Claude->>Claude: 解析 Frontmatter
    Claude->>Shell: 执行 !`command` 替换
    Shell-->>Claude: 返回命令输出
    Claude->>Claude: 替换 $ARGUMENTS
    Claude->>User: 处理提示词
    Claude->>User: 返回结果

实战技巧

1. 用 /plan 避免浪费

在执行大改动前,输入:

/plan 重构用户认证模块

Claude 会先分析代码、制定计划,你确认后再执行。避免做到一半发现方向错了。

2. 用 /compact 保持专注

长对话变慢时,/compact 压缩上下文,保留核心信息,速度恢复。

3. 自定义快捷命令

把常用的复杂操作封装为 Skill:

---
name: deploy
description: 部署到生产环境(仅用户可调用)
disable-model-invocation: true
allowed-tools: Bash(npm *), Bash(git *)
---

1. 运行测试:npm test
2. 构建:npm run build
3. 推送到部署目标
4. 验证部署状态

4. MCP 提示词作为命令

MCP 服务器暴露的提示词可以直接调用:

/mcp__github__list_prs
/mcp__github__pr_review 456
/mcp__jira__create_issue "Bug 标题" high

Skill vs Legacy Command

特性 Skills(推荐) Legacy Commands
位置 .claude/skills/<name>/SKILL.md .claude/commands/<name>.md
目录结构 支持打包文件 单文件
自动触发 支持 不支持
子代理执行 context: fork 不支持
优先级 更高 较低

同名时,Skill 优先。


常见问题

命令不生效?

  • 确认文件在 .claude/skills/<name>/SKILL.md.claude/commands/<name>.md
  • 检查 frontmatter 的 name 字段
  • 重启 Claude Code 会话

Skill 和 Command 冲突?

  • 删除其中一个,或重命名
  • 同名时 Skill 总是优先

总结

Slash Commands 是 Claude Code 的核心交互范式:

  • 内置命令:55+ 覆盖开发全流程
  • Bundled Skills:开箱即用的增强能力
  • 自定义 Skills:打造个人命令体系
  • MCP 提示词:扩展到外部工具

下一课我们将深入 Memory 系统,学习如何让 Claude 记住项目上下文。


延伸阅读

❌
❌