notes/Vue新手完全指南-基于你的项目.md
2025-06-25 19:21:02 +08:00

19 KiB
Raw Blame History

Vue 新手完全指南 - 基于你的待办事项项目

📚 目录

  1. Vue 基础概念
  2. 项目结构理解
  3. Composition API 详解
  4. 响应式数据系统
  5. 模板语法与指令
  6. 事件处理
  7. 计算属性
  8. CSS 与样式处理
  9. 组件开发模式
  10. 构建与部署

Vue 基础概念

🎯 什么是Vue

Vue.js 是一个渐进式JavaScript框架用于构建用户界面。你的项目使用的是Vue 3这是目前最新的版本。

核心特点:

  • 响应式:数据变化时,界面自动更新
  • 组件化:将复杂界面拆分成小组件
  • 声明式:描述"要什么结果",而不是"怎么做"

🏗️ Vue应用的工作原理

// 你的项目入口文件src/main.js
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

解释:

  1. createApp() - 创建Vue应用实例
  2. App - 根组件(你的整个应用)
  3. mount('#app') - 挂载到HTML元素上

项目结构理解

📁 你的项目文件结构

todo-app/
├── src/
│   ├── App.vue          # 主组件(你的核心代码)
│   ├── main.js          # 应用入口
│   └── assets/          # 静态资源
├── public/              # 公共文件
├── package.json         # 项目配置
└── vite.config.js       # 构建工具配置

📄 单文件组件 (.vue文件)

你的App.vue是一个单文件组件,包含三个部分:

<template>
  <!-- HTML模板界面结构 -->
</template>

<script setup>
  // JavaScript逻辑数据和方法
</script>

<style scoped>
  /* CSS样式外观设计 */
</style>

新手要点:

  • <template> = 你看到的页面内容
  • <script setup> = 页面的功能逻辑
  • <style scoped> = 只影响当前组件的样式

Composition API 详解

🔥 什么是 <script setup>

你的项目使用了Vue 3的Composition API,这是推荐的新写法:

<script setup>
import { ref, computed } from 'vue'

// 这里写你的代码逻辑
</script>

为什么用<script setup>

  • 代码更简洁
  • 性能更好
  • TypeScript支持更好
  • 这是Vue 3推荐写法

📦 导入Vue功能

// 你的项目第2行
import { ref, computed } from 'vue'

解释:

  • ref - 创建响应式数据
  • computed - 创建计算属性
  • 这些是Vue提供的功能函数

响应式数据系统

🎪 什么是响应式?

响应式 = 数据变化时,页面自动更新

📝 ref() - 基本响应式数据

// 你的项目代码示例
const newTodo = ref('')                    // 字符串
const todos = ref([...])                   // 数组
const filter = ref('all')                  // 字符串

// 使用方式
console.log(newTodo.value)                 // 读取值
newTodo.value = '新的待办事项'              // 修改值

新手重点:

  • 📌 ref() 创建响应式数据
  • 📌 在JavaScript中用 .value 访问
  • 📌 在模板中直接用变量名(自动解包)

🔄 响应式数组操作

// 你的项目中的数组操作
const todos = ref([
  { id: 1, text: '学习Vue基础语法', completed: false },
  { id: 2, text: '理解响应式数据', completed: true }
])

// 添加项目第32-38行
todos.value.push({
  id: Date.now(),
  text: newTodo.value.trim(),
  completed: false
})

// 删除项目第42-46行
const index = todos.value.findIndex(todo => todo.id === id)
todos.value.splice(index, 1)

// 过滤项目第56行
todos.value = todos.value.filter(todo => !todo.completed)

新手要点:

  • 🎯 数组方法:push(), splice(), filter(), find(), findIndex()
  • 🎯 修改数组后,页面自动更新
  • 🎯 Date.now() 生成唯一ID

🧮 computed() - 计算属性

