CodeBuddy Code CLI 实战:打造符合 Vue 3 规范的现代化待办应用

在 Vue 3 生态成熟的当下,“符合规范” 已不再是可选要求 —— 它直接影响项目的可维护性、性能和团队协作效率。但对多数开发者而言,手动配置 Vue 3 规范(如 Composition API 最佳实践、Pinia 状态管理、ESLint 规则)既耗时又容易遗漏。而 CodeBuddy Code CLI(以下简称 Code CLI)能一键生成符合 Vue 3 规范的项目骨架,再配合实战开发,能让我们快速掌握 “规范落地” 的核心逻辑。本文就以现代化待办应用为例,带你从项目初始化到功能上线,全程贴合 Vue 3 官方推荐规范。

第一步:用 Code CLI 初始化符合 Vue 3 规范的项目

Code CLI 的核心优势之一是 “内置规范配置”,初始化时只需选择正确选项,就能规避 “手动配置 ESLint、TypeScript 兼容” 等麻烦。这一步是后续规范开发的基础,务必仔细选择配置项。

1. 环境检查(确保兼容 Vue 3)

首先确认本地环境满足 Vue 3 要求,打开终端执行以下命令:

bash

# 检查 Node.js 版本(Vue 3 要求 ≥14.18.0)
node -v
# 检查 Code CLI 版本(确保是最新版,支持 Vue 3 规范)
codebuddy -v

若 Node.js 版本过低,需前往 Node.js 官网 升级;若 Code CLI 不是最新版,执行 npm update -g @codebuddy/code-cli 更新。

2. 执行初始化命令,锁定 Vue 3 规范

在目标文件夹下打开终端,执行初始化命令:

bash

codebuddy init vue

此时终端会弹出配置选项,严格按以下规范选择(每一步都对应 Vue 3 官方推荐):

  • 项目名称:自定义(如 vue3-todo-standard,建议用小写 + 横杠命名,符合前端项目命名规范)
  • Vue 版本:选择 Vue 3(必须,避免使用 Vue 2 兼容模式)
  • 是否使用 TypeScript:选择 Yes(Vue 3 对 TS 支持更完善,强类型能减少运行时错误,符合现代化规范)
  • 状态管理工具:选择 Pinia(Vue 3 官方推荐替代 Vuex,API 更简洁,支持 Composition API)
  • 包管理器:选择 pnpm(比 npm 更快,且能避免依赖冲突,Vue 生态推荐)
  • 代码规范工具:勾选 ESLint + Prettier(ESLint 检查语法错误,Prettier 统一代码格式,双工具配合符合 Vue 3 代码规范)
  • 是否添加路由:选择 Yes(后续扩展 “任务分类页面” 需用到 Vue Router,提前集成符合项目扩展规范)

3. 验证规范配置是否生效

初始化完成后,进入项目目录并启动开发服务:

bash

cd vue3-todo-standard
pnpm dev

启动成功后,做两个关键验证:

  1. 查看项目目录:是否自动生成 store(Pinia 状态管理目录)、router(路由目录),且组件内默认使用 <script setup lang="ts">(符合 Vue 3 Script Setup 规范)。
  2. 故意写一段不规范代码(如在组件内用 var 声明变量),观察终端是否报错(ESLint 应自动检测并提示错误,说明规范配置生效)。

第二步:梳理 Vue 3 规范下的项目结构

符合 Vue 3 规范的项目结构,核心是 “职责单一、目录可扩展”。相比 Vue 2,Vue 3 项目更强调 “状态管理(Pinia)、路由(Vue Router)、组件” 的分离,同时 Script Setup 简化了组件内代码组织。以下是本次实战的关键目录说明(重点关注规范要求):

