开发编程
#typescript
frontend-dev
前端开发规范,包含 Vue 3 编码规范、UI 风格约束、TypeScript 规范等
DeepseekModel
官方收录技能
质量 优秀 · 90
v1.0.0
获取
https://deepseekmodel.com/api/download.php?id=doccker-cc-use-exp-claude-skills-frontend-dev-skill-md&format=skill
下载 .skill
标准格式,含 system_prompt 与 model_config,导入任意 Agent 框架即可使用
.skill 文件中 system_prompt 字段的实际内容。
name frontend-dev description 前端开发规范,包含 Vue 3 编码规范、UI 风格约束、TypeScript 规范等 version v3.0 paths ["**/*.vue","**/*.tsx","**/*.jsx","**/*.ts","**/*.js","**/*.css","**/*.scss","**/*.less","**/package.json","**/vite.config.*"] 前端开发规范 参考来源: Vue 官方风格指南、Element Plus 最佳实践 UI 风格约束 严格禁止(常见 AI 风格) ❌ 蓝紫色霓虹渐变、发光描边、玻璃拟态 ❌ 大面积渐变、过多装饰性几何图形 ❌ 赛博风、暗黑科技风、AI 风格 UI ❌ UI 文案中使用 emoji 后台系统(默认风格) 要素 要求 主题 使用组件库默认主题 配色 黑白灰为主 + 1 个主色点缀 动效 克制,仅保留必要交互反馈 技术栈 层级 Vue(首选) React(备选) 框架 Vue 3 + TypeScript React 18 + TypeScript 构建 Vite Vite 路由 Vue Router 4 React Router 6 状态 Pinia Zustand UI 库 Element Plus Ant Design Vue 编码规范 组件基础 <script setup lang="ts"> import { ref, computed, onMounted } from 'vue' import type { User } from '@/types' // Props & Emits const props = defineProps<{ userId: number }>() const emit = defineEmits<{ (e: 'update', value: string): void }>() // 响应式状态 const loading = ref(false) const user = ref<User | null>(null) // 计算属性 const displayName = computed(() => user.value?.name ?? '未知用户') // 生命周期 onMounted(async () => { await fetchUser() }) // 方法 async function fetchUser() { loading.value = true try { user.value = await api.getUser(props.userId) } finally { loading.value = false } } </script> <template> <div class="user-card"> <h3>{{ displayName }}</h3> </div> </template> <style scoped> .user-card { padding: 16px; } </style> 命名约定 类型 约定 示例 组件文件 PascalCase.vue UserCard.vue Composables useXxx.ts useAuth.ts Store useXxxStore.ts useUserStore.ts 状态管理(Pinia) // stores/user.ts export const useUserStore = defineStore ( 'user' , () => { const user = ref< User | null >( null ) const token = ref< string >( '' ) const isLoggedIn = computed ( () => !!token. value ) async function login ( username : string , password : string ) { const res = await api. login (username, password) token. value = res. token user. value = res. user } return { user, token, isLoggedIn, login } }) 交互状态处理 必须处理的状态 : loading、empty、error、disabled、submitting <template> <el-skeleton v-if="loading" :rows="5" animated /> <el-result v-else-if="error" icon="error" :title="error"> <template #extra> <el-button @click="fetchData">重试</el-button> </template> </el-result> <el-empty v-else-if="list.length === 0" description="暂无数据" /> <template v-else> <!-- 正常内容 --> </template> </template> TypeScript 规范 // types/user.ts export interface User { id : number username : string role : 'admin' | 'user' } export interface ApiResponse <T = unknown > { code : number message : string data : T } API 调用类型安全 规则 说明 ✅ API 工具函数支持泛型 get<T>(url): Promise<T> 而非返回 unknown ✅ 调用处指定泛型或断言 get<UserInfo>(url) 或 data as typeof ref.value ❌ 禁止 as any 绕过 掩盖类型问题,后续维护踩坑 // ❌ get() 返回 unknown,赋值报 TS2322 const data = await get ( '/contact/config' ) contact. value = data // ❌ as any 绕过 contact. value = data as any // ✅ 泛型约束(推荐) const data = await get< ContactConfig >( '/contact/config' ) contact. value = data // ✅ 类型断言(最小改动) contact. value = data as typeof contact. value 性能优化 场景 方案 大列表 虚拟滚动 路由 懒加载 () => import() 计算 使用 computed 缓存 大数据 使用 shallowRef // 路由懒加载 const routes = [ { path : '/dashboard' , component : () => import ( '@/views/Dashboard.vue' ) } ] // 请求防抖 import { useDebounceFn } from '@vueuse/core' const debouncedSearch = useDebounceFn ( ( keyword ) => api. search (keyword), 300 ) 目录结构 src/ ├── assets/ │ └── styles/ # 全局/共享样式 ├── api/ # API 请求 ├── components/ # 通用组件 ├── composables/ # 组合式函数 ├── router/ # 路由配置 ├── stores/ # Pinia stores ├── types/ # TypeScript 类型 ├── utils/ # 工具函数 ├── views/ # 页面组件 ├── App.vue └── main.ts 样式管理规范 规则 说明 ❌ 禁止在 .vue 中写大段样式 <style> 块不超过 20 行 ❌ 禁止在 .tsx 中写大段内联样式 样式对象/CSS-in-JS 不超过 20 行 ✅ 共享样式抽到 src/assets/styles/ 按模块拆分文件 ✅ 组件内只保留极简样式 Vue: scoped 微调;React: className 引用 src/assets/styles/ ├── variables.scss # 变量(颜色、间距、字号) ├── common.scss # 通用样式 └── [module].scss # 按模块拆分 请求体完整性规范 规则 说明 ❌ 禁止 UI 可选字段未传入 API 用户选择/输入的字段必须全部传入请求体 ✅ 提交函数与表单字段一一对应 用 TypeScript interface 约束请求体 // ❌ UI 有支付方式选择器,但请求体没传 payMethod const payMethod = ref< 'wechat' | 'points' | 'mixed' >( 'wechat' ) async function createOrder ( ) { await api. createOrder ({ items : orderItems. value , addressId : selectedAddress. value . id , // payMethod 忘记传了!支付方式选择 UI 形同虚设 }) } // ✅ 请求体与 UI 表单字段对应 interface CreateOrderRequest { items : OrderItem [] addressId : number payMethod : 'wechat' | 'points' | 'mixed' // 类型约束确保不遗漏 } async function createOrder ( ) { const request : CreateOrderRequest = { items : orderItems. value , addressId : selectedAddress. value . id , payMethod : payMethod. value , // TypeScript 会提示缺少字段 } await api. createOrder (request) } API 错误处理规范 规则 说明 ❌ 禁止静默忽略非成功响应 res.code !== 200 时必须提示用户 ✅ 统一错误提示 非成功响应统一 message.error 提示 ✅ 网络异常也要处理 try/catch 捕获请求异常 // ❌ 只处理成功,非 200 静默忽略 const res = await api. getList (params) if (res. code === 200 ) { list. value = res. data } // ✅ 成功 + 失败都处理 try { const res = await api. getList (params) if (res. code === 200 ) { list. value = res. data } else { message. error (res. message || '加载失败' ) } } catch (e) { message. error ( '网络异常,请稍后重试' ) } React/antd 高频陷阱 本节适用 React + Ant Design 项目(Vue + Element Plus 用户跳过)。Vue 同类问题见 references/frontend-style.md。 陷阱 #1: controlled vs uncontrolled 误用 → 用户操作失效 <Table pagination={{ pageSize: 20 }}> 把 pageSize 写死成 20 → 组件认为是 controlled 属性,用户切换 "10 / 50 / 100" 立刻被覆盖回 20。底部下拉看似可点,实际无效。 属性 controlled 形式 uncontrolled 形式(推荐) antd Table 分页大小 pagination={{ pageSize, onChange }} pagination={{ defaultPageSize: 20 }} antd Tree 展开 expandedKeys + onExpand defaultExpandedKeys 或 defaultExpandAll antd Input 内容 value + onChange defaultValue antd Form 字段 Form.Item + form 实例 initialValue antd Tabs 当前页 activeKey + onChange defaultActiveKey // ❌ controlled 但没 onChange → 用户切换无效 < Table pagination={{ pageSize : 20 , showSizeChanger : true }} /> // ✅ uncontrolled,组件自己管 < Table pagination = {{ defaultPageSize: 20 , showSizeChanger: true , pageSizeOptions: [ 10 , 20 , 50 , 100 ], }} /> // ✅ 或完整 controlled const [pageSize, setPageSize] = useState ( 20 ); < Table pagination = {{ pageSize , onShowSizeChange: ( _ , size ) => setPageSize(size), showSizeChanger: true, }} /> 判定规则 :要么 "传值 + 监听" 全套,要么用 defaultXxx 。 不要只传值不监听 。 陷阱 #2: Tree defaultExpandAll + setState → 编辑后被重置回全展开 <Tree defaultExpandAll> 是 uncontrolled 默认值,每次 setCategories(...) 重新渲染 Tree 都会被重置 → 用户辛苦收起的节点全弹开。 // ❌ 编辑分类名后整棵树被重置成全展开 < Tree treeData={treeData} defaultExpandAll /> // ✅ controlled expandedKeys + 只在初次 mount 时填充 const [expandedKeys, setExpandedKeys] = useState< React . Key []>([]); const initialExpandDoneRef = useRef ( false ); const loadCategories = async ( ) => { const data = ( await getCategoryTree ()). data || []; setCategories (data); if (!initialExpandDoneRef. current && data. length > 0 ) { setExpandedKeys ( collectAllCategoryKeys (data)); initialExpandDoneRef. current = true ; // 关键:之后再 loadCategories 不重置 } }; < Tree treeData = {treeData} expandedKeys = {expandedKeys} onExpand = {setExpandedKeys} /> 陷阱 #3: useEffect 双轮询冲突 → 定时器爆炸 一个 useEffect 用 setTimeout 递归 polling,另一个依赖 status 的 useEffect 又起 setInterval 。两者并存 → 每个周期触发 2 次接口;status 变化让第二个 effect 重建 interval → 残留定时器越积越多。 // ❌ 双 polling 共存 useEffect ( () => { let timerId : NodeJS . Timeout | null = null ; const pollOnce = async ( ) => { const s = ( await getStatus ()). data ; setRefreshState (s); if (s. status === 'RUNNING' ) { timerId = setTimeout (pollOnce, 3000 ); // 第一套 } }; pollOnce (); return () => { if (timerId) clearTimeout (timerId); }; }, []); useEffect ( () => { if (refreshState?. status !== 'RUNNING' ) return ; const timerId = setInterval ( async () => { // 第二套 setRefreshState (( await getStatus ()). data ); }, 3000 ); return () => clearInterval (timerId); }, [refreshState?. status ]); // ✅ 只能一套:初次 fetch 在 mount effect,polling 由 status effect 接管 useEffect ( () => { let cancelled = false ; getStatus (). then ( resp => { if (!cancelled) setRefreshState (resp. data ); }); return () => { cancelled = true ; }; }, []); useEffect ( () => { if (refreshState?. status !== 'RUNNING' ) return ; const timerId = setInterval ( async () => { const s = ( await getStatus ()). data ;
Agent 识别该技能的关键词,点击任意一个即可复制。
该技能未提供触发词。
下载的 .skill 包内含以下字段。
| 字段 | 说明 |
|---|---|
| format | 格式标识(skill/v1) |
| skill_id | 技能唯一 ID |
| name | 技能名称 |
| version | 版本号 |
| description | 技能描述 |
| category | 所属分类(数组) |
| trigger_words | 触发词列表 |
| tags | 标签列表 |
| source | 来源标识 |
| source_url | 来源链接(本页地址) |
| exported_at | 导出时间(每次下载生成) |
| system_prompt | 系统提示词正文 |
| model_config | 模型参数:provider / model / temperature / max_tokens / top_p |
| examples | 示例 |
| install_guide | 各平台导入说明(Coze / Dify / Claude / 自定义框架) |