// 你的项目计算属性示例
const filteredTodos = computed(() => {
  switch (filter.value) {
    case 'active':
      return todos.value.filter(todo => !todo.completed)
    case 'completed':
      return todos.value.filter(todo => todo.completed)
    default:
      return todos.value
  }
})

const remainingCount = computed(() => {
  return todos.value.filter(todo => !todo.completed).length
})

计算属性特点:

  • 基于其他数据计算得出
  • 依赖数据变化时自动重新计算
  • 有缓存,性能更好
  • 在模板中像普通数据一样使用

什么时候用计算属性?

  • 📊 数据统计(如:未完成数量)
  • 🔍 数据过滤(如:按条件筛选)
  • 🔄 数据转换(如:格式化显示)

模板语法与指令

📝 插值表达式 {{}}

<!-- 你的项目中的插值示例 -->
<h1>Vue 待办事项</h1>                          <!-- 静态文本 -->
<span>还有 {{ remainingCount }} 项未完成</span>  <!-- 动态数据 -->
<span>全部 ({{ todos.length }})</span>         <!-- 表达式计算 -->

新手要点:

  • 🔤 {{ }} 用于显示数据
  • 🔤 可以是变量、表达式、方法调用
  • 🔤 自动转换为字符串显示

🎛️ v-model - 双向数据绑定

<!-- 你的项目第66行 -->
<input v-model="newTodo" placeholder="添加新的待办事项..." />

<!-- 你的项目第110行 -->
<input type="checkbox" v-model="todo.completed" />

双向绑定的含义:

  • 📥 输入框内容变化 → 数据自动更新
  • 📤 数据变化 → 输入框内容自动更新

常见用法:

<!-- 文本输入 -->
<input v-model="message" />

<!-- 复选框 -->
<input type="checkbox" v-model="checked" />

<!-- 单选框 -->
<input type="radio" value="A" v-model="picked" />

<!-- 选择框 -->
<select v-model="selected">
  <option value="apple">苹果</option>
</select>

👁️ v-show 和 v-if - 条件显示

<!-- 你的项目中的条件显示 -->
<main v-show="todos.length">                    <!-- 有数据时显示 -->
<button v-show="todos.length > remainingCount"> <!-- 有已完成项时显示 -->
<div v-show="!todos.length">                    <!-- 无数据时显示 -->

v-show vs v-if 的区别:

  • v-show: 通过CSS display 控制显示/隐藏
  • v-if: 真正的条件渲染,元素会被创建/销毁

什么时候用哪个?

  • 频繁切换 → 用 v-show
  • 很少改变 → 用 v-if

🔄 v-for - 列表渲染

<!-- 你的项目第102-107行 -->
<li v-for="todo in filteredTodos" :key="todo.id" class="todo-item">
  <div class="todo-content">
    <input type="checkbox" v-model="todo.completed" />
    <span class="todo-text">{{ todo.text }}</span>
  </div>
  <button @click="removeTodo(todo.id)">删除</button>
</li>

v-for 重点:

  • 📋 遍历数组或对象
  • 🔑 :key 必须提供(性能优化)
  • 🔑 key应该是唯一值如ID

常见用法:

<!-- 遍历数组 -->
<li v-for="item in items" :key="item.id">{{ item.name }}</li>

<!-- 遍历对象 -->
<li v-for="(value, key) in object" :key="key">{{ key }}: {{ value }}</li>

<!-- 遍历数字 -->
<span v-for="n in 10" :key="n">{{ n }}</span>

🎨 :class - 动态类绑定

<!-- 你的项目中的类绑定示例 -->
<button :class="{ active: filter === 'all' }">全部</button>
<li :class="{ completed: todo.completed }" class="todo-item">

类绑定语法:

<!-- 对象语法 -->
<div :class="{ active: isActive, disabled: isDisabled }"></div>

<!-- 数组语法 -->
<div :class="[activeClass, errorClass]"></div>

