Vue 3 入门——自学习版本
1. Vue 3 入门
- 1.1 Vue 3 简介与历史
- 1.2 Vue 3 与 Vue 2 的主要区别
- 1.3 安装与项目初始化
- 使用 Vue CLI 创建项目
- 使用 Vite 创建项目
- 1.4 Vue 3 的基本概念:响应式、组件、模板
- 1.5 第一个 Vue 3 项目
2. Vue 3 响应式系统
- 2.1 什么是响应式
- 2.2
ref
和reactive
的使用 - 2.3 计算属性 (
computed
) - 2.4 侦听器 (
watch
) - 2.5 深入理解 Vue 3 的响应式原理
3. Vue 3 组件系统
-
3.1 什么是组件
-
3.2 创建和使用组件
-
3.3 组件间的通信:props、$emit、事件总线
-
3.4 插槽(slots)
- 默认插槽与具名插槽
- 作用域插槽
-
3.5 动态组件与异步组件
4. Vue 3 的模板语法
- 4.1 模板的基本语法:插值、指令(
v-if
、v-for
、v-bind
等) - 4.2 表达式与条件渲染
- 4.3 列表渲染与事件处理
- 4.4 v-model 双向绑定的使用
5. Vue 3 生命周期
- 5.1 Vue 3 生命周期概述
- 5.2 Composition API 生命周期钩子
onMounted
、onBeforeMount
等
- 5.3 Options API 生命周期钩子
mounted
、created
等
6. Vue 3 的 Composition API
- 6.1 Composition API 简介
- 6.2
setup
函数 - 6.3
ref
、reactive
、computed
在 Composition API 中的使用 - 6.4
watch
和watchEffect
- 6.5 自定义组合函数(composables)
7. Vue 3 路由(Vue Router)
- 7.1 安装与配置 Vue Router
- 7.2 路由的基本使用
- 7.3 动态路由与路由参数
- 7.4 嵌套路由与命名视图
- 7.5 路由守卫与导航钩子
8. Vue 3 状态管理(Vuex)
- 8.1 Vuex 的基本概念
- 8.2 安装与配置 Vuex
- 8.3 State、Getters、Mutations、Actions 的使用
- 8.4 Vuex 模块化
- 8.5 与组件的结合使用
9. Vue 3 异常处理与调试
- 9.1 错误捕获与处理
- 9.2 使用 Vue DevTools 调试
- 9.3 日志记录与性能分析
10. Vue 3 的其他功能
- 10.1 过渡与动画
- 10.2 Vue 3 的 Teleport(传送门)
- 10.3 Vue 3 的
Suspense
和异步组件 - 10.4 Vue 3 中的自定义指令
11. Vue 3 项目实战
- 11.1 开发一个待办事项应用
- 11.2 开发一个博客网站(或类似项目)
- 11.3 集成第三方库与插件(如 Vue Router、Vuex、Axios)
12. Vue 3 性能优化与部署
- 12.1 性能优化策略
- 懒加载与代码分割
- 虚拟列表
- 12.2 项目构建与打包
- 12.3 部署到生产环境
13. Vue 3 与 TypeScript
- 13.1 Vue 3 中如何使用 TypeScript
- 13.2 类型推导与类型检查
- 13.3 类型声明文件与接口
1. Vue 3 入门
1.1 Vue 3 简介与历史
Vue.js 是一个用于构建用户界面的渐进式框架。它采用了响应式的数据绑定和组件化的开发模式,使得开发者可以高效地构建现代化的单页面应用(SPA)。Vue 3 是 Vue.js 框架的最新版本,在 Vue 2 的基础上做了许多改进和优化,特别是在性能和可维护性方面。
Vue 3 的开发始于 2018 年,由尤雨溪(Evan You)主导,Vue 3 于 2020 年发布。Vue 3 的发布标志着 Vue.js 框架进入了一个新的时代,引入了许多新特性,如 Composition API、性能优化和更好的 TypeScript 支持。
1.2 Vue 3 与 Vue 2 的主要区别
Vue 3 与 Vue 2 相比,有几个显著的改进:
-
Composition API:Vue 3 引入了 Composition API,使得组件的代码更加模块化,易于复用和维护。与 Vue 2 的 Options API(通过
data
、methods
、computed
等选项来定义组件的行为)不同,Composition API 通过setup
函数提供了更灵活的方式来组织组件的逻辑。 -
性能提升:Vue 3 在性能上有显著的改进,特别是在虚拟 DOM 的重绘优化、内存管理和组件生命周期上。Vue 3 的响应式系统使用 Proxy 取代了 Vue 2 的 Object.defineProperty,使得性能更加高效,特别是在大量数据变动时。
-
TypeScript 支持:Vue 3 对 TypeScript 的支持更好,开发者可以更方便地在 Vue 组件中使用 TypeScript 来提高类型安全和开发效率。
-
更小的包体积:Vue 3 采用了 tree-shaking 优化,去除了不必要的代码,使得框架体积更小,加载速度更快。
-
更灵活的生命周期钩子:Vue 3 为 Composition API 提供了新的生命周期钩子,如
onMounted
、onBeforeMount
,让开发者可以在setup
中更灵活地管理组件生命周期。
1.3 安装与项目初始化
在 Vue 3 中,我们可以使用 Vue CLI 或 Vite 来快速创建项目。
使用 Vue CLI 创建项目
-
安装 Vue CLI(如果尚未安装):
npm install -g @vue/cli
-
创建一个新的 Vue 3 项目:
vue create my-project
在创建过程中,选择 Vue 3 配置选项。
-
进入项目目录并启动开发服务器:
cd my-project npm run serve
使用 Vite 创建项目
Vite 是一个快速构建工具,Vue 3 推荐使用 Vite 来进行开发,因为它比 Vue CLI 更加高效。
-
创建一个新的 Vue 3 项目:
npm create vite@latest my-project --template vue
-
进入项目目录并安装依赖:
cd my-project npm install
-
启动开发服务器:
npm run dev
1.4 Vue 3 的基本概念:响应式、组件、模板
-
响应式:Vue 3 的核心特性之一就是响应式系统。Vue 会自动追踪数据的变化,并在数据变化时更新相关的视图。通过
ref
和reactive
,你可以使普通的 JavaScript 对象变得响应式。 -
组件:Vue 是一个基于组件的框架。每个 Vue 应用由多个组件组成,组件可以包含模板、样式和逻辑。Vue 3 支持使用
setup
函数来定义组件的逻辑,这是 Composition API 的一部分。 -
模板:Vue 使用基于 HTML 的模板语法来声明组件的 UI。模板与数据绑定,通过指令(如
v-if
、v-for
)和插值表达式来动态生成视图。
1.5 第一个 Vue 3 项目
让我们快速创建一个 Vue 3 应用来展示如何构建一个基本的 Vue 3 项目。
-
在终端中创建一个 Vue 3 项目:
npm create vite@latest vue-app --template vue
-
进入项目目录:
cd vue-app
-
安装依赖:
npm install
-
编辑
src/App.vue
,创建一个简单的组件:<template><div><h1>Hello, Vue 3!</h1><p>我的年龄是:{{ age }}</p><button @click="incrementAge">增加年龄</button></div> </template><script setup> import { ref } from 'vue';const age = ref(18);const incrementAge = () => {age.value++; }; </script><style> h1 {color: #42b983; } </style>
-
启动项目:
npm run dev
现在,你已经创建了一个简单的 Vue 3 项目。当点击按钮时,页面上的年龄会增加。通过这个例子,你可以看到 Vue 3 的基础组件、响应式和事件处理的简单实现。
2. Vue 3 响应式系统
2.1 什么是响应式
响应式是 Vue 的核心特性之一,意味着当数据发生变化时,视图会自动更新。Vue 的响应式系统会追踪数据的变化,并将变动反映到 DOM 上。这种机制减少了开发者手动更新 DOM 的需求,提高了开发效率。
Vue 3 引入了基于 Proxy
的响应式系统,这与 Vue 2 中基于 Object.defineProperty
的响应式实现有所不同。Proxy
能够拦截对象的操作,使得数据变化时,Vue 能够捕捉到并执行相应的更新。
2.2 ref
和 reactive
的使用
在 Vue 3 中,ref
和 reactive
是两个最常用的响应式 API,分别用于处理基本数据类型和对象/数组。
ref
ref
用于使基本数据类型变得响应式,通常用于处理原始值(如字符串、数字、布尔值等)。
-
语法:
const myValue = ref(0);
-
使用方式:
- 在模板中可以直接访问
myValue
,但是在 JavaScript 代码中需要通过.value
来访问或修改其值。
<template><div>{{ myValue }}</div><button @click="increment">增加</button> </template><script setup> import { ref } from 'vue';const myValue = ref(0);const increment = () => {myValue.value++; }; </script>
- 在模板中可以直接访问
reactive
reactive
用于将对象或数组变为响应式,适合处理复杂的数据结构。
-
语法:
const myObject = reactive({ count: 0, name: 'Vue' });
-
使用方式:
- 通过
reactive
创建的对象可以直接访问其属性,无需.value
。
<template><div>{{ myObject.count }}</div><button @click="increment">增加</button> </template><script setup> import { reactive } from 'vue';const myObject = reactive({ count: 0 });const increment = () => {myObject.count++; }; </script>
- 通过
2.3 计算属性 (computed
)
计算属性(computed
)是 Vue 中用于基于已有数据进行计算并缓存结果的一种机制。计算属性与普通的方法不同,计算属性会缓存其计算结果,直到依赖的数据发生变化,才会重新计算。
-
语法:
const fullName = computed(() => {return `${firstName.value} ${lastName.value}`; });
-
使用方式:
- 计算属性通常用于当数据的计算依赖于其他数据时,自动更新视图。
<template><div>{{ fullName }}</div> </template><script setup> import { ref, computed } from 'vue';const firstName = ref('John'); const lastName = ref('Doe');const fullName = computed(() => `${firstName.value} ${lastName.value}`); </script>
-
特点:
- 计算属性是惰性求值的,只有在依赖的响应式数据发生变化时才会重新计算。
- 适合用于逻辑上较为复杂的计算,如拼接字符串、处理日期等。
2.4 侦听器 (watch
)
侦听器(watch
)用于在数据变化时执行某些操作。与计算属性不同,侦听器更适合执行副作用操作,如异步请求、手动修改其他状态等。
-
语法:
watch(source, callback, options);
source
:要观察的数据源,通常是响应式的数据或计算属性。callback
:当source
改变时执行的回调函数。options
:可选的配置选项,如immediate
(立即执行回调)和deep
(深度观察对象)。
-
使用方式:
<template><div>{{ count }}</div><button @click="increment">增加</button> </template><script setup> import { ref, watch } from 'vue';const count = ref(0);const increment = () => {count.value++; };// 监听 count 的变化 watch(count, (newValue, oldValue) => {console.log(`count 变化了,从 ${oldValue} 变成了 ${newValue}`); }); </script>
-
特点:
watch
适用于监控一个或多个响应式数据的变化,并做出响应。- 适合用来处理复杂的副作用,如异步操作、数据请求等。
- 可以设置
deep
来深度监听对象的嵌套属性。
2.5 深入理解 Vue 3 的响应式原理
Vue 3 的响应式系统是基于 JavaScript 的 Proxy
对象实现的,相比 Vue 2 中的 Object.defineProperty
,Proxy
提供了更强大和灵活的拦截功能。
-
核心原理:
-
在 Vue 3 中,
reactive
和ref
会通过Proxy
拦截对象的读取和写入操作。当访问对象的属性时,Proxy
会自动记录下依赖,并在属性值发生变化时,通知视图更新。 -
Proxy
可以拦截以下操作:- 读取属性(
get
) - 设置属性(
set
) - 删除属性(
deleteProperty
)
- 读取属性(
-
-
实现过程:
- 每当我们访问一个响应式对象的属性时,
Proxy
会触发get
操作,这时 Vue 会将当前组件作为依赖进行追踪。 - 当对象的属性发生变化时,
Proxy
会触发set
操作,Vue 会更新依赖该属性的组件。
- 每当我们访问一个响应式对象的属性时,
-
Proxy
的优势:- Vue 3 的响应式系统通过
Proxy
使得 Vue 不需要显式地定义每个数据属性的 getter 和 setter。这样可以提高性能,简化代码,尤其是在处理嵌套数据和动态属性时。 Proxy
可以捕捉到更广泛的操作,包括属性的删除和原型链的继承,这在 Vue 2 中是无法实现的。
- Vue 3 的响应式系统通过
3. Vue 3 组件系统
3.1 什么是组件
组件是 Vue.js 中的核心概念之一,是 Vue 应用的构建块。每个 Vue 组件本质上是一个自包含的、可复用的单元,它包含了 HTML、CSS 和 JavaScript 代码。通过组件化的开发方式,开发者可以将复杂的 UI 拆分为多个小的独立部分,使得应用更加模块化、易于维护。
Vue 组件是基于模板、脚本和样式的组合,可以通过 props
接收父组件传递的数据,使用 data
存储内部状态,并通过 methods
和 computed
定义组件的行为。组件的生命周期函数也使得我们能够在特定的时刻执行代码。
3.2 创建和使用组件
在 Vue 3 中,组件可以通过 defineComponent
或 script setup
来定义。这里介绍两种常见的方式:
3.2.1 使用 defineComponent
创建组件
defineComponent
是 Vue 3 提供的一个函数,用来定义一个组件,它帮助我们定义一个标准的 Vue 组件。
-
语法:
import { defineComponent } from 'vue';export default defineComponent({name: 'MyComponent',data() {return {message: 'Hello, Vue 3!'};},methods: {greet() {alert(this.message);}} });
3.2.2 使用 script setup
创建组件
Vue 3 的 script setup
是 Composition API 的一种简化方式,极大地减少了模板和逻辑之间的代码冗余,使得组件代码更加简洁。
-
语法:
<template><div>{{ message }}</div><button @click="greet">Greet</button> </template><script setup> import { ref } from 'vue';const message = ref('Hello, Vue 3!');const greet = () => {alert(message.value); }; </script>
-
使用方式:
- 在
template
中,定义组件的 HTML 结构; - 在
script setup
中,定义响应式数据和方法。
- 在
3.2.3 使用组件
一旦组件被创建,我们可以在父组件中使用它。首先,确保组件被导入并注册。
-
父组件使用子组件:
<template><MyComponent /> </template><script setup> import MyComponent from './components/MyComponent.vue'; </script>
3.3 组件间的通信:props、$emit、事件总线
Vue 提供了多种方式来处理组件间的通信。
3.3.1 使用 props
传递数据
props
是子组件接收父组件传递数据的机制。父组件通过在子组件标签上使用 v-bind
(或简写 :
)将数据传递给子组件。
-
父组件:
<template><ChildComponent :message="parentMessage" /> </template><script setup> import { ref } from 'vue'; import ChildComponent from './components/ChildComponent.vue';const parentMessage = ref('Hello from Parent!'); </script>
-
子组件:
<template><div>{{ message }}</div> </template><script setup> defineProps({message: String }); </script>
3.3.2 使用 $emit
向父组件发送事件
子组件通过 $emit
向父组件发送事件。当子组件需要与父组件进行交互时,通常使用 $emit
发送自定义事件,父组件通过 v-on
(或简写 @
)监听这些事件。
-
子组件:
<template><button @click="sendMessage">Click me</button> </template><script setup> defineEmits(['sendMessage']);const sendMessage = () => {$emit('sendMessage', 'Hello, Parent!'); }; </script>
-
父组件:
<template><ChildComponent @sendMessage="receiveMessage" /> </template><script setup> import ChildComponent from './components/ChildComponent.vue';const receiveMessage = (message) => {console.log('Received message:', message); }; </script>
3.3.3 事件总线(Event Bus)
事件总线是一种跨组件通信的方式,它适用于非父子组件之间的通信。通常,我们可以使用一个中央对象来监听和触发事件。
-
创建事件总线:
import { reactive } from 'vue';const eventBus = reactive({emit(event, data) {this[event] && this[event](data);},on(event, callback) {this[event] = callback;} });export default eventBus;
-
组件通信:
<template><button @click="sendMessage">Send Message</button> </template><script setup> import eventBus from './eventBus';const sendMessage = () => {eventBus.emit('messageReceived', 'Hello from Component!'); }; </script>
3.4 插槽(Slots)
插槽是 Vue 中的一种机制,允许我们在父组件中通过占位符的方式传递 HTML 内容到子组件中,从而实现更大的灵活性和复用性。
3.4.1 默认插槽与具名插槽
-
默认插槽:
默认插槽是最基本的插槽类型,用于插入父组件中的内容。-
父组件:
<template><ChildComponent><p>This is content passed through default slot.</p></ChildComponent> </template>
-
子组件:
<template><slot></slot> <!-- 默认插槽 --> </template>
-
-
具名插槽:
具名插槽允许父组件将不同的内容传递到子组件的多个插槽中,通过插槽的name
属性进行区分。-
父组件:
<template><ChildComponent><template v-slot:header><h1>This is the header</h1></template><template v-slot:footer><footer>This is the footer</footer></template></ChildComponent> </template>
-
子组件:
<template><slot name="header"></slot> <!-- 具名插槽 --><slot name="footer"></slot> </template>
-
3.4.2 作用域插槽
作用域插槽是具名插槽的一种扩展,允许子组件将数据暴露给父组件,从而在父组件中访问这些数据。
-
父组件:
<template><ChildComponent v-slot:default="{ message }"><p>{{ message }}</p></ChildComponent> </template>
-
子组件:
<template><slot :message="message"></slot> <!-- 传递数据给父组件 --> </template><script setup> const message = 'Hello from Child Component!'; </script>
3.5 动态组件与异步组件
3.5.1 动态组件
动态组件允许在运行时切换不同的组件。Vue 3 通过 <component :is="componentName">
来实现动态组件。
-
语法:
<component :is="currentComponent"></component>
-
使用方式:
<template><button @click="switchComponent">Switch Component</button><component :is="currentComponent" /> </template><script setup> import { ref } from 'vue'; import ComponentA from './ComponentA.vue'; import ComponentB from './ComponentB.vue';const currentComponent = ref(ComponentA);const switchComponent = () => {currentComponent.value = currentComponent.value === ComponentA ? ComponentB : ComponentA; }; </script>
3.5.2 异步组件
异步组件是指只有在需要时才加载的组件,可以减少初始加载时间,提升应用性能。
-
语法:
const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));
-
使用方式:
<template><AsyncComponent /> </template><script setup> import { defineAsyncComponent } from 'vue';const AsyncComponent = defineAsyncComponent(() => import('./components/AsyncComponent.vue')); </script>
4. Vue 3 的模板语法
Vue 3 的模板语法采用基于 HTML 的声明式语法,允许开发者通过简单的标记来定义视图和逻辑的绑定。通过模板,我们可以轻松地将数据与 DOM 进行双向绑定,动态更新视图。
4.1 模板的基本语法:插值、指令(v-if、v-for、v-bind 等)
Vue 模板语法的基本组成包括插值和指令,下面是一些常见的语法。
插值
插值是指将数据绑定到模板中的某个位置。在 Vue 中,插值是通过 {{ }}
来实现的。常见的插值包括:
-
文本插值:
<template><div>{{ message }}</div> </template><script setup> import { ref } from 'vue';const message = ref('Hello, Vue 3!'); </script>
-
属性插值:
<template><img :src="imageUrl" alt="Vue 3 Logo"> </template><script setup> const imageUrl = ref('https://vuejs.org/images/logo.png'); </script>
指令
Vue 提供了许多内置的指令来对 DOM 元素进行动态操作。指令以 v-
开头,用于绑定数据和控制 DOM 的行为。
-
v-bind:动态绑定属性值。
<template><a v-bind:href="url">Visit Vue</a> </template><script setup> const url = ref('https://vuejs.org'); </script>
-
v-if:条件渲染。
<template><p v-if="isVisible">This is visible when isVisible is true.</p> </template><script setup> const isVisible = ref(true); </script>
-
v-for:列表渲染。
<template><ul><li v-for="item in items" :key="item.id">{{ item.name }}</li></ul> </template><script setup> const items = ref([{ id: 1, name: 'Vue 3' },{ id: 2, name: 'React' },{ id: 3, name: 'Angular' } ]); </script>
-
v-on:监听事件。
<template><button v-on:click="handleClick">Click me</button> </template><script setup> const handleClick = () => {console.log('Button clicked!'); }; </script>
4.2 表达式与条件渲染
表达式
Vue 允许在模板中使用 JavaScript 表达式进行计算。这些表达式会在数据变化时自动更新。常见的表达式包括算术运算、逻辑运算、字符串拼接等。
-
基本表达式:
<template><p>{{ count + 1 }}</p> <!-- 输出 count + 1 --> </template><script setup> const count = ref(5); </script>
-
逻辑运算:
<template><p>{{ isActive ? 'Active' : 'Inactive' }}</p> </template><script setup> const isActive = ref(true); </script>
条件渲染
Vue 提供了 v-if
和 v-show
两个指令来处理条件渲染。
-
v-if:当条件为真时,渲染该元素,否则不渲染。
<template><p v-if="isVisible">This is visible when isVisible is true.</p> </template><script setup> const isVisible = ref(true); </script>
-
v-show:和
v-if
类似,但v-show
始终渲染元素,只是通过display: none
来隐藏元素。这对于频繁切换显示状态的元素来说更为高效。<template><p v-show="isVisible">This is visible when isVisible is true.</p> </template><script setup> const isVisible = ref(true); </script>
-
v-else:和
v-if
配合使用,用于处理条件渲染的“否则”情况。<template><p v-if="isVisible">This is visible when isVisible is true.</p><p v-else>This is visible when isVisible is false.</p> </template><script setup> const isVisible = ref(false); </script>
4.3 列表渲染与事件处理
列表渲染
Vue 提供了 v-for
指令来循环渲染数组或对象。你可以使用 v-for
遍历数据并渲染每一项。
-
v-for 循环数组:
<template><ul><li v-for="item in items" :key="item.id">{{ item.name }}</li></ul> </template><script setup> const items = ref([{ id: 1, name: 'Vue 3' },{ id: 2, name: 'React' },{ id: 3, name: 'Angular' } ]); </script>
-
v-for 循环对象:
<template><ul><li v-for="(value, key) in object" :key="key">{{ key }}: {{ value }}</li></ul> </template><script setup> const object = ref({name: 'Vue 3',version: '3.0' }); </script>
事件处理
Vue 通过 v-on
或简写 @
来监听 DOM 事件并执行对应的事件处理方法。
-
事件绑定:
<template><button @click="handleClick">Click me</button> </template><script setup> const handleClick = () => {alert('Button clicked!'); }; </script>
-
事件参数:
<template><button @click="handleClick('Hello')">Click me</button> </template><script setup> const handleClick = (message) => {alert(message); }; </script>
4.4 v-model 双向绑定的使用
Vue 通过 v-model
实现双向数据绑定,即自动同步输入控件的值和组件的数据。当输入控件的值发生变化时,数据会更新;反之,数据变化时,控件的值也会更新。
基本使用
在表单输入元素中使用 v-model
实现双向绑定。
-
文本输入框:
<template><input v-model="message" /><p>{{ message }}</p> </template><script setup> const message = ref('Hello, Vue 3!'); </script>
在组件中使用 v-model
你可以在自定义组件中使用 v-model
来实现双向数据绑定。
-
父组件:
<template><ChildComponent v-model="message" /><p>{{ message }}</p> </template><script setup> import { ref } from 'vue'; import ChildComponent from './components/ChildComponent.vue';const message = ref('Hello from Parent!'); </script>
-
子组件:
<template><input v-model="internalMessage" /> </template><script setup> const internalMessage = ref('');defineEmits(['update:modelValue']); watch(internalMessage, (newValue) => {$emit('update:modelValue', newValue); }); </script>
在 Vue 3 中,v-model
默认绑定到 modelValue
属性,并触发 update:modelValue
事件。如果需要更改默认的 modelValue
,可以通过 model
选项进行自定义。
5. Vue 3 生命周期
Vue 生命周期是指从一个组件创建到销毁的过程中,Vue 会为组件提供一系列钩子函数,用于在不同的生命周期阶段执行特定的代码。这些生命周期钩子帮助开发者处理组件的初始化、数据获取、清理工作等。
在 Vue 3 中,生命周期钩子的使用发生了一些变化,特别是引入了 Composition API,使得生命周期钩子能够以更灵活的方式进行管理。
5.1 Vue 3 生命周期概述
Vue 组件的生命周期可以分为三个主要阶段:
- 创建阶段:组件被实例化,数据被初始化,但还没有挂载到 DOM 上。
- 挂载阶段:组件的模板被渲染并挂载到 DOM 上,组件已经可见,用户可以与组件进行交互。
- 销毁阶段:组件从 DOM 中移除,相关资源被清理,生命周期结束。
在 Vue 3 中,生命周期钩子函数允许开发者在这三个阶段的不同时间点执行代码。Vue 提供了不同的钩子函数来处理这些阶段。
5.2 Composition API 生命周期钩子
Vue 3 的 Composition API 引入了更加灵活和简洁的生命周期管理。所有生命周期钩子都通过 onXXX
的形式提供,并且它们可以直接放在 setup
函数中使用。
onMounted
onMounted
在组件挂载到 DOM 后立即调用。它对应于 Vue 2 中的 mounted
钩子。
-
使用方式:
<template><p>{{ message }}</p> </template><script setup> import { ref, onMounted } from 'vue';const message = ref('Hello, Vue 3!');onMounted(() => {console.log('Component has been mounted!'); }); </script>
-
特点:
- 仅在组件挂载之后执行一次。
onBeforeMount
onBeforeMount
在组件挂载之前调用,它对应于 Vue 2 中的 beforeMount
钩子。通常用于组件挂载之前的最后初始化。
-
使用方式:
<template><p>{{ message }}</p> </template><script setup> import { ref, onBeforeMount } from 'vue';const message = ref('Hello, Vue 3!');onBeforeMount(() => {console.log('Component is about to mount!'); }); </script>
onUpdated
onUpdated
在组件的响应式数据发生变化并重新渲染后调用。它对应于 Vue 2 中的 updated
钩子。它在视图更新后执行,可以用于处理更新后的状态。
-
使用方式:
<template><button @click="increment">Increment</button><p>{{ count }}</p> </template><script setup> import { ref, onUpdated } from 'vue';const count = ref(0);onUpdated(() => {console.log('Component has been updated!'); });const increment = () => {count.value++; }; </script>
-
特点:
- 在每次组件更新时执行。
onBeforeUnmount
onBeforeUnmount
在组件卸载之前调用,通常用于清理任务,如取消事件监听或清理定时器。
-
使用方式:
<template><p>{{ message }}</p> </template><script setup> import { ref, onBeforeUnmount } from 'vue';const message = ref('Goodbye, Vue 3!');onBeforeUnmount(() => {console.log('Component is about to unmount!'); }); </script>
-
特点:
- 在组件销毁前执行一次。
onUnmounted
onUnmounted
在组件销毁后调用,用于执行组件销毁后的清理工作。它对应于 Vue 2 中的 destroyed
钩子。
-
使用方式:
<template><p>{{ message }}</p> </template><script setup> import { ref, onUnmounted } from 'vue';const message = ref('Goodbye, Vue 3!');onUnmounted(() => {console.log('Component has been unmounted!'); }); </script>
-
特点:
- 在组件销毁后执行。
5.3 Options API 生命周期钩子
Vue 3 仍然保留了 Vue 2 的 Options API,生命周期钩子通过组件选项中的 created
、mounted
等钩子来实现。这些钩子函数适合用于传统的组件结构。
created
created
在组件实例化后调用,但此时模板和 DOM 尚未挂载。通常用于初始化数据或执行异步请求。
-
使用方式:
<template><p>{{ message }}</p> </template><script> export default {data() {return {message: 'Hello from Vue 3!'};},created() {console.log('Component is created!');} }; </script>
mounted
mounted
在组件挂载到 DOM 后调用,适合用于 DOM 操作、事件监听等。
-
使用方式:
<template><p>{{ message }}</p> </template><script> export default {data() {return {message: 'Component mounted!'};},mounted() {console.log('Component has been mounted!');} }; </script>
beforeUpdate
beforeUpdate
在数据变化之前调用,适用于需要在数据变化前执行某些操作的场景。
-
使用方式:
<template><p>{{ count }}</p><button @click="increment">Increment</button> </template><script> export default {data() {return {count: 0};},beforeUpdate() {console.log('Component will update!');},methods: {increment() {this.count++;}} }; </script>
updated
updated
在组件数据更新后调用,适用于在 DOM 更新后执行某些操作。
-
使用方式:
<template><p>{{ count }}</p><button @click="increment">Increment</button> </template><script> export default {data() {return {count: 0};},updated() {console.log('Component has been updated!');},methods: {increment() {this.count++;}} }; </script>
beforeDestroy
beforeDestroy
在组件销毁之前调用,适用于组件销毁时的清理工作。
-
使用方式:
<template><p>{{ message }}</p> </template><script> export default {data() {return {message: 'Component will be destroyed!'};},beforeDestroy() {console.log('Component is about to be destroyed!');} }; </script>
destroyed
destroyed
在组件销毁后调用,适用于执行销毁后的清理工作。
-
使用方式:
<template><p>{{ message }}</p> </template><script> export default {data() {return {message: 'Goodbye from Vue 3!'};},destroyed() {console.log('Component has been destroyed!');} }; </script>
6. Vue 3 的 Composition API
Vue 3 引入的 Composition API 是 Vue 的一个重要特性,它允许开发者通过组合式的方式组织和管理组件的逻辑,使得代码更加清晰、灵活、易于复用,特别是对于大型项目和复杂的应用程序。Composition API 提供了一种新的开发方式,与 Vue 2 中的 Options API 相比,它将组件的功能按逻辑组织,而不是按生命周期钩子、数据、方法等传统选项组织。
6.1 Composition API 简介
Composition API 提供了一组函数,允许我们在 setup
函数内组织组件的逻辑。相比于 Options API,Composition API 通过 ref
、reactive
、computed
等 API 将响应式数据、计算属性和副作用处理等功能拆分开来,使得代码逻辑更加集中和可复用。
Composition API 的优势在于:
- 更好的代码复用:可以将业务逻辑提取到自定义组合函数(composables)中,方便在多个组件之间复用。
- 更好的类型推导:由于其高度灵活性,Composition API 更容易与 TypeScript 配合使用,提供更强的类型推导和检查。
- 更清晰的逻辑分离:不同功能的逻辑不再被分散在组件的多个选项中,而是通过函数组织,易于管理和维护。
6.2 setup
函数
setup
函数是 Composition API 的核心,它在组件实例化时调用,并且是 Vue 3 中第一个执行的函数。setup
函数的作用是为组件定义响应式数据、计算属性、方法等,并返回这些数据和方法,以便模板中使用。
-
语法:
<script setup> // Composition API setup import { ref } from 'vue';const count = ref(0);const increment = () => {count.value++; }; </script>
-
返回值:
setup
函数必须返回一个对象,该对象包含组件的响应式数据、计算属性、方法等,这些返回的内容会自动暴露给模板使用。 -
注意:
setup
中的代码是在组件实例化前执行的,因此在setup
中不能访问this
。setup
函数的执行时机比created
更早。
6.3 ref
、reactive
、computed
在 Composition API 中的使用
Composition API 提供了 ref
、reactive
和 computed
三个常用的 API 来管理响应式数据和计算属性。
ref
ref
用于创建基本数据类型(如字符串、数字、布尔值等)的响应式数据。在模板中,我们可以直接访问 ref
返回的响应式对象,而在 JavaScript 中,需要通过 .value
来访问或修改其值。
-
使用示例:
<template><p>{{ count }}</p><button @click="increment">Increment</button> </template><script setup> import { ref } from 'vue';const count = ref(0);const increment = () => {count.value++; }; </script>
reactive
reactive
用于创建对象或数组的响应式数据。与 ref
不同,reactive
返回的是一个代理对象,直接修改对象的属性时不需要使用 .value
。
-
使用示例:
<template><p>{{ user.name }}</p><button @click="changeName">Change Name</button> </template><script setup> import { reactive } from 'vue';const user = reactive({ name: 'John', age: 25 });const changeName = () => {user.name = 'Jane'; }; </script>
computed
computed
用于创建计算属性,计算属性会根据依赖的数据自动更新,并且只有在相关数据发生变化时才会重新计算。计算属性会被缓存,只有当依赖的数据变化时,计算属性才会重新计算。
-
使用示例:
<template><p>{{ fullName }}</p> </template><script setup> import { ref, computed } from 'vue';const firstName = ref('John'); const lastName = ref('Doe');const fullName = computed(() => {return `${firstName.value} ${lastName.value}`; }); </script>
6.4 watch
和 watchEffect
watch
和 watchEffect
都用于响应式数据变化时执行副作用操作,但它们之间有一些细微的区别。
watch
watch
用于监听一个或多个响应式数据的变化,并在数据变化时执行回调函数。watch
适用于需要执行复杂逻辑、异步操作或副作用的场景。
-
语法:
watch(source, callback, options);
source
:要监听的响应式数据或计算属性。callback
:当source
发生变化时执行的回调函数。options
:可选的配置选项,如immediate
(立即执行回调)和deep
(深度观察对象)。
-
使用示例:
<template><p>{{ count }}</p><button @click="increment">Increment</button> </template><script setup> import { ref, watch } from 'vue';const count = ref(0);const increment = () => {count.value++; };watch(count, (newValue, oldValue) => {console.log(`Count changed from ${oldValue} to ${newValue}`); }); </script>
watchEffect
watchEffect
用于立即运行回调并追踪其内部使用的所有响应式数据。当内部的任何响应式数据发生变化时,watchEffect
会重新执行回调函数。watchEffect
不需要明确指定要观察的数据,它会自动追踪函数内部访问的响应式数据。
-
使用示例:
<template><p>{{ count }}</p><button @click="increment">Increment</button> </template><script setup> import { ref, watchEffect } from 'vue';const count = ref(0);watchEffect(() => {console.log(`Count is now: ${count.value}`); });const increment = () => {count.value++; }; </script>
6.5 自定义组合函数(composables)
自定义组合函数(composables)是 Vue 3 中 Composition API 的一部分,它允许我们将逻辑提取到可复用的函数中,从而提高代码的可复用性和清晰度。组合函数通常以 use
开头,遵循一定的命名约定,以便其他开发者能够理解其用途。
自定义组合函数的使用
-
创建组合函数:
import { ref } from 'vue';export function useCounter() {const count = ref(0);const increment = () => {count.value++;};return { count, increment }; }
-
在组件中使用组合函数:
<template><p>{{ count }}</p><button @click="increment">Increment</button> </template><script setup> import { useCounter } from './composables/useCounter';const { count, increment } = useCounter(); </script>
自定义组合函数的优势
- 逻辑复用:将复杂的逻辑提取到组合函数中,可以在多个组件之间复用。
- 代码结构清晰:可以按功能模块拆分业务逻辑,而不是将所有逻辑混合在
data
、methods
和生命周期钩子中。
7. Vue 3 路由(Vue Router)
Vue Router 是 Vue.js 官方的路由库,它用于管理 Vue 应用中的页面导航。Vue Router 使得开发者可以在 Vue 应用中实现复杂的页面跳转、动态加载和导航控制。Vue Router 与 Vue 3 完美集成,为单页面应用(SPA)提供路由管理。
7.1 安装与配置 Vue Router
在 Vue 3 中,Vue Router 是一个独立的库,使用之前需要先安装并配置。
安装 Vue Router
-
通过 npm 安装 Vue Router:
npm install vue-router@4
-
配置 Vue Router:在
src
目录下创建router
文件夹,并在其中创建index.js
文件。
-
src/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;
-
在主入口文件
src/main.js
中导入并使用 Vue Router:src/main.js
:import { createApp } from 'vue'; import App from './App.vue'; import router from './router';createApp(App).use(router).mount('#app');
配置 Vue Router 历史模式
Vue Router 支持两种历史模式:
-
Hash 模式:
#
后缀模式(例如/#/home
),不需要服务器支持,默认情况下使用该模式。 -
History 模式:使用 HTML5 的
history.pushState
API,创建干净的 URL 路径(例如/home
),需要服务器配置支持。 -
在上述配置中,
createWebHistory
用于设置历史模式。如果使用 hash 模式,可以使用createWebHashHistory
。
7.2 路由的基本使用
路由的基本功能是将 URL 映射到组件。Vue Router 会根据 URL 的变化,渲染对应的组件。
配置路由映射
在 router/index.js
中配置路由,并在模板中使用 <router-view>
渲染路由的内容。
-
src/router/index.js
(继续使用之前的配置):const routes = [{ path: '/', component: Home },{ path: '/about', component: About } ];
-
src/App.vue
:<template><div><router-link to="/">Home</router-link><router-link to="/about">About</router-link><router-view></router-view> <!-- 渲染匹配的组件 --></div> </template>
-
src/views/Home.vue
:<template><div><h1>Home Page</h1></div> </template><script setup> // 可以在这里写 Home 页面的相关逻辑 </script>
-
src/views/About.vue
:<template><div><h1>About Page</h1></div> </template><script setup> // 可以在这里写 About 页面的相关逻辑 </script>
使用 <router-link>
进行导航
<router-link>
用于创建可以点击的链接,点击链接后会触发路由导航。
-
语法:
<router-link to="/path">Link Text</router-link>
7.3 动态路由与路由参数
动态路由允许根据 URL 中的变化来渲染不同的组件,常用于需要传递参数的场景,例如用户的个人信息页。
配置动态路由
通过在路径中使用 :param
来定义动态路由参数。例如,展示用户信息时,可以使用 :id
来动态绑定用户的 ID。
-
src/router/index.js
:const routes = [{ path: '/user/:id', component: User }, ];
-
src/views/User.vue
:<template><div><h1>User ID: {{ id }}</h1></div> </template><script setup> import { useRoute } from 'vue-router';const route = useRoute(); const id = route.params.id; // 获取动态路由参数 </script>
使用动态路由
你可以通过 <router-link>
将动态参数传递给路由:
-
src/App.vue
:<router-link :to="'/user/' + userId">Go to User</router-link>
7.4 嵌套路由与命名视图
嵌套路由
嵌套路由允许你在一个路由组件内配置多个子路由,并在同一个页面中渲染多个视图。子路由的组件将会在父组件的 <router-view>
中渲染。
-
父路由配置:
const routes = [{path: '/parent',component: Parent,children: [{ path: 'child', component: Child }]} ];
-
父组件(
Parent.vue
):<template><h1>Parent Component</h1><router-view></router-view> <!-- 子路由渲染位置 --> </template>
-
子组件(
Child.vue
):<template><div><h2>Child Component</h2></div> </template>
命名视图
命名视图允许你在同一个页面中渲染多个 <router-view>
,并为它们指定不同的视图内容。
-
父路由配置:
const routes = [{path: '/dashboard',components: {default: Dashboard,sidebar: Sidebar}} ];
-
父组件(
Dashboard.vue
):<template><router-view></router-view> <!-- 默认视图 --><router-view name="sidebar"></router-view> <!-- 命名视图 --> </template>
-
Sidebar 组件(
Sidebar.vue
):<template><div>Sidebar Content</div> </template>
7.5 路由守卫与导航钩子
路由守卫可以在路由跳转之前或之后执行特定的操作,常用于权限控制、数据预加载等场景。
全局路由守卫
全局守卫应用于所有路由,在路由跳转前后都会被触发。
-
使用方式:
router.beforeEach((to, from, next) => {console.log('Global beforeEach guard');next(); // 必须调用 next() 来继续路由导航 });router.afterEach((to, from) => {console.log('Global afterEach guard'); });
路由独享守卫
路由独享守卫仅在某个特定路由的导航过程中触发,适用于特定页面的权限控制等操作。
-
使用方式:
const routes = [{path: '/protected',component: Protected,beforeEnter: (to, from, next) => {const isAuthenticated = checkAuth();if (isAuthenticated) {next();} else {next('/login');}}} ];
路由钩子
路由钩子允许你在路由生命周期的不同阶段执行代码。例如,beforeRouteEnter
在路由进入前被调用,beforeRouteLeave
在离开当前路由时被调用。
-
使用方式:
<script setup> import { onBeforeRouteLeave } from 'vue-router';onBeforeRouteLeave((to, from) => {console.log('Leaving route'); }); </script>
8. Vue 3 状态管理(Vuex)
Vuex 是 Vue.js 官方的状态管理库,用于管理和集中存储应用的所有组件的状态。Vuex 适用于 Vue 3 中单页面应用(SPA)的大型应用或复杂的状态管理。Vuex 提供了一种集中式的状态管理模式,使得组件间的状态共享变得更加简单和高效。
8.1 Vuex 的基本概念
Vuex 的核心概念包括:
- State:应用的状态数据,是 Vuex 中存储的唯一数据源。所有组件都可以访问这些数据。
- Getters:Vuex 的计算属性,用于从 State 中派生出数据。Getters 类似于组件中的计算属性,但它们的作用范围是整个应用。
- Mutations:同步操作状态的唯一方式。Mutations 用于直接修改 State 中的数据,并且必须是同步函数。
- Actions:处理异步操作的地方,可以在 Actions 中调用 Mutations。Actions 不能直接修改 State,而是通过调用 Mutations 来修改数据。
- Modules:Vuex 支持将状态、mutations、actions 和 getters 拆分成模块,从而让状态管理更具可维护性,特别是在大型应用中。
8.2 安装与配置 Vuex
- 安装 Vuex
在 Vue 3 中,Vuex 4 是与 Vue 3 兼容的版本,需要通过 npm 安装:
npm install vuex@4
- 配置 Vuex
在 src
目录下创建 store
文件夹,并在其中创建 index.js
文件来配置 Vuex。
-
src/store/index.js
:import { createStore } from 'vuex';const store = createStore({state() {return {count: 0};},mutations: {increment(state) {state.count++;}},actions: {incrementAsync({ commit }) {setTimeout(() => {commit('increment');}, 1000);}},getters: {getCount(state) {return state.count;}} });export default store;
- 在主应用中使用 Vuex
在 src/main.js
中导入并注册 Vuex store。
-
src/main.js
:import { createApp } from 'vue'; import App from './App.vue'; import store from './store'; // 导入 storecreateApp(App).use(store).mount('#app');
8.3 State、Getters、Mutations、Actions 的使用
State
State 是 Vuex 中的核心数据存储,用于存放应用的全局状态。任何组件都可以访问 State 中的数据。
-
使用示例:
<template><p>{{ count }}</p> </template><script setup> import { useStore } from 'vuex';const store = useStore(); const count = computed(() => store.state.count); // 访问 Vuex state </script>
Getters
Getters 类似于计算属性,它是对 State 中数据的派生和过滤。Getters 可以用于将多个 State 数据组合,或根据特定条件返回一个计算结果。
-
使用示例:
<template><p>{{ getCount }}</p> </template><script setup> import { useStore } from 'vuex';const store = useStore(); const getCount = computed(() => store.getters.getCount); // 访问 Vuex getters </script>
Mutations
Mutations 用于修改 State 中的数据。它们是同步操作,必须通过 commit
提交,不能在 mutations 中执行异步操作。
-
使用示例:
<template><button @click="increment">Increment</button> </template><script setup> import { useStore } from 'vuex';const store = useStore(); const increment = () => {store.commit('increment'); // 调用 Vuex mutation }; </script>
Actions
Actions 用于处理异步操作或复杂的逻辑,并且可以在 action 中通过 commit
调用 mutations。Actions 可以使用 dispatch
在组件中调用。
-
使用示例:
<template><button @click="incrementAsync">Increment Async</button> </template><script setup> import { useStore } from 'vuex';const store = useStore(); const incrementAsync = () => {store.dispatch('incrementAsync'); // 调用 Vuex action }; </script>
8.4 Vuex 模块化
Vuex 提供了模块化的机制,使得在大型应用中,能够将 store 分为多个模块,每个模块拥有自己的 state、mutations、actions 和 getters。
-
模块化配置:
import { createStore } from 'vuex';const store = createStore({modules: {counter: {state() {return {count: 0};},mutations: {increment(state) {state.count++;}},actions: {incrementAsync({ commit }) {setTimeout(() => {commit('increment');}, 1000);}},getters: {getCount(state) {return state.count;}}}} });export default store;
-
访问模块中的数据:
<template><p>{{ count }}</p><button @click="incrementAsync">Increment Async</button> </template><script setup> import { useStore } from 'vuex';const store = useStore(); const count = computed(() => store.state.counter.count); // 访问模块中的 state const incrementAsync = () => {store.dispatch('counter/incrementAsync'); // 调用模块中的 action }; </script>
8.5 与组件的结合使用
将 Vuex 与 Vue 组件结合使用时,通常会使用 useStore
钩子来访问 store。在组件中,通过 store.state
访问状态,通过 store.commit
提交 mutations,通过 store.dispatch
调用 actions,或者通过 store.getters
获取计算结果。
在组件中访问 State 和 Getters
-
访问 State:
<template><p>{{ count }}</p> </template><script setup> import { useStore } from 'vuex';const store = useStore(); const count = computed(() => store.state.count); // 访问 Vuex state </script>
-
访问 Getters:
<template><p>{{ getCount }}</p> </template><script setup> import { useStore } from 'vuex';const store = useStore(); const getCount = computed(() => store.getters.getCount); // 访问 Vuex getters </script>
在组件中提交 Mutations 和调用 Actions
-
提交 Mutations:
<template><button @click="increment">Increment</button> </template><script setup> import { useStore } from 'vuex';const store = useStore(); const increment = () => {store.commit('increment'); // 提交 Vuex mutation }; </script>
-
调用 Actions:
<template><button @click="incrementAsync">Increment Async</button> </template><script setup> import { useStore } from 'vuex';const store = useStore(); const incrementAsync = () => {store.dispatch('incrementAsync'); // 调用 Vuex action }; </script>
9. Vue 3 异常处理与调试
在开发复杂的 Vue 3 应用时,异常处理和调试是不可忽视的重要部分。Vue 提供了多种方法来捕获和处理错误,同时,调试工具和性能分析也能帮助我们高效地找到和解决问题。下面将详细介绍如何在 Vue 3 中进行异常处理、调试和性能分析。
9.1 错误捕获与处理
Vue 3 提供了多种方式来捕获和处理错误,包括全局错误捕获、组件级错误捕获和异步错误处理。
全局错误捕获
Vue 3 允许你通过 config.errorHandler
全局配置来捕获所有组件中的错误。这是在应用级别处理错误的有效方式,适用于那些未能在组件中处理的错误。
-
使用示例:
const app = createApp(App);app.config.errorHandler = (err, vm, info) => {console.error('Caught an error:', err);console.log('Component:', vm);console.log('Info:', info); };app.mount('#app');
-
参数解释:
err
: 捕获到的错误对象。vm
: 错误发生的组件实例。info
: 额外的信息(例如 Vue 生命周期钩子)。
组件级错误捕获
Vue 3 也支持组件级的错误捕获,允许你在单个组件内使用 errorCaptured
生命周期钩子来捕获错误。
-
使用示例:
<template><div><button @click="throwError">Throw Error</button></div> </template><script setup> const throwError = () => {throw new Error('An error occurred'); };defineOptions({errorCaptured(err, vm, info) {console.error('Error captured:', err);console.log('Component:', vm);console.log('Info:', info);return false; // 如果返回 false,则继续向上冒泡错误} }); </script>
异步错误捕获
Vue 3 对于异步操作中的错误也提供了捕获机制,可以通过 try-catch
语句来捕获异步函数中的错误。
-
使用示例:
<template><button @click="fetchData">Fetch Data</button> </template><script setup> const fetchData = async () => {try {const response = await fetch('https://api.example.com/data');const data = await response.json();console.log(data);} catch (error) {console.error('Error fetching data:', error);} }; </script>
9.2 使用 Vue DevTools 调试
Vue DevTools 是 Vue 官方提供的浏览器插件,用于调试 Vue 应用。它允许我们查看组件树、查看响应式数据、调试 Vuex 状态以及检测性能瓶颈等。
安装 Vue DevTools
- 在浏览器中访问 Vue DevTools 官方网站,并根据你的浏览器下载并安装对应的插件。
- 安装完成后,打开开发者工具(通常按
F12
或Ctrl + Shift + I
),在浏览器的插件栏中找到 Vue DevTools 面板。
使用 Vue DevTools
- 查看组件树:在 Vue DevTools 中,可以看到整个 Vue 应用的组件树,查看每个组件的状态和属性。
- 查看响应式数据:通过
Vuex
和Reactive
标签,你可以查看 Vuex 中的状态,或直接查看组件的响应式数据。 - 调试 Vuex:Vue DevTools 提供了 Vuex 状态管理的调试功能,允许你查看状态、mutations 和 actions,并且可以跳过或回溯操作。
- 性能分析:在 Vue DevTools 中的 “性能” 面板,可以查看每次组件渲染的时间,帮助你定位性能瓶颈。
调试生命周期钩子
Vue DevTools 也可以查看各个组件的生命周期钩子,帮助你分析应用的生命周期流程。你可以看到组件何时创建、更新、销毁等操作。
9.3 日志记录与性能分析
在开发过程中,记录日志和分析性能是非常重要的。Vue 提供了一些内建工具以及与第三方库的集成,帮助开发者进行日志记录和性能分析。
日志记录
Vue 3 的 console
方法可以结合应用的生命周期进行日志记录,帮助开发者追踪应用的状态和行为。
-
使用示例:
<template><button @click="increment">Increment</button> </template><script setup> import { ref } from 'vue';const count = ref(0);const increment = () => {count.value++;console.log('Current count:', count.value); // 日志记录 }; </script>
在实际应用中,开发者可以使用 console.log
、console.warn
和 console.error
方法来打印不同类型的日志。对于更复杂的应用,可以使用更专业的日志库,如 loglevel
或 winston
来管理日志的级别和输出。
性能分析
Vue 3 提供了内建的性能工具,可以帮助开发者分析应用的性能瓶颈。例如,使用浏览器的开发者工具进行性能分析,或借助 Vue DevTools
来查看组件的渲染时间。
-
性能分析工具:
- 浏览器开发者工具:现代浏览器提供了强大的性能分析工具,你可以使用它们来分析页面加载时间、网络请求和渲染性能等。
- Vue DevTools:如前所述,Vue DevTools 中的“性能”面板可以帮助开发者查看组件渲染时间、性能瓶颈以及不必要的组件更新。
performance.now()
:通过原生 JavaScript 的performance.now()
方法,你可以精确地计算代码执行的时间,并进行性能优化。
-
使用示例:
const start = performance.now();// 一些操作const end = performance.now(); console.log(`Operation took ${end - start} milliseconds`);
通过 performance.now()
,你可以精准地衡量操作的执行时间,从而找到可能的性能瓶颈。
第三方性能分析工具
对于更深入的性能分析,你可以集成第三方性能监控工具,如:
- Lighthouse:Lighthouse 是由 Google 提供的开源工具,用于分析 Web 应用的性能、可访问性和 SEO 等方面。
- Sentry:Sentry 是一个错误监控工具,可以帮助开发者捕获应用中的错误并跟踪性能问题。
- New Relic:New Relic 提供了全面的应用性能监控工具,可以帮助开发者跟踪应用的性能瓶颈和优化点。
10. Vue 3 的其他功能
Vue 3 提供了一些额外的功能,使得开发者能够构建更加灵活和高效的应用。以下是 Vue 3 中的一些进阶特性,包括过渡与动画、Teleport、Suspense、异步组件以及自定义指令等。
10.1 过渡与动画
Vue 3 提供了强大的过渡和动画系统,可以让你为元素的插入、移除和更新添加动画效果,提升应用的视觉体验。
基本用法
Vue 使用 <transition>
标签来包裹需要添加过渡效果的元素。当元素的状态发生变化时,Vue 会自动应用指定的过渡效果。
-
语法:
<transition name="fade"><div v-if="isVisible">Hello, Vue!</div> </transition>
过渡类名
Vue 提供了默认的过渡类名,这些类名会在元素的生命周期内自动切换,具体包括:
v-enter
: 元素插入前v-enter-active
: 元素插入时v-enter-to
: 元素插入后v-leave
: 元素移除前v-leave-active
: 元素移除时v-leave-to
: 元素移除后
你可以通过 CSS 自定义这些类来实现动画效果。
-
CSS 过渡效果:
<style> .fade-enter-active, .fade-leave-active {transition: opacity 1s; } .fade-enter, .fade-leave-to {opacity: 0; } </style>
动画与 JavaScript
如果你需要更复杂的动画效果,可以使用 JavaScript 钩子来控制过渡过程。
-
JavaScript 钩子:
<transition @before-enter="beforeEnter" @enter="enter" @leave="leave"><div v-if="isVisible">Hello, Vue!</div> </transition><script setup> const beforeEnter = (el) => {console.log('Before Enter'); };const enter = (el, done) => {el.offsetHeight; // Trigger reflow to enable transitiondone(); };const leave = (el, done) => {console.log('Leaving');done(); }; </script>
10.2 Vue 3 的 Teleport(传送门)
Vue 3 引入了 Teleport
组件,允许你将一个组件的内容渲染到 DOM 中的另一个位置,而不是其父组件的默认位置。这在需要将元素渲染到页面其他位置时非常有用,比如模态框、弹出层等。
-
使用示例:
<template><Teleport to="body"><div class="modal">This is a modal</div></Teleport> </template><style> .modal {position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);background: white;padding: 20px;border-radius: 8px; } </style>
-
to
属性:
to
属性用于指定要渲染到的目标元素,可以是一个 CSS 选择器字符串或一个 DOM 元素。 -
应用场景:
- 模态框:将模态框渲染到
body
元素中,而不是当前的父组件。 - 通知:可以将通知组件渲染到页面的顶部,而不是原本的组件树结构中。
- 模态框:将模态框渲染到
10.3 Vue 3 的 Suspense
和异步组件
Suspense
是 Vue 3 中用于处理异步组件加载的一种机制,它使得组件可以在异步加载数据或组件时显示加载状态。Suspense
组件允许你指定一个等待的状态,直到所有子组件加载完成后再渲染它们。
使用 Suspense
-
语法:
<Suspense><template #default><AsyncComponent /></template><template #fallback><p>Loading...</p></template> </Suspense>
-
解释:
default
插槽用于渲染异步组件。fallback
插槽用于渲染加载过程中的状态。
异步组件
在 Vue 3 中,异步组件是按需加载的组件。这些组件只有在真正需要时才会被加载,从而减少初始加载时的资源消耗。
-
定义异步组件:
const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));
-
使用异步组件:
<template><AsyncComponent /> </template><script setup> import { defineAsyncComponent } from 'vue';const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue')); </script>
-
异步组件的加载状态:
使用Suspense
可以为异步组件添加加载指示器,并在异步组件加载完成后渲染。
10.4 Vue 3 中的自定义指令
Vue 3 允许开发者创建自定义指令,以扩展 Vue 的功能。指令通常用于操作 DOM,例如处理样式、添加事件监听器或进行 DOM 操作等。
创建自定义指令
自定义指令的创建过程非常简单,你只需要定义一个对象并提供 beforeMount
、mounted
、updated
、unmounted
等生命周期钩子来操作 DOM。
-
使用示例:
const vFocus = {beforeMount(el) {el.focus();} };const app = createApp(App); app.directive('focus', vFocus);
-
组件中使用自定义指令:
<template><input v-focus /> </template>
自定义指令的钩子
Vue 3 的自定义指令可以使用以下生命周期钩子:
beforeMount
:指令绑定到元素时调用。mounted
:指令已经绑定到元素并插入 DOM 时调用。updated
:指令所在的元素及其子元素更新时调用。beforeUnmount
:指令解绑前调用。unmounted
:指令解绑后调用。
自定义指令的参数
自定义指令支持动态参数、修饰符和钩子中的传入值。你可以在指令上使用动态参数和修饰符来处理不同的 DOM 操作。
-
使用参数和修饰符:
<template><p v-color:blue>Change my color!</p> </template><script setup> app.directive('color', {beforeMount(el, binding) {el.style.color = binding.arg || 'red'; // 使用动态参数设置颜色} }); </script>
11. Vue 3 项目实战
在学习 Vue 3 的过程中,实际项目的开发是一个重要的步骤,能够帮助你巩固知识并掌握更高阶的技巧。下面我们将展示如何使用 Vue 3 来开发一些常见的应用,包括一个待办事项应用、一个博客网站,以及如何集成一些常见的第三方库和插件(如 Vue Router、Vuex 和 Axios)来增强功能。
11.1 开发一个待办事项应用
待办事项应用是一个非常经典的项目,适合初学者进行练习。通过这个项目,你将学习如何管理数据、处理用户输入、进行数据绑定以及实现增、删、改、查功能。
1.1.1 项目结构
项目可以分为以下几个部分:
- 一个输入框,用于输入待办事项。
- 一个列表,用于展示所有待办事项。
- 一个删除按钮,用于删除已完成的待办事项。
1.1.2 实现代码
<template><div><h1>待办事项</h1><input v-model="newTask" @keyup.enter="addTask" placeholder="输入新的任务" /><ul><li v-for="(task, index) in tasks" :key="index"><span :style="{ textDecoration: task.completed ? 'line-through' : '' }">{{ task.text }}</span><button @click="toggleCompletion(index)">完成</button><button @click="removeTask(index)">删除</button></li></ul></div>
</template><script setup>
import { ref } from 'vue';const newTask = ref('');
const tasks = ref([]);const addTask = () => {if (newTask.value.trim()) {tasks.value.push({ text: newTask.value, completed: false });newTask.value = '';}
};const toggleCompletion = (index) => {tasks.value[index].completed = !tasks.value[index].completed;
};const removeTask = (index) => {tasks.value.splice(index, 1);
};
</script><style scoped>
input {margin: 10px;padding: 5px;font-size: 16px;
}button {margin-left: 10px;
}
</style>
1.1.3 代码解析
- 使用
v-model
来实现双向数据绑定,用户输入的内容会自动更新到newTask
中。 - 通过
v-for
渲染任务列表,每个任务有一个按钮用来标记完成或删除任务。 toggleCompletion
方法用来切换任务的完成状态,removeTask
方法用来删除任务。
11.2 开发一个博客网站(或类似项目)
博客网站通常需要实现用户认证、文章展示、评论功能、以及后台管理系统等。在 Vue 3 中,你可以使用 Vue Router 来实现页面导航,使用 Vuex 来管理状态,并且通过 Axios 来进行数据请求。
1.2.1 项目结构
一个简单的博客应用包括以下页面和功能:
- 首页:展示所有文章。
- 文章详情页:查看某篇文章的内容。
- 登录/注册页面:用户可以登录或注册。
- 后台管理页面:用于创建、编辑和删除文章。
1.2.2 安装依赖
首先,使用 Vue CLI 或 Vite 创建项目并安装必要的依赖:
npm install vue-router@4 vuex@4 axios
1.2.3 配置 Vue Router
import { createRouter, createWebHistory } from 'vue-router';
import HomePage from './components/HomePage.vue';
import ArticlePage from './components/ArticlePage.vue';
import LoginPage from './components/LoginPage.vue';const routes = [{ path: '/', component: HomePage },{ path: '/article/:id', component: ArticlePage },{ path: '/login', component: LoginPage }
];const router = createRouter({history: createWebHistory(),routes
});export default router;
1.2.4 配置 Vuex
import { createStore } from 'vuex';const store = createStore({state() {return {articles: [],user: null};},mutations: {setArticles(state, articles) {state.articles = articles;},setUser(state, user) {state.user = user;}},actions: {fetchArticles({ commit }) {axios.get('/api/articles').then((response) => {commit('setArticles', response.data);});},login({ commit }, user) {commit('setUser', user);}}
});export default store;
1.2.5 使用 Axios 获取数据
<template><div><h1>博客文章</h1><ul><li v-for="article in articles" :key="article.id"><router-link :to="'/article/' + article.id">{{ article.title }}</router-link></li></ul></div>
</template><script setup>
import { useStore } from 'vuex';
import { onMounted } from 'vue';const store = useStore();
const articles = computed(() => store.state.articles);onMounted(() => {store.dispatch('fetchArticles');
});
</script>
1.2.6 代码解析
- 使用
Vue Router
来管理页面的跳转。 - 使用
Vuex
来存储应用的全局状态(如文章列表和用户信息)。 - 使用
Axios
来从后台获取文章数据,并在组件加载时通过onMounted
调用 Vuex action 来获取数据。
11.3 集成第三方库与插件(如 Vue Router、Vuex、Axios)
在 Vue 3 中,集成第三方库和插件非常简单。以下是常见的集成方式。
1.3.1 使用 Vue Router 实现页面导航
-
安装 Vue Router:
npm install vue-router@4
-
创建路由并在
main.js
中配置:import { createApp } from 'vue'; import App from './App.vue'; import router from './router';createApp(App).use(router).mount('#app');
1.3.2 使用 Vuex 进行状态管理
-
安装 Vuex:
npm install vuex@4
-
创建并配置 store:
import { createStore } from 'vuex';const store = createStore({state() {return {user: null};},mutations: {setUser(state, user) {state.user = user;}} });const app = createApp(App); app.use(store);
1.3.3 使用 Axios 进行 API 请求
-
安装 Axios:
npm install axios
-
发送 HTTP 请求:
import axios from 'axios';axios.get('https://api.example.com/data').then(response => {console.log(response.data);}).catch(error => {console.error('API request failed:', error);});
通过这些第三方库和插件,你可以轻松地构建更为复杂的应用,整合 Vue Router、Vuex 和 Axios 后,你的 Vue 3 项目将变得更加高效、功能齐全,能够处理更复杂的应用需求。
12. Vue 3 性能优化与部署
在开发复杂的 Vue 3 应用时,性能优化和部署是非常重要的环节。良好的性能可以提升用户体验,确保应用在不同设备上流畅运行。与此同时,部署到生产环境时需要考虑如何有效地进行打包、构建和优化。
12.1 性能优化策略
Vue 3 提供了多种优化策略,帮助我们提升应用的加载速度、响应速度和整体性能。以下是一些常见的优化技术:
12.1.1 懒加载与代码分割
懒加载(Lazy Loading) 是一种按需加载的技术,可以将应用程序的代码分成多个较小的块,只在需要时加载。这有助于减少初始加载的时间。
-
懒加载组件:
Vue Router 和 Vue 3 支持异步组件,允许在需要时加载特定的组件,而不是在应用启动时加载所有组件。const MyComponent = defineAsyncComponent(() => import('./components/MyComponent.vue'));
-
路由懒加载:
在 Vue Router 中,我们可以通过懒加载来按需加载页面组件,这样可以减少应用的初始包大小。const routes = [{path: '/home',component: () => import('./views/Home.vue') // 异步加载 Home 组件} ];
-
配置 Webpack 进行代码分割:
Webpack 会自动根据import()
的动态导入来分割代码。你可以在组件中按需引入其他模块,Webpack 会为这些模块生成单独的包。// 动态导入 const module = () => import('./module');
12.1.2 虚拟列表
虚拟列表(Virtual Scrolling)是一种优化长列表渲染性能的技术。它通过只渲染视口范围内的元素,而不是一次性渲染整个列表,从而大大减少了 DOM 节点的数量,提升渲染性能。
-
虚拟滚动实现:
使用虚拟滚动时,我们只渲染视口可见部分的内容,随着用户滚动,新的内容才会被渲染到视图中。可以使用第三方库来实现虚拟列表,例如 vue-virtual-scroller。
npm install vue-virtual-scroller
然后在应用中使用:
<template><virtual-scroller :items="items" :item-height="50"><template v-slot="{ item }"><div>{{ item }}</div></template></virtual-scroller> </template><script setup> import { ref } from 'vue'; import { VirtualScroller } from 'vue-virtual-scroller';const items = ref(Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`)); </script>
12.2 项目构建与打包
Vue 3 项目通常使用现代的构建工具进行打包和优化,最常用的是 Vite 和 Webpack。
12.2.1 使用 Vite 构建与打包
Vite 是一个快速的构建工具,基于原生 ES 模块,在开发时非常高效。它提供了开箱即用的代码分割、热模块替换(HMR)等功能,非常适合用于 Vue 3 项目的构建和打包。
-
安装 Vite:
使用 Vue 3 和 Vite 创建项目:npm create vite@latest vue-app --template vue
-
构建项目:
在package.json
文件中配置 Vite 的构建命令:{"scripts": {"build": "vite build"} }
-
优化配置:
Vite 默认支持代码分割和懒加载,但你可以进一步优化构建,例如配置更精细的分包策略,减少 bundle 大小。// vite.config.js export default {build: {rollupOptions: {output: {manualChunks(id) {if (id.includes('node_modules')) {return 'vendor';}}}}} };
12.2.2 使用 Webpack 构建与打包
Webpack 是一个功能强大的构建工具,广泛用于 Vue 2 项目,但它也支持 Vue 3 项目。Webpack 提供了很多插件和功能,用于优化构建和打包过程。
-
安装 Webpack:
使用 Vue CLI 创建项目时,Webpack 会自动安装并配置。vue create my-project
-
打包项目:
在package.json
文件中配置 Webpack 的构建命令:{"scripts": {"build": "webpack --config webpack.config.js"} }
-
Webpack 优化:
Webpack 提供了多种优化选项,例如:splitChunks
:用于将第三方库从应用代码中分离,减少主包的大小。Tree Shaking
:移除未使用的代码。Minification
:压缩代码,减小文件体积。
// webpack.config.js module.exports = {optimization: {splitChunks: {chunks: 'all'}} };
12.3 部署到生产环境
在 Vue 3 项目完成构建和打包后,接下来就是将应用部署到生产环境。Vue 3 项目通常部署到静态文件服务器,或通过云服务(如 Netlify、Vercel、GitHub Pages)进行托管。
12.3.1 打包 Vue 3 项目
无论使用 Vite 还是 Webpack,打包后的文件会生成在 dist/
目录下。你可以使用以下命令来构建项目:
-
Vite:
npm run build
-
Webpack:
npm run build
12.3.2 部署到静态服务器
将 dist/
目录中的内容上传到静态文件托管服务,常见的静态服务器包括:
- Nginx:部署到 Nginx 服务器,设置静态文件托管。
- Apache:部署到 Apache 服务器,配置
.htaccess
以支持 Vue Router 的 history 模式。
12.3.3 部署到云服务
现代的云服务提供了一键部署的功能,使得部署变得更加简单。常见的云服务包括:
- Netlify:直接将 GitHub 仓库与 Netlify 连接,自动构建并部署。
- Vercel:类似于 Netlify,Vercel 支持从 GitHub 或 GitLab 自动部署。
- GitHub Pages:适用于小型项目,将 Vue 项目部署到 GitHub Pages。
12.3.4 配置 CDN
对于大型应用,可以通过 CDN(内容分发网络)优化静态资源的加载速度。将 JS、CSS 和图片等静态文件上传到 CDN,可以大幅提高全球用户的访问速度。
12.3.5 环境变量与配置
在部署时,确保使用正确的环境变量来控制生产和开发环境的不同配置。Vue CLI 和 Vite 都支持环境变量管理。
-
Vite 配置环境变量:
在.env
文件中设置环境变量:VITE_API_URL=https://api.example.com
-
使用环境变量:
const apiUrl = import.meta.env.VITE_API_URL;
13. Vue 3 与 TypeScript
Vue 3 从一开始就全面支持 TypeScript,允许开发者在开发过程中利用 TypeScript 的类型检查、类型推导和类型安全等功能,提升开发效率和代码质量。在 Vue 3 中使用 TypeScript,不仅能获得更强大的类型支持,还能增强代码的可维护性和可扩展性。
13.1 Vue 3 中如何使用 TypeScript
在 Vue 3 中,使用 TypeScript 非常简单。你只需要通过 Vue CLI 或 Vite 创建一个 TypeScript 项目,或者手动配置 TypeScript。
使用 Vue CLI 创建 TypeScript 项目
-
安装 Vue CLI(如果尚未安装):
npm install -g @vue/cli
-
创建 Vue 项目并选择 TypeScript:
vue create my-vue-project
在提示中选择 TypeScript 模板。
-
项目创建完成后,你会看到项目中已经包含了 TypeScript 配置文件 (
tsconfig.json
),并且 Vue 组件默认是.vue
文件,其中的<script>
标签会自动识别为 TypeScript。
使用 Vite 创建 TypeScript 项目
Vite 是一个现代的构建工具,特别适合 Vue 3 项目,并且内置对 TypeScript 的支持。
-
创建项目并选择 Vue 3 + TypeScript 模板:
npm create vite@latest my-vue-project --template vue-ts
-
安装依赖:
cd my-vue-project npm install
-
项目结构会自动配置好 TypeScript 和 Vue 3,接下来你就可以开始编写 TypeScript 代码。
手动配置 TypeScript
如果你已经有一个 Vue 3 项目,并且想要添加 TypeScript 支持,你需要手动配置以下几个步骤:
-
安装 TypeScript 和相关类型:
npm install typescript @types/node
-
在项目根目录下创建一个
tsconfig.json
文件,配置 TypeScript:{"compilerOptions": {"target": "esnext","module": "esnext","moduleResolution": "node","jsx": "preserve","strict": true,"esModuleInterop": true,"skipLibCheck": true,"forceConsistentCasingInFileNames": true,"noImplicitAny": true,"resolveJsonModule": true,"isolatedModules": true,"allowSyntheticDefaultImports": true,"baseUrl": "./","paths": {"@/*": ["src/*"]}},"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],"exclude": ["node_modules"] }
-
修改
.vue
文件中的<script>
标签,指定为lang="ts"
:<template><div>{{ message }}</div> </template><script lang="ts"> import { defineComponent } from 'vue';export default defineComponent({data() {return {message: 'Hello, TypeScript in Vue 3!'};} }); </script>
13.2 类型推导与类型检查
TypeScript 的类型推导和类型检查功能可以帮助开发者捕获潜在的错误和问题。Vue 3 与 TypeScript 的结合使得我们能够在组件中享受到类型安全和智能提示的好处。
组件中的类型推导
-
响应式数据的类型推导:
在 Vue 3 中使用ref
和reactive
创建响应式数据时,TypeScript 会自动推导类型。例如:import { ref } from 'vue';const count = ref(0); // TypeScript 会推导出 count 的类型是 `Ref<number>`
-
计算属性的类型推导:
Vue 3 的computed
函数同样支持类型推导,它可以自动推导计算结果的类型:import { ref, computed } from 'vue';const count = ref(0); const doubleCount = computed(() => count.value * 2); // doubleCount 的类型是 `ComputedRef<number>`
类型检查
TypeScript 会检查数据类型和方法类型,帮助我们在编写代码时避免类型错误。例如,Vue 3 会对方法中的参数进行类型检查:
import { ref } from 'vue';const message = ref('Hello, TypeScript!');const updateMessage = (newMessage: string) => {message.value = newMessage; // TypeScript 会检查 newMessage 是否为 string 类型
};
13.3 类型声明文件与接口
在 Vue 3 和 TypeScript 中,类型声明文件和接口非常重要,它们帮助我们更好地定义组件、props、state 和方法的类型。
使用接口定义组件的 props
类型
你可以通过接口定义 props
的类型,这样 TypeScript 就能检查组件传入的 props
是否符合预期。
import { defineComponent } from 'vue';interface Props {name: string;age: number;
}export default defineComponent({props: {name: String,age: Number},setup(props: Props) {console.log(props.name); // TypeScript 会检查 props.name 是否为 string 类型}
});
类型声明文件
类型声明文件(.d.ts
)用于在项目中声明模块或全局变量的类型,以便 TypeScript 识别这些类型。
-
示例: 创建一个全局类型声明文件
src/shims-vue.d.ts
,让 TypeScript 知道.vue
文件的类型:declare module "*.vue" {import { DefineComponent } from "vue";const component: DefineComponent<{}, {}, any>;export default component; }
使用类型声明文件扩展全局类型
你可以通过类型声明文件来扩展 Vue 的全局类型,例如为 Vue 组件的实例添加类型定义。
-
示例: 在
src/types.d.ts
文件中添加 Vue 实例的类型声明:declare module '@vue/runtime-core' {interface ComponentCustomProperties {$myCustomMethod: () => void;} }
然后,你可以在 Vue 组件中访问
$myCustomMethod
:import { defineComponent } from 'vue';export default defineComponent({mounted() {this.$myCustomMethod(); // TypeScript 知道 $myCustomMethod 的类型} });