目录 / 文件 作用 规范要点
src/store Pinia 状态管理目录 每个模块单独一个文件(如 todoStore.ts),避免单文件冗余,符合模块化规范
src/router/index.ts Vue Router 配置文件 路由定义需用 createRouter 函数,路径命名用 kebab-case(如 /todo-list
src/components 自定义组件目录 组件名用 PascalCase(如 TodoInput.vue),且单文件组件仅包含单一功能
src/views 页面级组件目录 与路由一一对应(如 TodoHome.vue 对应 / 路由),区别于通用组件
src/components/Base 基础 UI 组件目录(如按钮、输入框) 前缀统一用 Base(如 BaseButton.vue),符合组件命名规范
src/types TypeScript 类型定义目录 公共类型(如 Todo.ts)集中管理,避免组件内重复定义,符合 TS 规范

本次实战先聚焦核心功能,暂不拆分过多组件,重点先把 “状态管理、路由、组件语法” 的规范落地。

第三步:按 Vue 3 规范开发核心功能(附代码与规范解读)

待办应用的核心功能仍为 “增删改查 + 状态持久化”,但本次开发全程贴合 Vue 3 规范:用 Pinia 管理全局状态(替代组件间传值)、用 Script Setup + TypeScript 写组件、用 Vue Router 实现页面跳转、用 ESLint 保障代码格式。每一步代码都会标注 “规范要点”,帮你理解 “为什么这么写符合 Vue 3 规范”。

1. 第一步:定义 TypeScript 类型(符合强类型规范)

Vue 3 推荐用 TypeScript 定义所有数据类型,避免 “any 类型滥用”。先在 src/types/Todo.ts 中定义待办任务的类型:

typescript

// src/types/Todo.ts
export interface Todo {
  id: string; // 用 string 类型(UUID),避免数字 ID 溢出,符合数据安全规范
  text: string; // 任务内容
  done: boolean; // 完成状态
  createTime: number; // 创建时间(时间戳),便于排序
}

规范要点:公共类型必须集中管理,且用 interface 而非 typeinterface 支持扩展,更适合定义数据结构,符合 Vue 3 + TS 规范)。

2. 第二步:用 Pinia 管理全局状态(符合状态管理规范)

Vue 3 官方推荐用 Pinia 替代 Vuex,核心原因是 Pinia 支持 Composition API、语法更简洁,且无需嵌套模块。本次待办任务的状态(任务列表、增删方法)全部放在 Pinia 中管理。

(1)创建 Pinia Store

在 src/store/todoStore.ts 中创建状态管理模块:

typescript

// src/store/todoStore.ts
import { defineStore } from 'pinia';
import { Todo } from '@/types/Todo';
import { v4 as uuidv4 } from 'uuid'; // 生成唯一 ID,需先安装:pnpm add uuid @types/uuid

// 用 defineStore 定义 Store,第一个参数为唯一 ID(必须与文件名对应,符合命名规范)
export const useTodoStore = defineStore('todo', {
  // 状态:类似 Vue 2 的 data
  state: (): { todoList: Todo[] } => ({
    todoList: [],
  }),

  // 计算属性:类似 Vue 2 的 computed,缓存结果
  getters: {
    // 未完成任务数量(符合“计算属性只做数据转换,不修改状态”规范)
    undoneCount: (state) => state.todoList.filter(todo => !todo.done).length,
    // 按创建时间排序的任务列表(新任务在前)
    sortedTodoList: (state) => 
      [...state.todoList].sort((a, b) => b.createTime - a.createTime),
  },

  // 方法:类似 Vue 2 的 methods,用于修改状态(同步/异步均可)
  actions: {
    // 初始化任务:从本地存储读取(符合“状态初始化统一管理”规范)
    initTodo() {
      const savedTodos = localStorage.getItem('vue3-todo-list');
      if (savedTodos) {
        this.todoList = JSON.parse(savedTodos);
      }
    },

    // 添加任务:接收任务文本,生成完整 Todo 对象(符合“单一职责”规范)
    addTodo(text: string) {
      if (!text.trim()) return; // 空文本不添加,避免无效数据
      const newTodo: Todo = {
        id: uuidv4(), // 唯一 ID
        text: text.trim(),
        done: false,
        createTime: Date.now(),
      };
      this.todoList.push(newTodo);
      this.saveTodo(); // 保存到本地存储
    },

    // 切换任务完成状态:接收 ID,修改对应任务(符合“修改状态通过 actions,不直接操作 state”规范)
    toggleTodoDone(id: string) {
      const todo = this.todoList.find(todo => todo.id === id);
      if (todo) todo.done = !todo.done;
      this.saveTodo();
    },

    // 删除任务:接收 ID,过滤数组(符合“不可变数据”思想,避免直接 splice 修改原数组)
    deleteTodo(id: string) {
      this.todoList = this.todoList.filter(todo => todo.id !== id);
      this.saveTodo();
    },

    // 保存任务到本地存储:单独抽离,避免重复代码(符合“复用性”规范)
    saveTodo() {
      localStorage.setItem('vue3-todo-list', JSON.stringify(this.todoList));
    },
  },
});