<!-- 混合使用 -->
<div class="static-class" :class="{ dynamic: isDynamic }"></div>

新手理解:

  • 🎯 { active: filter === 'all' }
    • 如果 filter === 'all' 为真,添加 active
    • 如果为假,不添加

事件处理

🖱️ @click - 点击事件

<!-- 你的项目中的点击事件 -->
<button @click="addTodo">添加</button>
<button @click="filter = 'all'">全部</button>
<button @click="removeTodo(todo.id)">删除</button>
<button @click="clearCompleted">清除已完成</button>

事件处理方式:

<!-- 调用方法 -->
<button @click="handleClick">点击我</button>

<!-- 直接执行代码 -->
<button @click="count++">计数+1</button>

<!-- 传递参数 -->
<button @click="handleClick(item.id)">删除</button>

<!-- 传递事件对象 -->
<button @click="handleClick($event)">获取事件</button>

⌨️ @keyup - 键盘事件

<!-- 你的项目第67行 -->
<input @keyup.enter="addTodo" />

键盘事件修饰符:

<input @keyup.enter="submit">      <!-- 回车键 -->
<input @keyup.esc="cancel">        <!-- ESC键 -->
<input @keyup.space="toggle">      <!-- 空格键 -->
<input @keyup.tab="nextField">     <!-- Tab键 -->
<input @keyup.ctrl.enter="save">   <!-- Ctrl+Enter -->

📤 @change - 值变化事件

<!-- 你的项目第111行 -->
<input type="checkbox" @change="toggleTodo(todo.id)" />

常见事件类型:

  • @click - 点击
  • @input - 输入(实时)
  • @change - 值改变(失去焦点时)
  • @submit - 表单提交
  • @focus - 获得焦点
  • @blur - 失去焦点

🛠️ 方法定义

// 你的项目中的方法定义
const addTodo = () => {
  if (newTodo.value.trim()) {                    // 检查输入不为空
    todos.value.push({                           // 添加到数组
      id: Date.now(),                            // 生成唯一ID
      text: newTodo.value.trim(),                // 去除前后空格
      completed: false                           // 初始状态
    })
    newTodo.value = ''                           // 清空输入框
  }
}

const removeTodo = (id) => {
  const index = todos.value.findIndex(todo => todo.id === id)
  if (index > -1) {
    todos.value.splice(index, 1)               // 从数组中删除
  }
}

const toggleTodo = (id) => {
  const todo = todos.value.find(todo => todo.id === id)
  if (todo) {
    todo.completed = !todo.completed           // 切换完成状态
  }
}

方法编写规范:

  • 使用箭头函数 const method = () => {}
  • 参数验证(检查数据有效性)
  • 防御性编程(检查对象是否存在)
  • 一个方法做一件事

计算属性深入

🧩 计算属性 vs 方法的区别

计算属性(推荐):

// 你的项目使用的计算属性
const remainingCount = computed(() => {
  return todos.value.filter(todo => !todo.completed).length
})

方法写法(不推荐):

const getRemainingCount = () => {
  return todos.value.filter(todo => !todo.completed).length
}

为什么用计算属性?

  • 🚀 有缓存 - 依赖不变时不重新计算
  • 🚀 自动更新 - 依赖变化时自动重算
  • 🚀 性能更好 - 避免重复计算

🔍 复杂计算属性示例

// 你的项目中的过滤计算属性
const filteredTodos = computed(() => {
  switch (filter.value) {
    case 'active':
      return todos.value.filter(todo => !todo.completed)
    case 'completed':  
      return todos.value.filter(todo => todo.completed)
    default:
      return todos.value
  }
})

这个计算属性做了什么?

  1. 监听 filter.value 的变化
  2. 根据筛选条件返回不同的待办列表
  3. filtertodos 变化时自动重新计算

💡 编写计算属性的技巧

// ✅ 好的计算属性
const completedTodos = computed(() => {
  return todos.value.filter(todo => todo.completed)
})

