vue3的入门学习

文章目录

  • 引言
  • [Vue 3 学习笔记](#Vue 3 学习笔记)
  • 第一部分:基础入门
    • [1. Vue 是什么](#1. Vue 是什么)
      • [1.1 关键词拆解](#1.1 关键词拆解)
      • [1.2 Vue 的生态圈](#1.2 Vue 的生态圈)
      • [1.3 Vue 的核心思想:数据驱动视图](#1.3 Vue 的核心思想:数据驱动视图)
    • [2. 环境准备](#2. 环境准备)
      • [2.1 学习阶段:CDN 引入](#2.1 学习阶段:CDN 引入)
      • [2.2 常用 CDN](#2.2 常用 CDN)
      • [2.3 下载到本地](#2.3 下载到本地)
      • [2.4 版本区别](#2.4 版本区别)
      • [2.5 开发工具](#2.5 开发工具)
    • [3. 第一个 Vue 程序](#3. 第一个 Vue 程序)
      • [3.1 三步骤](#3.1 三步骤)
      • [3.2 常见错误](#3.2 常见错误)
    • [4. Vue 的两种 API 风格](#4. Vue 的两种 API 风格)
      • [4.1 选项式 API(Options API)](#4.1 选项式 API(Options API))
      • [4.2 组合式 API(Composition API)](#4.2 组合式 API(Composition API))
      • [4.3 对比](#4.3 对比)
    • [5. 插值表达式](#5. 插值表达式)
      • [5.1 可以写](#5.1 可以写)
      • [5.2 不能写](#5.2 不能写)
      • [5.3 ⚠️ 不能用在属性里](#5.3 ⚠️ 不能用在属性里)
  • 第二部分:核心指令
    • [6. 指令总览](#6. 指令总览)
    • [7. v-for 列表渲染](#7. v-for 列表渲染)
      • [7.1 基本语法](#7.1 基本语法)
      • [7.2 参数](#7.2 参数)
      • [7.3 遍历对象](#7.3 遍历对象)
      • [7.4 ⚠️ key 的重要性](#7.4 ⚠️ key 的重要性)
      • [7.5 实战](#7.5 实战)
    • [8. v-bind 属性绑定](#8. v-bind 属性绑定)
      • [8.1 基本语法](#8.1 基本语法)
      • [8.2 常见用法](#8.2 常见用法)
      • [8.3 ⚠️ 注意](#8.3 ⚠️ 注意)
    • [9. v-if / v-else-if / v-else](#9. v-if / v-else-if / v-else)
      • [9.1 基本语法](#9.1 基本语法)
      • [9.2 实战](#9.2 实战)
      • [9.3 原理](#9.3 原理)
      • [9.4 ⚠️ 规则](#9.4 ⚠️ 规则)
    • [10. v-show 显示隐藏](#10. v-show 显示隐藏)
      • [10.1 基本语法](#10.1 基本语法)
      • [10.2 原理](#10.2 原理)
      • [10.3 适用场景](#10.3 适用场景)
    • [11. v-model 双向绑定](#11. v-model 双向绑定)
      • [11.1 基本语法](#11.1 基本语法)
      • [11.2 作用](#11.2 作用)
      • [11.3 实战](#11.3 实战)
      • [11.4 修饰符](#11.4 修饰符)
      • [11.5 ⚠️ 注意](#11.5 ⚠️ 注意)
    • [12. v-on 事件绑定](#12. v-on 事件绑定)
      • [12.1 基本语法](#12.1 基本语法)
      • [12.2 实战](#12.2 实战)
      • [12.3 传参](#12.3 传参)
      • [12.4 常见事件](#12.4 常见事件)
      • [12.5 修饰符](#12.5 修饰符)
      • [12.6 实战:阻止冒泡](#12.6 实战:阻止冒泡)
    • [13. v-if 与 v-show 深度对比](#13. v-if 与 v-show 深度对比)
  • 第三部分:响应式核心
    • [14. 什么是响应式](#14. 什么是响应式)
    • [15. ref 与 reactive](#15. ref 与 reactive)
      • [15.1 ref:基本类型](#15.1 ref:基本类型)
      • [15.2 reactive:对象类型](#15.2 reactive:对象类型)
      • [15.3 对比](#15.3 对比)
      • [15.4 推荐用法](#15.4 推荐用法)
      • [15.5 解构保持响应式](#15.5 解构保持响应式)
    • [16. computed 计算属性](#16. computed 计算属性)
      • [16.1 作用](#16.1 作用)
      • [16.2 基本用法](#16.2 基本用法)
      • [16.3 getter / setter](#16.3 getter / setter)
      • [16.4 实战:过滤列表](#16.4 实战:过滤列表)
      • [16.5 ⚠️ 注意](#16.5 ⚠️ 注意)
    • [17. watch 侦听器](#17. watch 侦听器)
      • [17.1 作用](#17.1 作用)
      • [17.2 基本用法](#17.2 基本用法)
      • [17.3 监听多个](#17.3 监听多个)
      • [17.4 监听对象](#17.4 监听对象)
      • [17.5 立即执行](#17.5 立即执行)
      • [17.6 实战:搜索防抖](#17.6 实战:搜索防抖)
    • [18. computed vs watch vs methods](#18. computed vs watch vs methods)
  • 第四部分:生命周期
    • [19. 生命周期钩子](#19. 生命周期钩子)
    • [20. 组合式 API 的生命周期](#20. 组合式 API 的生命周期)
      • [20.1 对应关系](#20.1 对应关系)
      • [20.2 实战](#20.2 实战)
  • 第五部分:组件化开发
    • [21. 组件是什么](#21. 组件是什么)
    • [22. 注册组件](#22. 注册组件)
      • [22.1 全局注册](#22.1 全局注册)
      • [22.2 局部注册](#22.2 局部注册)
      • [22.3 `<script setup>`(推荐)](#22.3 <script setup>(推荐))
    • [23. props 父传子](#23. props 父传子)
      • [23.1 子组件](#23.1 子组件)
      • [23.2 父组件](#23.2 父组件)
      • [23.3 ⚠️ 单向数据流](#23.3 ⚠️ 单向数据流)
    • [24. emit 子传父](#24. emit 子传父)
      • [24.1 子组件](#24.1 子组件)
      • [24.2 父组件](#24.2 父组件)
    • [25. slot 插槽](#25. slot 插槽)
      • [25.1 默认插槽](#25.1 默认插槽)
      • [25.2 具名插槽](#25.2 具名插槽)
      • [25.3 作用域插槽](#25.3 作用域插槽)
    • [26. script setup 语法糖](#26. script setup 语法糖)
      • [26.1 基本结构](#26.1 基本结构)
      • [26.2 特点](#26.2 特点)
      • [26.3 对比](#26.3 对比)
  • 第六部分:工程化
    • [27. Vite 项目搭建](#27. Vite 项目搭建)
      • [27.1 创建项目](#27.1 创建项目)
      • [27.2 目录结构](#27.2 目录结构)
    • [28. 项目结构](#28. 项目结构)
    • [29. 单文件组件 .vue](#29. 单文件组件 .vue)
      • [29.1 三个块](#29.1 三个块)
  • 第七部分:生态
    • [30. Vue Router 路由](#30. Vue Router 路由)
      • [30.1 安装](#30.1 安装)
      • [30.2 定义路由](#30.2 定义路由)
      • [30.3 挂载](#30.3 挂载)
      • [30.4 使用](#30.4 使用)
      • [30.5 传参](#30.5 传参)
      • [30.6 编程式导航](#30.6 编程式导航)
    • [31. Pinia 状态管理](#31. Pinia 状态管理)
      • [31.1 安装](#31.1 安装)
      • [31.2 定义 store](#31.2 定义 store)
      • [31.3 使用](#31.3 使用)
      • [31.4 挂载](#31.4 挂载)
      • [31.5 对比 Vuex](#31.5 对比 Vuex)
    • [32. Axios 网络请求](#32. Axios 网络请求)
      • [32.1 安装](#32.1 安装)
      • [32.2 基本用法](#32.2 基本用法)
      • [32.3 封装](#32.3 封装)
      • [32.4 实战](#32.4 实战)
  • 第八部分:实战与调试
    • [33. Tlias 员工管理实战](#33. Tlias 员工管理实战)
      • [33.1 需求](#33.1 需求)
      • [33.2 核心数据结构](#33.2 核心数据结构)
      • [33.3 渲染](#33.3 渲染)
      • [33.4 搜索](#33.4 搜索)
      • [33.5 多选](#33.5 多选)
    • [34. Vue DevTools 调试](#34. Vue DevTools 调试)
      • [34.1 安装](#34.1 安装)
      • [34.2 关键设置](#34.2 关键设置)
      • [34.3 验证](#34.3 验证)
      • [34.4 图标灰色排查](#34.4 图标灰色排查)
      • [34.5 面板功能](#34.5 面板功能)
  • 第九部分:避坑与速查
    • [35. 常见错误汇总](#35. 常见错误汇总)
      • [35.1 `Vue is not defined`](#35.1 Vue is not defined)
      • [35.2 页面显示 `{{ message }}`](#35.2 页面显示 {{ message }})
      • [35.3 `Cannot read properties of null`](#35.3 Cannot read properties of null)
      • [35.4 `v-model` 绑定 undefined](#35.4 v-model 绑定 undefined)
      • [35.5 插值表达式用在属性里](#35.5 插值表达式用在属性里)
      • [35.6 v-for 忘写 :key](#35.6 v-for 忘写 :key)
      • [35.7 修改 props 报错](#35.7 修改 props 报错)
      • [35.8 reactive 解构丢失响应式](#35.8 reactive 解构丢失响应式)
      • [35.9 ref 在 JS 里忘了 .value](#35.9 ref 在 JS 里忘了 .value)
      • [35.10 computed 里写异步](#35.10 computed 里写异步)
    • [36. 指令速查表](#36. 指令速查表)
      • 修饰符
      • [组合式 API 速查](#组合式 API 速查)
    • [37. 学习路线](#37. 学习路线)
    • [附录:Vue 2 vs Vue 3](#附录:Vue 2 vs Vue 3)

引言

本文是一份面向零基础初学者的 Vue 3 学习笔记。阅读本文前,你只需了解 HTML、>CSS 和 JavaScript 的基础语法即可,无需任何前端框架经验。

全文按照「基础 → 指令 → 响应式 → 组件 → 工程化 → 生态 → 实战」的顺序循序渐进:>先认识 Vue 是什么、如何搭建环境,再掌握核心指令与响应式原理,随后进入组件化开发与工程化实践,最后通过 Vue Router、Pinia、Axios 等生态工具完成一个完整的员工管理实战项目。

学完本文,你将能够独立使用 Vue 3 + Vite 开发中小型前端项目,并具备排查常见错误、使用 DevTools 调试的能力。建议学习周期为 2-3 周:第一周打好基础与指令,第二周攻克组件与工程化,第三周完成实战与生态整合。

Vue 3 学习笔记

目录

第一部分:基础入门

  1. [Vue 是什么](#Vue 是什么)
  2. 环境准备
  3. [第一个 Vue 程序](#第一个 Vue 程序)
  4. [Vue 的两种 API 风格](#Vue 的两种 API 风格)
  5. 插值表达式

第二部分:核心指令

  1. 指令总览
  2. [v-for 列表渲染](#v-for 列表渲染)
  3. [v-bind 属性绑定](#v-bind 属性绑定)
  4. [v-if / v-else-if / v-else](#v-if / v-else-if / v-else)
  5. [v-show 显示隐藏](#v-show 显示隐藏)
  6. [v-model 双向绑定](#v-model 双向绑定)
  7. [v-on 事件绑定](#v-on 事件绑定)
  8. [v-if 与 v-show 深度对比](#v-if 与 v-show 深度对比)

第三部分:响应式核心

  1. 什么是响应式
  2. [ref 与 reactive](#ref 与 reactive)
  3. [computed 计算属性](#computed 计算属性)
  4. [watch 侦听器](#watch 侦听器)
  5. [computed vs watch vs methods](#computed vs watch vs methods)

第四部分:生命周期

  1. 生命周期钩子
  2. [组合式 API 的生命周期](#组合式 API 的生命周期)

第五部分:组件化开发

  1. 组件是什么
  2. 注册组件
  3. [props 父传子](#props 父传子)
  4. [emit 子传父](#emit 子传父)
  5. [slot 插槽](#slot 插槽)
  6. [script setup 语法糖](#script setup 语法糖)

第六部分:工程化

  1. [Vite 项目搭建](#Vite 项目搭建)
  2. 项目结构
  3. [单文件组件 .vue](#单文件组件 .vue)

第七部分:生态

  1. [Vue Router 路由](#Vue Router 路由)
  2. [Pinia 状态管理](#Pinia 状态管理)
  3. [Axios 网络请求](#Axios 网络请求)

第八部分:实战与调试

  1. [Tlias 员工管理实战](#Tlias 员工管理实战)
  2. [Vue DevTools 调试](#Vue DevTools 调试)

第九部分:避坑与速查

  1. 常见错误汇总
  2. 指令速查表
  3. 学习路线

第一部分:基础入门

1. Vue 是什么

Vue 是一款用于构建用户界面 的渐进式 JavaScript 框架。

官方:https://cn.vuejs.org/

1.1 关键词拆解

关键词 含义
构建用户界面 把数据渲染成页面
渐进式 可以只用一部分,也可以全用
框架 一套有规则的开发方案
JavaScript 基于 JS,不是新语言

1.2 Vue 的生态圈

复制代码
Vue 核心(声明式渲染 + 组件系统)
    ↓
VueRouter(客户端路由)
    ↓
Pinia(状态管理,Vuex 的替代)
    ↓
Vite / Webpack(构建工具)

1.3 Vue 的核心思想:数据驱动视图

传统写法:

js 复制代码
document.getElementById('msg').innerText = 'Hello'

Vue 写法:

js 复制代码
message.value = 'Hello'   // 页面自动更新

你只关心数据,DOM 交给 Vue。


2. 环境准备

2.1 学习阶段:CDN 引入

ESM 模块版:

html 复制代码
<script type="module">
    import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
</script>

全局构建版:

html 复制代码
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const { createApp } = Vue
</script>

2.2 常用 CDN

来源 地址
unpkg https://unpkg.com/vue@3/dist/vue.global.js
jsDelivr https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js
npmmirror https://registry.npmmirror.com/vue/3.4.21/files/dist/vue.global.js

2.3 下载到本地

  1. 浏览器打开 https://unpkg.com/vue@3.4.21/dist/vue.global.js
  2. 右键另存为 vue.js
  3. 和 HTML 放同一目录
  4. <script src="./vue.js"></script>

2.4 版本区别

文件 用途 是否含编译器
vue.global.js 全局开发版 ✅
vue.global.prod.js 全局生产版 ✅
vue.esm-browser.js ESM 开发版 ✅
vue.runtime.global.js 仅运行时 ❌

写 {``{ }} 模板必须用含编译器的版本。

2.5 开发工具

工具 用途
VS Code 编辑器
Live Server 插件 本地服务器
Vue DevTools 浏览器调试插件
Vue VSCode Snippets 代码片段

3. 第一个 Vue 程序

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Vue 入门</title>
</head>
<body>
    <div id="app">
        <h1>{{ message }}</h1>
        <h2>{{ count }}</h2>
    </div>

    <script src="./vue.js"></script>
    <script>
        const { createApp } = Vue

        createApp({
            data() {
                return {
                    message: 'Hello World!',
                    count: 100
                }
            }
        }).mount('#app')
    </script>
</body>
</html>

3.1 三步骤

  1. 引入 Vue 模块
  2. 创建应用实例 :createApp({...})
  3. 准备元素 :<div id="app">
  4. 挂载 :.mount('#app')

3.2 常见错误

错误 原因
Vue is not defined 大小写错,应该大写 Vue
页面显示 {``{ message }} 没挂载 / Vue 没加载
Cannot read properties of null #app 元素不存在

4. Vue 的两种 API 风格

4.1 选项式 API(Options API)

js 复制代码
createApp({
    data() {
        return { message: 'Hello', count: 0 }
    },
    methods: {
        add() { this.count++ }
    },
    computed: {
        double() { return this.count * 2 }
    }
}).mount('#app')

4.2 组合式 API(Composition API)

js 复制代码
const { createApp, ref, computed } = Vue

createApp({
    setup() {
        const message = ref('Hello')
        const count = ref(0)

        function add() { count.value++ }

        const double = computed(() => count.value * 2)

        return { message, count, add, double }
    }
}).mount('#app')

4.3 对比

对比项 选项式 组合式
写法 data/methods/computed setup() + ref
this 用 this.xxx 不用 this
适合 简单页面 复杂项目
Vue 3 推荐 --- ✅

大项目用组合式,逻辑可以按功能分组,而不是按选项分散。


5. 插值表达式

html 复制代码
<div id="app">
    <h1>{{ message }}</h1>
    <p>{{ count + 1 }}</p>
    <p>{{ message.toUpperCase() }}</p>
    <p>{{ count > 50 ? '大' : '小' }}</p>
</div>

5.1 可以写

  • 变量
  • 算术运算 a + b
  • 三元 a ? b : c
  • 字符串方法 msg.toUpperCase()
  • 数组/对象访问 arr[0]、obj.name

5.2 不能写

  • if / for 语句
  • 赋值 a = 1
  • 声明 let x = 1

5.3 ⚠️ 不能用在属性里

html 复制代码
<!-- 错误 -->
<img src="{{ image }}">

<!-- 正确 -->
<img :src="image">

第二部分:核心指令

6. 指令总览

指令:HTML 标签上带 v- 前缀的特殊属性。

指令 缩写 作用
v-for --- 列表渲染
v-bind : 属性绑定
v-if --- 条件渲染
v-else-if --- 条件链
v-else --- 条件兜底
v-show --- 显示/隐藏
v-model --- 双向绑定
v-on @ 事件绑定

7. v-for 列表渲染

7.1 基本语法

html 复制代码
<tr v-for="(item, index) in items" :key="item.id">
    <td>{{ index + 1 }}</td>
    <td>{{ item.name }}</td>
</tr>

7.2 参数

参数 含义
items 数组
item 当前元素
index 索引(可省略)
:key 唯一标识

7.3 遍历对象

html 复制代码
<div v-for="(value, key, index) in obj" :key="key">
    {{ index }} - {{ key }} - {{ value }}
</div>

7.4 ⚠️ key 的重要性

推荐: :key="item.id"(唯一 id)

不推荐: :key="index"(会变)

原因: 用 index 做 key,删除中间项时,后面所有项的 key 都变,Vue 会重新渲染,性能差且可能出错。

7.5 实战

html 复制代码
<tbody>
    <tr v-for="(e, index) in empList" :key="e.id">
        <td>{{ index + 1 }}</td>
        <td>{{ e.name }}</td>
        <td>{{ e.gender === 1 ? '男' : '女' }}</td>
        <td><img :src="e.image" class="avatar"></td>
        <td>{{ e.job }}</td>
        <td>{{ e.entrydate }}</td>
        <td>{{ e.updatetime }}</td>
    </tr>
</tbody>

8. v-bind 属性绑定

8.1 基本语法

html 复制代码
<!-- 完整 -->
<img v-bind:src="image">

<!-- 缩写 -->
<img :src="image">

8.2 常见用法

html 复制代码
<!-- 绑定 src -->
<img :src="emp.image">

<!-- 绑定 href -->
<a :href="url">链接</a>

<!-- 绑定 class(对象语法) -->
<div :class="{ active: isActive, 'text-red': isRed }"></div>

<!-- 绑定 class(数组语法) -->
<div :class="['btn', type, isActive ? 'active' : '']"></div>

<!-- 绑定 style -->
<div :style="{ color: textColor, fontSize: size + 'px' }"></div>

<!-- 绑定 disabled -->
<button :disabled="isDisabled">按钮</button>

<!-- 绑定多个属性 -->
<img v-bind="{ src: imgUrl, alt: imgAlt }">

8.3 ⚠️ 注意

动态为标签属性绑定值,不能用插值表达式,必须用 v-bind。

且绑定的数据必须在 data 中定义。

html 复制代码
<!-- 错误 -->
<img src="{{ image }}">

<!-- 正确 -->
<img :src="image">

9. v-if / v-else-if / v-else

9.1 基本语法

html 复制代码
<span v-if="gender === 1">男生</span>
<span v-else-if="gender === 2">女生</span>
<span v-else>未知</span>

9.2 实战

html 复制代码
<td>
    <span v-if="e.job === 1">班主任</span>
    <span v-else-if="e.job === 2">讲师</span>
    <span v-else-if="e.job === 3">学工主管</span>
    <span v-else-if="e.job === 4">教研主管</span>
    <span v-else-if="e.job === 5">咨询师</span>
    <span v-else>其他</span>
</td>

9.3 原理

条件不成立 → 元素不渲染(DOM 里没有)。

9.4 ⚠️ 规则

  • v-else-if 必须在 v-if 之后
  • v-else 必须在最后
  • 三者必须紧挨着,中间不能插入其他元素

10. v-show 显示隐藏

10.1 基本语法

html 复制代码
<span v-show="e.job === 1">班主任</span>
<span v-show="e.job === 2">讲师</span>

10.2 原理

通过 CSS display: none 控制。

F12 里看到:

html 复制代码
<td>
    <span>班主任</span>
    <span style="display: none;">讲师</span>
</td>

10.3 适用场景

频繁切换显示/隐藏。


11. v-model 双向绑定

11.1 基本语法

html 复制代码
<input type="text" v-model="searchForm.name">

11.2 作用

  • 数据变 → 输入框变
  • 输入框变 → 数据变

11.3 实战

html 复制代码
<form class="search-form">
    <label>姓名:</label>
    <input type="text" v-model="searchForm.name" placeholder="请输入姓名">

    <label>性别:</label>
    <select v-model="searchForm.gender">
        <option value=""></option>
        <option value="1">男</option>
        <option value="2">女</option>
    </select>

    <button type="button" @click="search">查询</button>
    <button type="button" @click="clear">清空</button>
</form>
js 复制代码
const { createApp } = Vue
createApp({
    data() {
        return {
            searchForm: {
                name: '',
                gender: '',
                job: ''
            }
        }
    },
    methods: {
        search() {
            console.log(this.searchForm)
        }
    }
}).mount('#app')

11.4 修饰符

修饰符 作用 例子
.trim 去首尾空格 v-model.trim="name"
.number 转数字 v-model.number="age"
.lazy 失焦才同步 v-model.lazy="msg"

11.5 ⚠️ 注意

v-model 绑定的变量必须在 data 中定义。


12. v-on 事件绑定

12.1 基本语法

html 复制代码
<!-- 完整 -->
<button v-on:click="handle">点我</button>

<!-- 缩写 -->
<button @click="handle">点我</button>

12.2 实战

html 复制代码
<button @click="handle">点我</button>
js 复制代码
const { createApp } = Vue
createApp({
    data() {
        return {}
    },
    methods: {
        handle() {
            console.log('试试就试试')
        }
    }
}).mount('#app')

12.3 传参

html 复制代码
<button @click="add(1)">加1</button>
<button @click="del(emp)">删除</button>
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>测试</title>
</head>
<body>
<div id="app">
    <p>{{ count }}</p>
    <button @click="add(1)">加1</button>
</div>

<script src="./vue.js"></script>
<script>
    const { createApp } = Vue

    createApp({
        data() {
            return {
                count: 0
            }
        },
        methods: {
            add(n) {
                this.count += n
            },
            del(emp) {
                console.log('删除', emp)
            }
        }
    }).mount('#app')
</script>
</body>
</html>

12.4 常见事件

事件 触发
@click 点击
@dblclick 双击
@input 输入
@change 值改变
@submit 表单提交
@keyup.enter 回车
@keyup.esc ESC
@mouseover 鼠标移入
@mouseleave 鼠标移出

12.5 修饰符

修饰符 作用 例子
.stop 阻止冒泡 @click.stop
.prevent 阻止默认 @submit.prevent
.self 只点自己 @click.self
.once 只一次 @click.once
.enter 回车 @keyup.enter

12.6 实战:阻止冒泡

html 复制代码
<tr @click="selectRow(emp)">
    <td>
        <button @click.stop="edit(emp)">编辑</button>
        <button @click.stop="remove(emp)">删除</button>
    </td>
</tr>

13. v-if 与 v-show 深度对比

对比项 v-if v-show
原理 不渲染 / 销毁重建 display: none
DOM 有 / 没有 一直有
初始开销 高 低
切换开销 高 低
场景 不频繁切换 频繁切换
支持 v-else ✅ ❌

口诀:频繁切换用 v-show,条件稳定用 v-if。


第三部分:响应式核心

14. 什么是响应式

数据变 → 页面自动变。

Vue 3 用 Proxy 实现响应式:

js 复制代码
const obj = reactive({ count: 0 })
obj.count++   // 页面自动更新

对比 Vue 2: Vue 2 用 Object.defineProperty,无法检测新增属性、数组索引赋值。Vue 3 的 Proxy 解决了这些问题。


15. ref 与 reactive

15.1 ref:基本类型

js 复制代码
const count = ref(0)
const msg = ref('Hello')

console.log(count.value)   // 0
count.value++              // 修改

模板里不用 .value:

html 复制代码
<p>{{ count }}</p>

JS 里必须 .value:

js 复制代码
count.value = 10

15.2 reactive:对象类型

js 复制代码
const state = reactive({
    name: '张三',
    age: 18
})

state.name = '李四'   // 直接改,不用 .value

15.3 对比

对比项 ref reactive
适用 基本类型、任意 对象、数组
JS 访问 .value 直接访问
模板访问 直接 直接
替换整个对象 ✅ ❌
解构 需 toRefs 需 toRefs

15.4 推荐用法

  • 基本类型 → ref
  • 对象/数组 → reactive
  • 不确定 → 全用 ref(模板里不用 .value,更省心)

15.5 解构保持响应式

js 复制代码
const state = reactive({ name: '张三', age: 18 })
const { name, age } = toRefs(state)   // 保持响应式

16. computed 计算属性

16.1 作用

依赖其他数据,自动计算,有缓存。

16.2 基本用法

js 复制代码
const count = ref(0)
const double = computed(() => count.value * 2)
html 复制代码
<p>{{ count }}</p>
<p>{{ double }}</p>   <!-- count 变,double 自动变 -->

16.3 getter / setter

js 复制代码
const firstName = ref('张')
const lastName = ref('三')

const fullName = computed({
    get: () => firstName.value + lastName.value,
    set: (val) => {
        firstName.value = val[0]
        lastName.value = val.slice(1)
    }
})

16.4 实战:过滤列表

js 复制代码
const filteredEmployees = computed(() => {
    return employees.value.filter(emp => {
        const matchName = !activeName.value || emp.name.includes(activeName.value)
        const matchGender = !activeGender.value || emp.gender === activeGender.value
        return matchName && matchGender
    })
})

16.5 ⚠️ 注意

  • computed 里不要写异步 、不要改其他数据
  • 依赖不变时,computed 不会重新计算(有缓存)

17. watch 侦听器

17.1 作用

监听数据变化,执行副作用(如请求、打印、改其他数据)。

17.2 基本用法

js 复制代码
const count = ref(0)

watch(count, (newVal, oldVal) => {
    console.log(`从 ${oldVal} 变成 ${newVal}`)
})

17.3 监听多个

js 复制代码
watch([count, msg], ([newCount, newMsg], [oldCount, oldMsg]) => {
    console.log('变了')
})

17.4 监听对象

js 复制代码
const state = reactive({ name: '张三', age: 18 })

// 监听整个对象(深度)
watch(state, (newVal) => {
    console.log('变了')
}, { deep: true })

// 只监听某个属性
watch(() => state.name, (newVal) => {
    console.log('name 变了')
})

17.5 立即执行

js 复制代码
watch(count, (val) => {
    console.log(val)
}, { immediate: true })

17.6 实战:搜索防抖

js 复制代码
watch(searchName, (val) => {
    // 输入停止 500ms 后才查询
    clearTimeout(timer)
    timer = setTimeout(() => {
        doSearch(val)
    }, 500)
})

18. computed vs watch vs methods

对比项 computed watch methods
用途 派生新数据 监听变化做副作用 事件处理
缓存 ✅ --- ❌
异步 ❌ ✅ ✅
返回值 必须 无 可有可无
依赖自动追踪 ✅ 需手动 ---

选择建议:

  • 要派生数据 → computed
  • 要监听变化做副作用 (请求、日志) → watch
  • 要响应用户事件 → methods

第四部分:生命周期

19. 生命周期钩子

Vue 组件从创建到销毁的过程:

复制代码
beforeCreate → created → beforeMount → mounted
    → beforeUpdate → updated
    → beforeUnmount → unmounted
钩子 时机 常用场景
beforeCreate 实例创建前 很少用
created 实例创建后 请求数据(无 DOM)
beforeMount 挂载前 很少用
mounted 挂载后 操作 DOM、请求数据
beforeUpdate 更新前 很少用
updated 更新后 操作更新后的 DOM
beforeUnmount 卸载前 清理定时器、事件监听
unmounted 卸载后 很少用

20. 组合式 API 的生命周期

js 复制代码
import { onMounted, onUnmounted } from 'vue'

onMounted(() => {
    console.log('组件挂载了')
    // 请求数据、操作 DOM
})

onUnmounted(() => {
    console.log('组件卸载了')
    // 清理定时器
})

20.1 对应关系

选项式 组合式
beforeCreate setup()
created setup()
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted

20.2 实战

js 复制代码
onMounted(() => {
    // 页面加载后请求员工列表
    fetchEmployees()
})

onUnmounted(() => {
    clearInterval(timer)
})

第五部分:组件化开发

21. 组件是什么

组件 = 可复用的 UI 片段 + 逻辑。

比如一个按钮、一个表格、一个弹窗,都可以做成组件。


22. 注册组件

22.1 全局注册

js 复制代码
app.component('MyButton', {
    template: '<button>点我</button>'
})

22.2 局部注册

js 复制代码
import MyButton from './MyButton.vue'

export default {
    components: { MyButton }
}

22.3 <script setup>(推荐)

vue 复制代码
<script setup>
import MyButton from './MyButton.vue'
</script>

<template>
    <MyButton />
</template>

导入即注册,不用写 components。


23. props 父传子

23.1 子组件

vue 复制代码
<script setup>
const props = defineProps({
    title: String,
    count: {
        type: Number,
        default: 0,
        required: false
    }
})
</script>

<template>
    <h3>{{ title }} - {{ count }}</h3>
</template>

23.2 父组件

vue 复制代码
<template>
    <Child title="你好" :count="10" />
</template>

23.3 ⚠️ 单向数据流

props 只能读,不能改。 想改要 emit 通知父组件。


24. emit 子传父

24.1 子组件

vue 复制代码
<script setup>
const emit = defineEmits(['add', 'delete'])

function handleAdd() {
    emit('add', 1)
}

function handleDelete(id) {
    emit('delete', id)
}
</script>

<template>
    <button @click="handleAdd">加1</button>
    <button @click="handleDelete(5)">删除</button>
</template>

24.2 父组件

vue 复制代码
<template>
    <Child @add="onAdd" @delete="onDelete" />
</template>

<script setup>
function onAdd(n) { console.log('加了', n) }
function onDelete(id) { console.log('删了', id) }
</script>

25. slot 插槽

25.1 默认插槽

vue 复制代码
<!-- 子组件 -->
<template>
    <div class="card">
        <slot>默认内容</slot>
    </div>
</template>

<!-- 父组件 -->
<Card>
    <p>我是插槽内容</p>
</Card>

25.2 具名插槽

vue 复制代码
<!-- 子组件 -->
<slot name="header"></slot>
<slot name="footer"></slot>

<!-- 父组件 -->
<template #header>
    <h1>标题</h1>
</template>
<template #footer>
    <p>底部</p>
</template>

25.3 作用域插槽

vue 复制代码
<!-- 子组件 -->
<slot :user="user"></slot>

<!-- 父组件 -->
<template #default="{ user }">
    {{ user.name }}
</template>

26. script setup 语法糖

<script setup> 是 Vue 3.2+ 的推荐写法。

26.1 基本结构

vue 复制代码
<script setup>
import { ref, computed } from 'vue'

const count = ref(0)
const double = computed(() => count.value * 2)

function add() { count.value++ }
</script>

<template>
    <button @click="add">{{ count }}</button>
    <p>{{ double }}</p>
</template>

26.2 特点

  • 顶层变量、函数自动暴露给模板
  • 不需要 return
  • 不需要 components 注册
  • 组件名默认是文件名

26.3 对比

对比项 普通 setup <script setup>
模板暴露 需 return 自动
组件注册 需 components 自动
代码量 多 少
推荐 --- ✅

第六部分:工程化

27. Vite 项目搭建

27.1 创建项目

bash 复制代码
npm create vite@latest my-vue-app -- --template vue
cd my-vue-app
npm install
npm run dev

27.2 目录结构

复制代码
my-vue-app/
├── index.html
├── package.json
├── vite.config.js
├── src/
│   ├── main.js
│   ├── App.vue
│   ├── components/
│   └── assets/
└── public/

28. 项目结构

复制代码
src/
├── main.js          # 入口
├── App.vue          # 根组件
├── components/      # 通用组件
├── views/           # 页面
├── router/          # 路由
├── stores/          # Pinia 状态
├── api/             # 接口
├── utils/           # 工具
└── assets/          # 静态资源

29. 单文件组件 .vue

vue 复制代码
<template>
    <div class="box">
        <h1>{{ title }}</h1>
    </div>
</template>

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

const title = ref('Hello')
</script>

<style scoped>
.box { padding: 20px; }
</style>

29.1 三个块

块 作用
<template> 模板(必须)
<script setup> 逻辑(推荐)
<style scoped> 样式(scoped 局部)

第七部分:生态

30. Vue Router 路由

30.1 安装

bash 复制代码
npm install vue-router@4

30.2 定义路由

js 复制代码
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'

const routes = [
    { path: '/', component: Home },
    { path: '/about', component: About }
]

const router = createRouter({
    history: createWebHistory(),
    routes
})

export default router

30.3 挂载

js 复制代码
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

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

30.4 使用

vue 复制代码
<template>
    <router-link to="/">首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-view />
</template>

30.5 传参

js 复制代码
// 路由定义
const routes = [
    { path: '/user/:id', component: User }
]

// 跳转
router.push('/user/123')

// 获取
const route = useRoute()
console.log(route.params.id)

30.6 编程式导航

js 复制代码
import { useRouter } from 'vue-router'
const router = useRouter()

router.push('/about')
router.replace('/about')
router.back()

31. Pinia 状态管理

31.1 安装

bash 复制代码
npm install pinia

31.2 定义 store

js 复制代码
// stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => {
    const count = ref(0)
    const double = computed(() => count.value * 2)

    function increment() {
        count.value++
    }

    return { count, double, increment }
})

31.3 使用

vue 复制代码
<script setup>
import { useCounterStore } from '@/stores/counter'

const store = useCounterStore()
</script>

<template>
    <p>{{ store.count }}</p>
    <p>{{ store.double }}</p>
    <button @click="store.increment">+1</button>
</template>

31.4 挂载

js 复制代码
// main.js
import { createPinia } from 'pinia'
createApp(App).use(createPinia()).mount('#app')

31.5 对比 Vuex

对比项 Vuex Pinia
版本 Vue 2/3 Vue 3
写法 mutations/actions 直接改
TS 支持 一般 ✅ 好
推荐 旧项目 ✅ 新项目

32. Axios 网络请求

32.1 安装

bash 复制代码
npm install axios

32.2 基本用法

js 复制代码
import axios from 'axios'

const res = await axios.get('/api/employees')
console.log(res.data)

await axios.post('/api/employees', { name: '张三' })
await axios.put('/api/employees/1', { name: '李四' })
await axios.delete('/api/employees/1')

32.3 封装

js 复制代码
// api/request.js
import axios from 'axios'

const request = axios.create({
    baseURL: 'http://localhost:8080',
    timeout: 5000
})

request.interceptors.request.use(config => {
    // 加 token
    config.headers.Authorization = localStorage.getItem('token')
    return config
})

request.interceptors.response.use(
    res => res.data,
    err => {
        console.error(err)
        return Promise.reject(err)
    }
)

export default request

32.4 实战

js 复制代码
import request from '@/api/request'

async function fetchEmployees() {
    const data = await request.get('/employees')
    employees.value = data
}

onMounted(() => {
    fetchEmployees()
})

第八部分:实战与调试

33. Tlias 员工管理实战

33.1 需求

  • 登录 / 退出
  • 搜索:姓名、性别、职位
  • 表格:10 条数据
  • 新增 / 编辑 / 删除
  • 批量删除
  • 行选中

33.2 核心数据结构

js 复制代码
const employees = ref([
    {
        name: '令狐冲',
        gender: '男',
        avatar: '🧑',
        position: '讲师',
        hireDate: '2021-06-15',
        lastOpTime: '2024-09-16 15:30'
    }
])

33.3 渲染

html 复制代码
<tr v-for="emp in filteredEmployees" :key="emp.name">
    <td>{{ emp.name }}</td>
    <td>{{ emp.gender }}</td>
    <td>
        <span v-if="emp.gender === '女'" class="avatar female">👩</span>
        <span v-else-if="emp.gender === '男'" class="avatar male">🧑</span>
        <span v-else class="avatar unknown">❓</span>
    </td>
    <td>
        <span v-if="emp.position === '讲师'">🎓 {{ emp.position }}</span>
        <span v-else-if="emp.position === '班主任'">📋 {{ emp.position }}</span>
        <span v-else>{{ emp.position }}</span>
    </td>
    <td>{{ emp.hireDate }}</td>
    <td>{{ emp.lastOpTime }}</td>
    <td>
        <button @click.stop="edit(emp)">编辑</button>
        <button @click.stop="remove(emp)">删除</button>
    </td>
</tr>

33.4 搜索

js 复制代码
const searchName = ref('')
const activeName = ref('')

function search() {
    activeName.value = searchName.value
}

const filteredEmployees = computed(() => {
    return employees.value.filter(emp => {
        return !activeName.value || emp.name.includes(activeName.value)
    })
})

33.5 多选

js 复制代码
const selectedNames = ref([])

const isAllSelected = computed(() => {
    return filteredEmployees.value.length > 0 &&
           selectedNames.value.length === filteredEmployees.value.length
})

function toggleSelectAll() {
    if (isAllSelected.value) {
        selectedNames.value = []
    } else {
        selectedNames.value = filteredEmployees.value.map(e => e.name)
    }
}

34. Vue DevTools 调试

34.1 安装

浏览器 地址
Chrome Chrome 应用商店搜 Vue.js devtools
Edge Edge 加载项搜 Vue.js devtools
Firefox Firefox 附加组件搜 Vue.js devtools

34.2 关键设置

file:// 页面必须开:

复制代码
chrome://extensions/ → Vue.js devtools → 详情 → 允许访问文件 URL

推荐用 Live Server (http://127.0.0.1:5500/),不受限制。

34.3 验证

  1. 打开 Vue 页面
  2. F12 → 顶部有 Vue 标签
  3. 工具栏 Vue 图标绿色

34.4 图标灰色排查

原因 解决
页面不是 Vue 切到 Vue 页面
没刷新 F5
file:// 限制 开"允许访问文件 URL"
版本不对 v7 只支持 Vue 3,Vue 2 用 v6

34.5 面板功能

面板 作用
组件树 查看嵌套
State 实时查看/改数据
Events 事件触发
Timeline 性能

第九部分:避坑与速查

35. 常见错误汇总

35.1 Vue is not defined

原因: 大小写错。

js 复制代码
vue   // ❌
Vue   // ✅

35.2 页面显示 {``{ message }}

原因:

  1. Vue 没加载(CDN 挂了 / 路径错)
  2. type="module" + file:// 被 CORS 拦
  3. 忘写 .mount('#app')
  4. #app id 不一致

排查: F12 → Console → Vue.version,有输出说明 Vue 加载成功。

35.3 Cannot read properties of null

原因: #app 元素不存在。

解决: 检查 HTML 里有没有 <div id="app">,且 <script> 在它之后。

35.4 v-model 绑定 undefined

原因: 变量没在 data() 里定义。

解决: 在 return 对象里加上。

35.5 插值表达式用在属性里

html 复制代码
<!-- 错误 -->
<img src="{{ image }}">

<!-- 正确 -->
<img :src="image">

35.6 v-for 忘写 :key

后果: 警告、性能差、渲染错乱。

解决: :key="item.id",不要用 index。

35.7 修改 props 报错

原因: props 是只读的。

解决: 子组件 emit 事件,父组件改数据。

35.8 reactive 解构丢失响应式

js 复制代码
const state = reactive({ name: '张三' })
const { name } = state   // ❌ 丢失响应式

// 正确
const { name } = toRefs(state)   // ✅

35.9 ref 在 JS 里忘了 .value

js 复制代码
const count = ref(0)
count = 1        // ❌
count.value = 1  // ✅

35.10 computed 里写异步

computed 必须是同步的。 异步用 watch 或 onMounted。


36. 指令速查表

指令 缩写 作用 示例
v-for --- 列表 v-for="item in list"
v-bind : 属性 :src="img"
v-if --- 条件 v-if="isShow"
v-else-if --- 条件链 v-else-if="type===2"
v-else --- 兜底 v-else
v-show --- 显示隐藏 v-show="isShow"
v-model --- 双向 v-model="name"
v-on @ 事件 @click="handle"

修饰符

修饰符 用于 作用
.stop v-on 阻止冒泡
.prevent v-on 阻止默认
.self v-on 只点自己
.once v-on 只一次
.enter v-on 回车
.trim v-model 去空格
.number v-model 转数字
.lazy v-model 失焦同步

组合式 API 速查

API 作用
ref 基本类型响应式
reactive 对象响应式
computed 计算属性
watch 侦听器
onMounted 挂载后
onUnmounted 卸载后
defineProps 定义 props
defineEmits 定义 emits

37. 学习路线

第一阶段:基础

  • HTML / CSS / JS
  • Vue 3 快速入门
  • 指令:v-for / v-bind / v-if / v-model / v-on
  • computed / watch

第二阶段:进阶

  • 组件化(props / emit / slot)
  • 生命周期
  • <script setup>
  • 组件通信(provide/inject)

第三阶段:生态

  • Vue Router
  • Pinia
  • Axios
  • Element Plus

第四阶段:工程化

  • Vite
  • 项目结构
  • TypeScript + Vue
  • 打包部署

推荐资源

资源 地址
Vue 3 官方 https://cn.vuejs.org/
Vue Router https://router.vuejs.org/zh/
Pinia https://pinia.vuejs.org/zh/
Vite https://cn.vitejs.dev/
Element Plus https://element-plus.org/zh-CN/
VueUse https://vueuse.org/

附录:Vue 2 vs Vue 3

对比项 Vue 2 Vue 3
响应式 Object.defineProperty Proxy
API 选项式 选项式 + 组合式
模板根 只能一个 可以多个
生命周期 beforeDestroy beforeUnmount
v-model value + input modelValue + update
Teleport ❌ ✅
性能 一般 更快
TS 一般 ✅
相关推荐
用户847172102841 小时前
.shot 文件格式规范
前端·后端
web打印社区1 小时前
政务办事大厅:叫号小票、办理回执网页怎么静默出纸
前端·javascript·vue.js·pdf·html·政务
Joe_Wang51 小时前
【从0到1学习JVM · 21】既不浪费空间又没有碎片,标记整理算法到底慢在哪
jvm·学习·算法·垃圾回收·标记整理算法
C++ 老炮儿的技术栈1 小时前
有符号变量与无符号变量的区别
服务器·开发语言·前端·数据结构·c++·算法·c
个 人 练 习 生1 小时前
C++:模板初阶详解
开发语言·c++·经验分享·学习
aixingpan1 小时前
aixingpan.cn API开发文档:api_docs_bichart_natalvssecondary2接口指南
前端·php
辣知2 小时前
辣知·化智69 西周青铜器何尊的宅兹中国
学习
AOI小白新手上路2 小时前
韦东山 3-5 I.MX6ULL 的 LED 编程 · 学习笔记
arm开发·笔记·单片机·学习·架构·硬件架构
知识分享小能手2 小时前
C++ 学习教程,从入门到精通,C++类继承 — 完整知识点详解(13)
开发语言·c++·学习