规范要点

  • Store 唯一 ID 必须与文件名一致(如 todo 对应 todoStore.ts),便于定位模块。
  • 所有状态修改必须通过 actions 实现,禁止在组件内直接修改 state(符合 “单向数据流” 规范)。
  • 计算属性用 getters,避免在组件内重复写过滤 / 排序逻辑(符合 “逻辑复用” 规范)。
(2)在入口文件挂载 Pinia

打开 src/main.ts,添加 Pinia 挂载代码(确保全局可访问):

typescript

// src/main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia'; // 引入 Pinia
import router from './router'; // 引入路由
import App from './App.vue';
import './style.css';

const app = createApp(App);
app.use(createPinia()); // 挂载 Pinia
app.use(router); // 挂载路由
app.mount('#app');

3. 第三步:配置 Vue Router(符合路由规范)

本次实战需要两个页面:“待办首页”(展示所有任务)、“已完成任务页”(展示已完成任务)。路由配置需符合 Vue Router 4(Vue 3 配套版本)的规范。

(1)创建页面组件

先在 src/views 目录下创建两个页面组件:

  • TodoHome.vue(待办首页):展示所有任务,包含添加 / 删除功能。
  • TodoDone.vue(已完成任务页):仅展示已完成任务,支持删除。

以 TodoHome.vue 为例(核心代码,重点看 Script Setup 规范):

vue

<!-- src/views/TodoHome.vue -->
<template>
  <div class="todo-home">
    <h2>我的待办任务</h2>
    <!-- 引入基础输入组件 -->
    <BaseTodoInput @add-todo="handleAddTodo" />
    <!-- 任务列表 -->
    <div class="todo-list">
      <TodoItem 
        v-for="todo in sortedTodoList" 
        :key="todo.id" 
        :todo="todo" 
        @toggle-done="handleToggleDone" 
        @delete-todo="handleDeleteTodo" 
      />
    </div>
    <!-- 未完成任务统计 -->
    <div class="todo-count">未完成任务:{{ undoneCount }}</div>
  </div>
</template>

<script setup lang="ts">
// 1. 引入依赖(符合“按需引入”规范,避免全局引入冗余)
import { useTodoStore } from '@/store/todoStore';
import BaseTodoInput from '@/components/Base/BaseTodoInput.vue';
import TodoItem from '@/components/TodoItem.vue';
import { onMounted } from 'vue';

// 2. 初始化 Pinia Store(符合“Store 按需使用”规范,不全局注入)
const todoStore = useTodoStore();
// 解构 Store 中的 getters 和 actions(简化代码,符合“便捷性”规范)
const { initTodo, addTodo, toggleTodoDone, deleteTodo, undoneCount, sortedTodoList } = todoStore;

// 3. 生命周期钩子:页面挂载时初始化任务(符合“生命周期统一管理”规范)
onMounted(() => {
  initTodo();
});

// 4. 事件处理:接收子组件事件,调用 Store 方法(符合“组件不直接修改状态,仅触发 actions”规范)
const handleAddTodo = (text: string) => {
  addTodo(text);
};

const handleToggleDone = (id: string) => {
  toggleTodoDone(id);
};

const handleDeleteTodo = (id: string) => {
  deleteTodo(id);
};
</script>

<style scoped>
/* 样式用 scoped,避免污染全局(符合“组件样式隔离”规范) */
.todo-home {
  max-width: 800px;
  margin: 2rem auto;
  padding: 0 1rem;
}
.todo-list {
  margin: 1.5rem 0;
  gap: 0.8rem;
  display: flex;
  flex-direction: column;
}
.todo-count {
  color: #666;
  font-size: 0.9rem;
}
</style>