// ✅ 链式计算属性
const completedTodosCount = computed(() => {
  return completedTodos.value.length
})

// ❌ 避免副作用
const badComputed = computed(() => {
  // 不要在计算属性中修改数据
  todos.value.push({...})  // 错误!
  return someValue
})

CSS 与样式处理

🎨 Scoped CSS

<style scoped>
.todo-app {
  max-width: 600px;
  margin: 0 auto;
}
</style>

scoped 的作用:

  • 🔒 样式只影响当前组件
  • 🔒 不会污染全局样式
  • 🔒 避免样式冲突

🎭 CSS类的条件应用

<!-- 你的项目中的条件样式 -->
<button :class="{ active: filter === 'all' }">
<li :class="{ completed: todo.completed }" class="todo-item">

对应的CSS

.filters button.active {
  background-color: #42b883;
  color: white;
}

.todo-item.completed {
  opacity: 0.6;
  background-color: #f8f9fa;
}

.completed .todo-text {
  text-decoration: line-through;
  color: #95a5a6;
}

🌈 CSS变量和主题色

/* 你的项目使用的主题色 */
.new-todo:focus {
  border-color: #42b883;    /* Vue绿色 */
}

.add-btn {
  background-color: #42b883; 
}

.filters button.active {
  background-color: #42b883;
}

📱 响应式设计

/* 你的项目中没有用到,但建议添加 */
@media (max-width: 768px) {
  .todo-app {
    padding: 10px;
  }
  
  .input-container {
    flex-direction: column;
  }
}

组件开发模式

🧱 单文件组件的优势

你的项目使用单文件组件(.vue文件):

<template>
  <!-- 模板 -->
</template>

<script setup>
  <!-- 逻辑 -->
</script>

<style scoped>
  /* 样式 */
</style>

优势:

  • 📦 高内聚 - 相关代码在一起
  • 🔧 易维护 - 修改功能只需改一个文件
  • 🚀 易复用 - 整个组件可以在其他地方使用

🔄 组件拆分建议

你的项目目前是单组件,可以考虑拆分:

// 可以拆分成的组件
components/
├── TodoApp.vue          // 主容器组件
├── TodoInput.vue        // 输入框组件
├── TodoList.vue         // 列表组件
├── TodoItem.vue         // 单个待办项组件
├── TodoFilter.vue       // 过滤器组件
└── TodoFooter.vue       // 底部统计组件

组件拆分原则:

  • 🎯 单一职责 - 一个组件做一件事
  • 🎯 合理大小 - 不要太大也不要太小
  • 🎯 易于理解 - 功能清晰明确

📡 组件通信(进阶)

当你拆分组件后,需要组件间通信:

// 父组件传数据给子组件 (Props)
<TodoItem :todo="todo" @toggle="toggleTodo" />

// 子组件触发父组件事件 (Emit)
const emit = defineEmits(['toggle'])
emit('toggle', todo.id)

构建与部署

Vite 构建工具

你的项目使用 Vite 作为构建工具:

// vite.config.js
export default defineConfig({
  plugins: [
    vue(),                    // Vue支持
    vueDevTools(),           // 开发工具
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    },
  },
})

Vite的优势

  • 启动速度快
  • 热更新快
  • 构建速度快
  • 支持现代JavaScript特性

🚀 开发和构建命令

# 开发模式(你正在使用的)
npm run dev

# 构建生产版本
npm run build

# 预览构建结果
npm run preview

📦 依赖管理

// package.json 中的依赖
{
  "dependencies": {
    "vue": "^3.5.13"           // Vue 3框架
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.2.3",     // Vue插件
    "vite": "^6.2.4",                   // 构建工具
    "vite-plugin-vue-devtools": "^7.7.2" // 开发工具
  }
}

🎓 学习路径和下一步

📚 基于你项目的学习顺序

