808 lines
19 KiB
Markdown
808 lines
19 KiB
Markdown
# Vue 新手完全指南 - 基于你的待办事项项目
|
||
|
||
## 📚 目录
|
||
1. [Vue 基础概念](#vue-基础概念)
|
||
2. [项目结构理解](#项目结构理解)
|
||
3. [Composition API 详解](#composition-api-详解)
|
||
4. [响应式数据系统](#响应式数据系统)
|
||
5. [模板语法与指令](#模板语法与指令)
|
||
6. [事件处理](#事件处理)
|
||
7. [计算属性](#计算属性)
|
||
8. [CSS 与样式处理](#css-与样式处理)
|
||
9. [组件开发模式](#组件开发模式)
|
||
10. [构建与部署](#构建与部署)
|
||
|
||
---
|
||
|
||
## Vue 基础概念
|
||
|
||
### 🎯 什么是Vue?
|
||
Vue.js 是一个**渐进式JavaScript框架**,用于构建用户界面。你的项目使用的是Vue 3,这是目前最新的版本。
|
||
|
||
**核心特点:**
|
||
- **响应式**:数据变化时,界面自动更新
|
||
- **组件化**:将复杂界面拆分成小组件
|
||
- **声明式**:描述"要什么结果",而不是"怎么做"
|
||
|
||
### 🏗️ Vue应用的工作原理
|
||
```javascript
|
||
// 你的项目入口文件: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`是一个**单文件组件**,包含三个部分:
|
||
|
||
```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**,这是推荐的新写法:
|
||
|
||
```javascript
|
||
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
|
||
// 这里写你的代码逻辑
|
||
</script>
|
||
```
|
||
|
||
**为什么用`<script setup>`?**
|
||
- ✅ 代码更简洁
|
||
- ✅ 性能更好
|
||
- ✅ TypeScript支持更好
|
||
- ✅ 这是Vue 3推荐写法
|
||
|
||
### 📦 导入Vue功能
|
||
```javascript
|
||
// 你的项目第2行
|
||
import { ref, computed } from 'vue'
|
||
```
|
||
|
||
**解释:**
|
||
- `ref` - 创建响应式数据
|
||
- `computed` - 创建计算属性
|
||
- 这些是Vue提供的功能函数
|
||
|
||
---
|
||
|
||
## 响应式数据系统
|
||
|
||
### 🎪 什么是响应式?
|
||
**响应式** = 数据变化时,页面自动更新
|
||
|
||
### 📝 ref() - 基本响应式数据
|
||
```javascript
|
||
// 你的项目代码示例
|
||
const newTodo = ref('') // 字符串
|
||
const todos = ref([...]) // 数组
|
||
const filter = ref('all') // 字符串
|
||
|
||
// 使用方式
|
||
console.log(newTodo.value) // 读取值
|
||
newTodo.value = '新的待办事项' // 修改值
|
||
```
|
||
|
||
**新手重点:**
|
||
- 📌 `ref()` 创建响应式数据
|
||
- 📌 在JavaScript中用 `.value` 访问
|
||
- 📌 在模板中直接用变量名(自动解包)
|
||
|
||
### 🔄 响应式数组操作
|
||
```javascript
|
||
// 你的项目中的数组操作
|
||
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() - 计算属性
|
||
```javascript
|
||
// 你的项目计算属性示例
|
||
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
|
||
})
|
||
```
|
||
|
||
**计算属性特点:**
|
||
- ✨ 基于其他数据计算得出
|
||
- ✨ 依赖数据变化时自动重新计算
|
||
- ✨ 有缓存,性能更好
|
||
- ✨ 在模板中像普通数据一样使用
|
||
|
||
**什么时候用计算属性?**
|
||
- 📊 数据统计(如:未完成数量)
|
||
- 🔍 数据过滤(如:按条件筛选)
|
||
- 🔄 数据转换(如:格式化显示)
|
||
|
||
---
|
||
|
||
## 模板语法与指令
|
||
|
||
### 📝 插值表达式 `{{}}`
|
||
```html
|
||
<!-- 你的项目中的插值示例 -->
|
||
<h1>Vue 待办事项</h1> <!-- 静态文本 -->
|
||
<span>还有 {{ remainingCount }} 项未完成</span> <!-- 动态数据 -->
|
||
<span>全部 ({{ todos.length }})</span> <!-- 表达式计算 -->
|
||
```
|
||
|
||
**新手要点:**
|
||
- 🔤 `{{ }}` 用于显示数据
|
||
- 🔤 可以是变量、表达式、方法调用
|
||
- 🔤 自动转换为字符串显示
|
||
|
||
### 🎛️ v-model - 双向数据绑定
|
||
```html
|
||
<!-- 你的项目第66行 -->
|
||
<input v-model="newTodo" placeholder="添加新的待办事项..." />
|
||
|
||
<!-- 你的项目第110行 -->
|
||
<input type="checkbox" v-model="todo.completed" />
|
||
```
|
||
|
||
**双向绑定的含义:**
|
||
- 📥 输入框内容变化 → 数据自动更新
|
||
- 📤 数据变化 → 输入框内容自动更新
|
||
|
||
**常见用法:**
|
||
```html
|
||
<!-- 文本输入 -->
|
||
<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 - 条件显示
|
||
```html
|
||
<!-- 你的项目中的条件显示 -->
|
||
<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 - 列表渲染
|
||
```html
|
||
<!-- 你的项目第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)
|
||
|
||
**常见用法:**
|
||
```html
|
||
<!-- 遍历数组 -->
|
||
<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 - 动态类绑定
|
||
```html
|
||
<!-- 你的项目中的类绑定示例 -->
|
||
<button :class="{ active: filter === 'all' }">全部</button>
|
||
<li :class="{ completed: todo.completed }" class="todo-item">
|
||
```
|
||
|
||
**类绑定语法:**
|
||
```html
|
||
<!-- 对象语法 -->
|
||
<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 - 点击事件
|
||
```html
|
||
<!-- 你的项目中的点击事件 -->
|
||
<button @click="addTodo">添加</button>
|
||
<button @click="filter = 'all'">全部</button>
|
||
<button @click="removeTodo(todo.id)">删除</button>
|
||
<button @click="clearCompleted">清除已完成</button>
|
||
```
|
||
|
||
**事件处理方式:**
|
||
```html
|
||
<!-- 调用方法 -->
|
||
<button @click="handleClick">点击我</button>
|
||
|
||
<!-- 直接执行代码 -->
|
||
<button @click="count++">计数+1</button>
|
||
|
||
<!-- 传递参数 -->
|
||
<button @click="handleClick(item.id)">删除</button>
|
||
|
||
<!-- 传递事件对象 -->
|
||
<button @click="handleClick($event)">获取事件</button>
|
||
```
|
||
|
||
### ⌨️ @keyup - 键盘事件
|
||
```html
|
||
<!-- 你的项目第67行 -->
|
||
<input @keyup.enter="addTodo" />
|
||
```
|
||
|
||
**键盘事件修饰符:**
|
||
```html
|
||
<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 - 值变化事件
|
||
```html
|
||
<!-- 你的项目第111行 -->
|
||
<input type="checkbox" @change="toggleTodo(todo.id)" />
|
||
```
|
||
|
||
**常见事件类型:**
|
||
- `@click` - 点击
|
||
- `@input` - 输入(实时)
|
||
- `@change` - 值改变(失去焦点时)
|
||
- `@submit` - 表单提交
|
||
- `@focus` - 获得焦点
|
||
- `@blur` - 失去焦点
|
||
|
||
### 🛠️ 方法定义
|
||
```javascript
|
||
// 你的项目中的方法定义
|
||
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 方法的区别
|
||
|
||
**计算属性(推荐):**
|
||
```javascript
|
||
// 你的项目使用的计算属性
|
||
const remainingCount = computed(() => {
|
||
return todos.value.filter(todo => !todo.completed).length
|
||
})
|
||
```
|
||
|
||
**方法写法(不推荐):**
|
||
```javascript
|
||
const getRemainingCount = () => {
|
||
return todos.value.filter(todo => !todo.completed).length
|
||
}
|
||
```
|
||
|
||
**为什么用计算属性?**
|
||
- 🚀 **有缓存** - 依赖不变时不重新计算
|
||
- 🚀 **自动更新** - 依赖变化时自动重算
|
||
- 🚀 **性能更好** - 避免重复计算
|
||
|
||
### 🔍 复杂计算属性示例
|
||
```javascript
|
||
// 你的项目中的过滤计算属性
|
||
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. 当 `filter` 或 `todos` 变化时自动重新计算
|
||
|
||
### 💡 编写计算属性的技巧
|
||
```javascript
|
||
// ✅ 好的计算属性
|
||
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
|
||
```vue
|
||
<style scoped>
|
||
.todo-app {
|
||
max-width: 600px;
|
||
margin: 0 auto;
|
||
}
|
||
</style>
|
||
```
|
||
|
||
**`scoped` 的作用:**
|
||
- 🔒 样式只影响当前组件
|
||
- 🔒 不会污染全局样式
|
||
- 🔒 避免样式冲突
|
||
|
||
### 🎭 CSS类的条件应用
|
||
```html
|
||
<!-- 你的项目中的条件样式 -->
|
||
<button :class="{ active: filter === 'all' }">
|
||
<li :class="{ completed: todo.completed }" class="todo-item">
|
||
```
|
||
|
||
对应的CSS:
|
||
```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变量和主题色
|
||
```css
|
||
/* 你的项目使用的主题色 */
|
||
.new-todo:focus {
|
||
border-color: #42b883; /* Vue绿色 */
|
||
}
|
||
|
||
.add-btn {
|
||
background-color: #42b883;
|
||
}
|
||
|
||
.filters button.active {
|
||
background-color: #42b883;
|
||
}
|
||
```
|
||
|
||
### 📱 响应式设计
|
||
```css
|
||
/* 你的项目中没有用到,但建议添加 */
|
||
@media (max-width: 768px) {
|
||
.todo-app {
|
||
padding: 10px;
|
||
}
|
||
|
||
.input-container {
|
||
flex-direction: column;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 组件开发模式
|
||
|
||
### 🧱 单文件组件的优势
|
||
你的项目使用单文件组件(`.vue`文件):
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 模板 -->
|
||
</template>
|
||
|
||
<script setup>
|
||
<!-- 逻辑 -->
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 样式 */
|
||
</style>
|
||
```
|
||
|
||
**优势:**
|
||
- 📦 **高内聚** - 相关代码在一起
|
||
- 🔧 **易维护** - 修改功能只需改一个文件
|
||
- 🚀 **易复用** - 整个组件可以在其他地方使用
|
||
|
||
### 🔄 组件拆分建议
|
||
你的项目目前是单组件,可以考虑拆分:
|
||
|
||
```javascript
|
||
// 可以拆分成的组件
|
||
components/
|
||
├── TodoApp.vue // 主容器组件
|
||
├── TodoInput.vue // 输入框组件
|
||
├── TodoList.vue // 列表组件
|
||
├── TodoItem.vue // 单个待办项组件
|
||
├── TodoFilter.vue // 过滤器组件
|
||
└── TodoFooter.vue // 底部统计组件
|
||
```
|
||
|
||
**组件拆分原则:**
|
||
- 🎯 单一职责 - 一个组件做一件事
|
||
- 🎯 合理大小 - 不要太大也不要太小
|
||
- 🎯 易于理解 - 功能清晰明确
|
||
|
||
### 📡 组件通信(进阶)
|
||
当你拆分组件后,需要组件间通信:
|
||
|
||
```javascript
|
||
// 父组件传数据给子组件 (Props)
|
||
<TodoItem :todo="todo" @toggle="toggleTodo" />
|
||
|
||
// 子组件触发父组件事件 (Emit)
|
||
const emit = defineEmits(['toggle'])
|
||
emit('toggle', todo.id)
|
||
```
|
||
|
||
---
|
||
|
||
## 构建与部署
|
||
|
||
### ⚡ Vite 构建工具
|
||
你的项目使用 Vite 作为构建工具:
|
||
|
||
```javascript
|
||
// vite.config.js
|
||
export default defineConfig({
|
||
plugins: [
|
||
vue(), // Vue支持
|
||
vueDevTools(), // 开发工具
|
||
],
|
||
resolve: {
|
||
alias: {
|
||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||
},
|
||
},
|
||
})
|
||
```
|
||
|
||
**Vite的优势:**
|
||
- ⚡ 启动速度快
|
||
- ⚡ 热更新快
|
||
- ⚡ 构建速度快
|
||
- ⚡ 支持现代JavaScript特性
|
||
|
||
### 🚀 开发和构建命令
|
||
```bash
|
||
# 开发模式(你正在使用的)
|
||
npm run dev
|
||
|
||
# 构建生产版本
|
||
npm run build
|
||
|
||
# 预览构建结果
|
||
npm run preview
|
||
```
|
||
|
||
### 📦 依赖管理
|
||
```json
|
||
// 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`**
|
||
```javascript
|
||
// ❌ 错误
|
||
const count = ref(0)
|
||
console.log(count) // 输出: RefImpl对象
|
||
count++ // 不会工作
|
||
|
||
// ✅ 正确
|
||
console.log(count.value) // 输出: 0
|
||
count.value++ // 正确的修改方式
|
||
```
|
||
|
||
**2. 直接修改 props**
|
||
```javascript
|
||
// ❌ 错误 - 不要直接修改父组件传来的数据
|
||
props.todo.text = 'new text'
|
||
|
||
// ✅ 正确 - 通过事件通知父组件
|
||
emit('update-todo', { id: props.todo.id, text: 'new text' })
|
||
```
|
||
|
||
**3. 缺少 key 属性**
|
||
```html
|
||
<!-- ❌ 错误 -->
|
||
<li v-for="item in items">{{ item.name }}</li>
|
||
|
||
<!-- ✅ 正确 -->
|
||
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
|
||
```
|
||
|
||
**4. 在计算属性中修改数据**
|
||
```javascript
|
||
// ❌ 错误
|
||
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. 控制台调试**
|
||
```javascript
|
||
// 在方法中添加调试输出
|
||
const addTodo = () => {
|
||
console.log('添加前的todos:', todos.value)
|
||
console.log('输入的内容:', newTodo.value)
|
||
|
||
if (newTodo.value.trim()) {
|
||
// ... 你的逻辑
|
||
}
|
||
|
||
console.log('添加后的todos:', todos.value)
|
||
}
|
||
```
|
||
|
||
**3. 模板调试**
|
||
```html
|
||
<!-- 在模板中显示数据进行调试 -->
|
||
<div>{{ todos }}</div>
|
||
<div>当前筛选: {{ filter }}</div>
|
||
<div>计算属性结果: {{ filteredTodos }}</div>
|
||
```
|
||
|
||
### 📖 推荐学习资源
|
||
|
||
**官方文档:**
|
||
- [Vue 3 官方文档](https://vuejs.org/)
|
||
- [Vue 3 中文文档](https://cn.vuejs.org/)
|
||
|
||
**实用教程:**
|
||
- 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的世界还有很多精彩等你探索!🌟 |