规范要点

  • 组件内用 <script setup lang="ts">(Vue 3 推荐的组件语法,简化代码,支持 TS)。
  • Store 按需引入,不全局注入(避免不必要的性能开销,符合 “轻量化” 规范)。
  • 组件样式用 scoped(避免样式污染,符合 “组件隔离” 规范)。
  • 子组件事件通过 @事件名 接收,不直接在子组件修改状态(符合 “单向数据流” 规范)。
(2)配置路由规则

打开 src/router/index.ts,添加路由配置(符合 Vue Router 4 规范):

typescript

// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import TodoHome from '@/views/TodoHome.vue';
import TodoDone from '@/views/TodoDone.vue';

// 路由规则:路径用 kebab-case,组件用 PascalCase(符合命名规范)
const routes = [
  {
    path: '/',
    name: 'TodoHome',
    component: TodoHome,
    meta: { title: '待办首页' } // 页面标题,便于后续统一设置
  },
  {
    path: '/done',
    name: 'TodoDone',
    component: TodoDone,
    meta: { title: '已完成任务' }
  }
];

// 创建路由实例:用 createWebHistory(HTML5 历史模式,无 # 号,符合现代化 URL 规范)
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
});

// 全局导航守卫:设置页面标题(符合“路由守卫统一处理公共逻辑”规范)
router.beforeEach((to) => {
  if (to.meta.title) {
    document.title = to.meta.title as string;
  }
});

export default router;

规范要点

  • 路由路径用 kebab-case(如 /done,而非 /todoDone),符合 URL 命名规范。
  • 使用 createWebHistory(历史模式),避免 hash 模式的 # 号,符合现代化应用 URL 规范。
  • 用全局导航守卫统一设置页面标题,避免在每个组件内重复写 document.title(符合 “公共逻辑集中管理” 规范)。

4. 第四步:开发通用组件(符合组件化规范)

Vue 3 强调 “组件拆分粒度适中”,通用组件(如输入框、任务项)需单独抽离,便于复用和维护。以下是核心通用组件的实现(以 BaseTodoInput.vue 为例):

vue

<!-- src/components/Base/BaseTodoInput.vue -->
<template>
  <div class="base-todo-input">
    <input
      type="text"
      v-model.trim="inputValue"
      placeholder="请输入任务内容..."
      @keyup.enter="handleSubmit"
      :placeholder="placeholder"
      class="input"
    />
    <button @click="handleSubmit" class="btn">添加</button>
  </div>
</template>

<script setup lang="ts">
// 1. 定义 props(用 defineProps,符合 Vue 3 组件通信规范)
const props = defineProps<{
  placeholder?: string; // 可选 props,有默认值
}>();

// 2. 定义 emits(用 defineEmits,明确事件类型,符合 TS 规范)
const emit = defineEmits<{
  (e: 'add-todo', text: string): void; // 事件名 + 参数类型
}>();

// 3. 响应式变量(用 ref,符合 Vue 3 响应式 API 规范)
const inputValue = ref('');

// 4. 事件处理:验证通过后触发事件(符合“子组件不处理业务逻辑,仅传递数据”规范)
const handleSubmit = () => {
  if (!inputValue.value) return; // 空值不提交
  emit('add-todo', inputValue.value); // 触发事件,传递数据
  inputValue.value = ''; // 清空输入框
};

// 5. 设置 props 默认值(符合“props 默认值统一管理”规范)
props.placeholder ??= '请输入任务内容...';
</script>

<style scoped>
.base-todo-input {
  display: flex;
  gap: 0.5rem;
}
.input {
  flex: 1;
  padding: 0.8rem;
  border: 1px solid #e5e7eb;
  border-radius: 0.4rem;
  font-size: 1rem;
}
.btn {
  padding: 0 1.2rem;
  background-color: #2563eb;
  color: white;
  border: none;
  border-radius: 0.4rem;
  cursor: pointer;
}
.btn:hover {
  background-color: #1d4ed8;
}
</style>