第1阶段巩固基础你已经在用

  • 响应式数据 (ref)
  • 计算属性 (computed)
  • 事件处理 (@click, @keyup)
  • 条件渲染 (v-show)
  • 列表渲染 (v-for)
  • 双向绑定 (v-model)

第2阶段进阶功能

  • 🔄 组件拆分和复用
  • 🔄 组件通信 (Props & Emit)
  • 🔄 生命周期钩子 (onMounted, onUpdated)
  • 🔄 侦听器 (watch, watchEffect)

第3阶段实用技能

  • 🚀 路由管理 (Vue Router)
  • 🚀 状态管理 (Pinia)
  • 🚀 HTTP请求 (Axios)
  • 🚀 UI组件库 (Element Plus, Ant Design Vue)

💪 练习建议

基于你的项目扩展:

  1. 添加编辑功能 - 双击编辑待办事项
  2. 添加优先级 - 给待办事项分优先级
  3. 添加分类 - 工作、生活、学习分类
  4. 添加截止日期 - 为待办事项设置期限
  5. 数据持久化 - 使用 localStorage 保存数据

🐛 常见新手错误和解决方案

1. 忘记 .value

// ❌ 错误
const count = ref(0)
console.log(count)          // 输出: RefImpl对象
count++                     // 不会工作

// ✅ 正确  
console.log(count.value)    // 输出: 0
count.value++               // 正确的修改方式

2. 直接修改 props

// ❌ 错误 - 不要直接修改父组件传来的数据
props.todo.text = 'new text'

// ✅ 正确 - 通过事件通知父组件
emit('update-todo', { id: props.todo.id, text: 'new text' })

3. 缺少 key 属性

<!-- ❌ 错误 -->
<li v-for="item in items">{{ item.name }}</li>

<!-- ✅ 正确 -->
<li v-for="item in items" :key="item.id">{{ item.name }}</li>

4. 在计算属性中修改数据

// ❌ 错误
const processedData = computed(() => {
  originalData.value.push(newItem)  // 不要在计算属性中修改数据
  return originalData.value
})

// ✅ 正确
const processedData = computed(() => {
  return originalData.value.map(item => ({ ...item, processed: true }))
})

🛠️ 调试技巧

1. 使用 Vue DevTools 在浏览器中安装 Vue DevTools 扩展,可以:

  • 查看组件树
  • 检查响应式数据
  • 追踪事件

2. 控制台调试

// 在方法中添加调试输出
const addTodo = () => {
  console.log('添加前的todos:', todos.value)
  console.log('输入的内容:', newTodo.value)
  
  if (newTodo.value.trim()) {
    // ... 你的逻辑
  }
  
  console.log('添加后的todos:', todos.value)
}

3. 模板调试

<!-- 在模板中显示数据进行调试 -->
<div>{{ todos }}</div>
<div>当前筛选: {{ filter }}</div>
<div>计算属性结果: {{ filteredTodos }}</div>

📖 推荐学习资源

官方文档:

实用教程:

  • Vue 3 快速上手
  • Composition API 深入理解
  • Vue 生态系统指南

练习项目建议:

  1. 完善当前的待办事项应用
  2. 制作一个简单的计算器
  3. 开发一个天气查询应用
  4. 创建一个个人博客系统

🎉 总结

恭喜你你的待办事项项目已经使用了Vue 3的核心特性

你已经掌握的:

  • Composition API (<script setup>)
  • 响应式数据 (ref)
  • 计算属性 (computed)
  • 事件处理 (@click, @keyup)
  • 模板语法 (v-model, v-for, v-show)
  • 动态类绑定 (:class)

🚀 你的代码质量很高:

  • 使用了现代的Vue 3语法
  • 数据流清晰合理
  • 用户体验良好
  • 代码结构规范

📈 继续提升的方向:

  • 组件化开发
  • 更复杂的状态管理
  • 与后端API交互
  • 更丰富的用户界面

继续保持学习的热情Vue.js的世界还有很多精彩等你探索🌟