规范要点

  • 用 defineProps 和 defineEmits 替代 Vue 2 的 props 和 $emit(Vue 3 推荐语法,支持 TS 类型检查)。
  • 通用组件(Base 前缀)仅负责 UI 和数据传递,不处理业务逻辑(符合 “单一职责” 规范)。
  • Props 默认值用 ??= 设置,避免在 defineProps 内写默认值(更灵活,符合 TS 规范)。

第四步:Vue 3 规范落地的关键检查(避坑指南)

开发完成后,需对照 Vue 3 规范做一次 “全面检查”,避免遗漏关键细节。以下是本次实战的核心检查项,可直接复用:

规范类别 检查项 是否符合规范
组件语法 所有组件是否使用 <script setup lang="ts"> □ 是 □ 否
状态管理 是否通过 Pinia actions 修改状态,无组件内直接修改 state? □ 是 □ 否
路由规范 路径是否用 kebab-case,是否使用 createWebHistory 模式? □ 是 □ 否
组件通信 是否用 defineProps/defineEmits,无 \(parent/\)children 通信? □ 是 □ 否
代码格式 ESLint + Prettier 是否无报错,代码缩进 / 命名统一? □ 是 □ 否
类型安全 是否无 any 类型,所有数据都有明确 TS 类型定义? □ 是 □ 否
样式隔离 组件样式是否用 scoped,无全局样式污染? □ 是 □ 否

若存在 “不符合” 项,需针对性修改:

  • 若组件用 Options API:重构为 Script Setup(参考 TodoHome.vue 语法)。
  • 若有 any 类型:补充 TS 类型定义(如 Todo 接口)。
  • 若路由用 hash 模式:修改为 createWebHistory(参考 router/index.ts)。

第五步:打包优化与部署(符合现代化应用规范)

Vue 3 项目的打包优化有明确规范,核心是 “减小包体积、提升加载速度”。Code CLI 生成的项目已内置基础优化,但仍需补充关键配置。

1. 打包优化配置

打开 vite.config.ts(Code CLI 生成的 Vite 配置文件),添加以下优化项(符合 Vite + Vue 3 打包规范):

typescript

// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';

export default defineConfig({
  plugins: [vue()],
  // 路径别名:简化 @ 开头的路径引用(符合“路径统一”规范)
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
  // 打包优化:减小包体积(符合“性能优化”规范)
  build: {
    sourcemap: false, // 关闭 sourcemap(生产环境无需调试,减小包体积)
    rollupOptions: {
      output: {
        // 拆分公共依赖(如 Pinia、Vue Router),避免重复打包(符合“代码分割”规范)
        manualChunks: {
          pinia: ['pinia'],
          router: ['vue-router'],
        },
      },
    },
  },
  // 开发环境优化:提升热更新速度(符合“开发效率”规范)
  server: {
    hmr: true, // 开启热模块替换
    open: true, // 启动后自动打开浏览器
  },
});

2. 执行打包命令

在终端执行打包命令,生成优化后的静态文件:

bash

pnpm run build

打包完成后,dist 文件夹体积会比未优化前减小 30% 左右(视依赖情况而定)。

3. 部署到 Vercel(符合现代化部署规范)

Vercel 对 Vue 3 项目支持友好,且能自动识别 Vite 配置,步骤如下:

  1. 把项目推送到 GitHub 仓库(确保包含 package.json 和 vite.config.ts)。
  2. 打开 Vercel 官网,用 GitHub 账号登录。
  3. 点击 “New Project”,选择刚推送的仓库,点击 “Deploy”。
  4. 等待部署完成,Vercel 会生成一个在线链接(如 https://vue3-todo-standard.vercel.app/),可直接访问。

规范要点:部署时无需手动修改配置,Vercel 会自动读取 package.json 中的 build 命令(符合 “自动化部署” 规范)。

总结:Vue 3 规范的核心价值与扩展方向

通过本次实战,你会发现:Vue 3 规范并非 “束缚”,而是 “提升效率的工具”——Pinia 简化了状态管理,Script Setup 减少了冗余代码,TypeScript 提前规避了错误,Code CLI 则让规范落地更轻松。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