Vue.js 完全指南:从入门到精通
1. Vue.js 简介
1.1 什么是 Vue.js?
Vue.js(通常简称为 Vue)是一个用于构建用户界面的渐进式 JavaScript 框架。所谓"渐进式",意味着 Vue 的设计是由浅入深的,你可以根据自己的需求选择使用它的一部分或全部功能。
Vue 最初由尤雨溪(Evan You)在 2014 年创建,其设计灵感部分来源于 Angular,但更轻量级且更加灵活。Vue 专注于视图层(View Layer),易于与其他库或现有项目集成。
1.2 Vue.js 的核心特性
Vue.js 的核心特性包括:
- 声明式渲染:Vue 使用模板语法,允许我们以声明式的方式将数据渲染到 DOM。
- 响应式系统:Vue 自动追踪依赖关系并在数据变化时更新 DOM。
- 组件化开发:Vue 应用由独立、可复用的组件构建而成,每个组件都包含自己的 HTML、CSS 和 JavaScript。
- 虚拟 DOM:Vue 使用虚拟 DOM 来高效地更新实际 DOM。
- 指令系统:Vue 提供了一系列以
v-
开头的特殊属性,用于在 HTML 中实现各种功能。 - 过渡效果:Vue 提供了内置的过渡和动画系统。
- 状态管理:通过 Vuex 或 Pinia 提供集中式状态管理。
- 路由管理:通过 Vue Router 提供客户端路由管理。
1.3 Vue.js 的优势
Vue.js 相比其他框架具有以下优势:
- 易学易用:Vue 的 API 设计简洁直观,学习曲线平缓,特别适合初学者。
- 灵活渐进:可以逐步采用,从简单的 CDN 引入到完整的单页应用开发。
- 高性能:通过虚拟 DOM 和响应式系统实现高效的 DOM 更新。
- 轻量级:核心库非常小(约 20KB gzipped),不增加太多应用的加载时间。
- 生态系统:拥有丰富的插件、组件库和开发工具。
- 良好的文档:官方文档详细清晰,并有多语言版本。
- 活跃的社区:大量的开发者和企业支持,资源丰富。
1.4 Vue.js 的版本演进
Vue.js 的主要版本演进:
- Vue 1.x:初始版本,奠定了 Vue 的基础。
- Vue 2.x:引入虚拟 DOM,提升性能和稳定性,增加了服务端渲染支持。
- Vue 3.x:重写核心代码,使用 Proxy 代替 Object.defineProperty 实现响应式系统,引入 Composition API,支持 TypeScript,性能更佳。
2. 安装与环境搭建
2.1 Vue.js 的安装方式
Vue.js 提供了多种安装方式,可以根据不同的项目需求选择:
2.1.1 使用 CDN 直接引入
最简单的方式是通过 CDN 直接在 HTML 文件中引入 Vue.js:
<!-- 开发环境版本,包含有用的警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
Vue 2.x 的 CDN 链接:
<!-- 开发环境版本 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 生产环境版本 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.min.js"></script>
2.1.2 使用 npm 或 yarn 安装
对于更复杂的项目,推荐使用 npm 或 yarn 安装:
# npm
npm install vue
# yarn
yarn add vue
2.1.3 使用 Vue CLI 创建项目
Vue CLI 是 Vue.js 的官方脚手架工具,用于快速搭建 Vue 项目:
# 安装 Vue CLI
npm install -g @vue/cli
# 创建一个新项目
vue create my-project
# 或者使用图形界面
vue ui
2.1.4 使用 Vite 创建项目
Vite 是一个现代化的构建工具,由 Vue 团队开发,特别适合 Vue 项目:
# npm
npm create vite@latest my-vue-app -- --template vue
# yarn
yarn create vite my-vue-app --template vue
2.2 项目结构解析
一个典型的 Vue CLI 创建的项目结构如下:
my-project/
├── node_modules/ # 依赖包
├── public/ # 静态资源,不会被 webpack 处理
│ ├── favicon.ico # 网站图标
│ └── index.html # 页面模板
├── src/ # 源代码目录
│ ├── assets/ # 资源文件,会被 webpack 处理
│ ├── components/ # Vue 组件
│ ├── views/ # 页面级别的组件
│ ├── router/ # Vue Router 配置
│ ├── store/ # Vuex 状态管理
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── .gitignore # Git 忽略文件
├── babel.config.js # Babel 配置
├── package.json # 项目依赖和脚本
├── README.md # 项目说明
└── vue.config.js # Vue CLI 配置文件
Vite 创建的项目结构稍有不同:
my-vue-app/
├── node_modules/ # 依赖包
├── public/ # 静态资源
├── src/ # 源代码目录
│ ├── assets/ # 资源文件
│ ├── components/ # Vue 组件
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── .gitignore # Git 忽略文件
├── index.html # HTML 入口文件(注意位置不同)
├── package.json # 项目依赖和脚本
├── README.md # 项目说明
└── vite.config.js # Vite 配置文件
2.3 开发工具配置
为了提高 Vue.js 开发效率,推荐使用以下工具:
2.3.1 编辑器推荐
- Visual Studio Code:轻量级且功能强大的编辑器,有丰富的 Vue.js 插件支持。
2.3.2 推荐的 VS Code 插件
- Volar:Vue 3 的官方 VS Code 插件,提供语法高亮、智能提示等功能。
- ESLint:代码质量检查工具。
- Prettier:代码格式化工具。
- GitLens:Git 集成工具。
- Auto Import:自动导入组件和函数。
2.3.3 浏览器扩展
- Vue.js devtools:Chrome 和 Firefox 的浏览器扩展,用于调试 Vue 应用。可以查看组件层次结构、状态、事件等信息。
2.4 第一个 Vue 应用
创建一个简单的 Vue 应用,了解基本概念:
2.4.1 使用 CDN 创建
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My First Vue App</title>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<h1>{
{ message }}</h1>
<button @click="reverseMessage">反转消息</button>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
message: 'Hello Vue!'
}
},
methods: {
reverseMessage() {
this.message = this.message.split('').reverse().join('')
}
}
}).mount('#app');
</script>
</body>
</html>
2.4.2 使用 Vue CLI 或 Vite 创建
创建项目后,修改 App.vue
文件:
<template>
<div id="app">
<h1>{
{ message }}</h1>
<button @click="reverseMessage">反转消息</button>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
message: 'Hello Vue!'
}
},
methods: {
reverseMessage() {
this.message = this.message.split('').reverse().join('')
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
margin-top: 60px;
}
</style>
3. Vue 实例与生命周期
3.1 Vue 实例
在 Vue 2 中,每个 Vue 应用都是通过创建一个 Vue
实例开始的:
const vm = new Vue({
// 选项
})
在 Vue 3 中,我们使用 createApp
函数创建应用实例:
const app = Vue.createApp({
// 选项
})
// 挂载到 DOM 元素
app.mount('#app')
3.2 Vue 实例选项
Vue 实例接受许多选项,常用的包括:
3.2.1 数据选项
- data:Vue 实例的数据对象,在组件中是一个返回对象的函数。
- props:接收来自父组件的数据。
- computed:计算属性,基于依赖进行缓存。
- methods:实例的方法。
- watch:监听数据变化的回调函数。
3.2.2 DOM 相关选项
- el:指定挂载的 DOM 元素(Vue 2)。
- template:字符串模板。
- render:渲染函数,代替模板。
3.2.3 生命周期钩子
- beforeCreate、created
- beforeMount、mounted
- beforeUpdate、updated
- beforeDestroy、destroyed(Vue 2)/ beforeUnmount、unmounted(Vue 3)
3.2.4 资源选项
- directives:自定义指令。
- filters(Vue 2):自定义过滤器。
- components:注册组件。
3.2.5 组合选项
- mixins:混入对象。
- extends:扩展另一个组件。
- provide/inject:依赖注入。
3.3 Vue 实例属性和方法
Vue 实例暴露了一些有用的实例属性和方法,以 $
前缀标识:
const vm = new Vue({
data: {
message: 'Hello'
}
})
// 实例属性
console.log(vm.$data.message) // 'Hello'
console.log(vm.$el) // DOM 元素
// 实例方法
vm.$watch('message', function(newValue, oldValue) {
// 当 message 变化时调用
})
vm.$set(object, key, value) // 响应式地设置属性
vm.$delete(object, key) // 响应式地删除属性
3.4 Vue 生命周期详解
Vue 组件实例有一个完整的生命周期,从创建到销毁,可以在特定阶段执行自定义代码。
3.4.1 生命周期图示
创建阶段 挂载阶段 更新阶段 销毁阶段
┌─────────────────────┐ ┌───────────────────────┐ ┌───────────────────────┐ ┌───────────────────────┐
│ │ │ │ │ │ │ │
│ beforeCreate │ │ beforeMount │ │ beforeUpdate │ │ beforeDestroy │
│ │ │ │ │ │ │ (Vue 2) │
│ ↓ │ │ ↓ │ │ ↓ │ │ or │
│ │ │ │ │ │ │ beforeUnmount │
│ created │ │ mounted │ │ updated │ │ (Vue 3) │
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ ↓ │
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ destroyed │
│ │ │ │ │ │ │ (Vue 2) │
│ │ │ │ │ │ │ or │
│ │ │ │ │ │ │ unmounted │
│ │ │ │ │ │ │ (Vue 3) │
└─────────────────────┘ └───────────────────────┘ └───────────────────────┘ └───────────────────────┘
3.4.2 生命周期钩子函数详解
-
beforeCreate:
- 实例初始化后,数据观测和事件配置之前调用。
- 此时无法访问 data、computed、methods 等选项。
-
created:
- 实例创建完成后立即调用。
- 可以访问 data、computed、methods 等,但尚未挂载到 DOM。
- 适合执行初始化数据获取。
-
beforeMount:
- 挂载开始前调用。
- 相关的 render 函数首次被调用。
-
mounted:
- 实例挂载到 DOM 后调用。
- 可以访问 DOM 元素,适合执行需要访问 DOM 的操作。
- 不能保证所有子组件也都已经挂载完成。
-
beforeUpdate:
- 数据更新时,虚拟 DOM 重新渲染前调用。
- 适合在更新前访问现有的 DOM。
-
updated:
- 数据更改导致的虚拟 DOM 重新渲染后调用。
- 应避免在此钩子中修改数据,可能导致无限循环。
-
beforeDestroy(Vue 2)/ beforeUnmount(Vue 3):
- 实例销毁前调用。
- 实例仍然完全可用,适合清理事件监听器、定时器等资源。
-
destroyed(Vue 2)/ unmounted(Vue 3):
- 实例销毁后调用。
- 所有指令解绑,事件监听器移除,子实例也被销毁。
3.4.3 其他生命周期钩子
-
activated:
- 被 keep-alive 缓存的组件激活时调用。
-
deactivated:
- 被 keep-alive 缓存的组件停用时调用。
-
errorCaptured:
- 捕获一个来自后代组件的错误时调用。
-
renderTracked(Vue 3):
- 跟踪虚拟 DOM 重新渲染时调用。
-
renderTriggered(Vue 3):
- 虚拟 DOM 重新渲染被触发时调用。
3.4.4 生命周期钩子使用示例
export default {
data() {
return {
message: 'Hello Vue'
}
},
beforeCreate() {
console.log('beforeCreate: 实例初始化')
// 此时无法访问 data
console.log('this.message:', this.message) // undefined
},
created() {
console.log('created: 实例已创建')
// 可以访问 data
console.log('this.message:', this.message) // 'Hello Vue'
// 可以执行异步操作
this.fetchData()
},
beforeMount() {
console.log('beforeMount: 挂载前')
// DOM 尚未创建
console.log(this.$el) // 尚未挂载的元素
},
mounted() {
console.log('mounted: 已挂载')
// 可以访问 DOM
console.log(this.$el) // 实际 DOM 元素
// 适合执行需要访问 DOM 的操作
this.setupChart()
},
beforeUpdate() {
console.log('beforeUpdate: 数据更新前')
// 可以在更新前访问现有的 DOM
},
updated() {
console.log('updated: 数据更新后')
// DOM 已更新
},
beforeUnmount() { // Vue 3 使用 beforeUnmount,Vue 2 使用 beforeDestroy
console.log('beforeUnmount: 卸载前')
// 清理资源
this.cleanupResources()
},
unmounted() { // Vue 3 使用 unmounted,Vue 2 使用 destroyed
console.log('unmounted: 已卸载')
// 实例已销毁
},
// 辅助方法
methods: {
fetchData() {
// 获取初始数据
},
setupChart() {
// 设置图表
},
cleanupResources() {
// 清理资源
}
}
}
4. 模板语法与数据绑定
Vue 使用基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定到底层组件实例的数据。
4.1 插值
4.1.1 文本插值
最基本的数据绑定形式是使用"Mustache"语法(双大括号):
<span>消息:{
{ message }}</span>
{
{ message }}
会被替换为对应数据对象上 message
属性的值。每当 message
属性变化时,插值内容也会更新。
4.1.2 原始 HTML
双大括号会将数据解释为普通文本,如果要输出 HTML,需要使用 v-html
指令:
<p>使用文本插值:{
{ rawHtml }}</p>
<p>使用 v-html 指令:<span v-html="rawHtml"></span></p>
注意:在网站上动态渲染任意 HTML 可能会导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
4.1.3 属性绑定
Mustache 语法不能用在 HTML 属性上,应该使用 v-bind
指令:
<div v-bind:id="dynamicId"></div>
简写形式(推荐使用):
<div :id="dynamicId"></div>
对于布尔属性,如果条件是 truthy 值,则属性会被包含;否则会被省略:
<button :disabled="isButtonDisabled">按钮</button>
4.1.4 使用 JavaScript 表达式
Vue 支持在绑定表达式中使用完整的 JavaScript 表达式:
{
{ number + 1 }}
{
{ ok ? 'YES' : 'NO' }}
{
{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
每个绑定只能包含单个表达式,以下内容不会生效:
<!-- 这是语句,不是表达式 -->
{
{ var a = 1 }}
<!-- 流控制也不会生效,使用三元表达式 -->
{
{ if (ok) { return message } }}
4.2 指令
指令是带有 v-
前缀的特殊属性,用于在表达式的值改变时响应式地作用于 DOM。
4.2.1 指令基础
<p v-if="seen">现在你看到我了</p>
当 seen
的值为 true 时,<p>
元素会被渲染;否则会被移除。
4.2.2 指令参数
一些指令可以接收一个"参数",在指令名后以冒号表示:
<a v-bind:href="url">链接</a>
<a v-on:click="doSomething">点击</a>
4.2.3 动态参数
可以使用方括号括起来的 JavaScript 表达式作为指令的参数:
<a v-bind:[attributeName]="url">链接</a>
<a v-on:[eventName]="doSomething">点击</a>
这里的 attributeName
会被作为一个 JavaScript 表达式进行动态求值,求值结果作为最终的参数使用。
4.2.4 修饰符
修饰符是以 .
开头的特殊后缀,用于指定指令应该以特殊方式绑定:
<form v-on:submit.prevent="onSubmit">提交</form>
这里 .prevent
修饰符表示调用 event.preventDefault()
方法,阻止表单的默认提交行为。
4.3 常用内置指令
Vue 提供了许多内置指令,以下是一些最常用的:
4.3.1 v-bind
绑定 HTML 属性或组件 prop 到表达式:
<img v-bind:src="imageSrc">
<!-- 简写 -->
<img :src="imageSrc">
<!-- 动态属性名 -->
<button :[key]="value"></button>
<!-- 绑定多个属性 -->
<div v-bind="{ id: 'container', class: 'wrapper' }"></div>
4.3.2 v-on
绑定事件监听器:
<button v-on:click="doThis"></button>
<!-- 简写 -->
<button @click="doThis"></button>
<!-- 动态事件名 -->
<button @[event]="doThis"></button>
<!-- 内联语句 -->
<button @click="count++"></button>
<!-- 对象语法 -->
<button v-on="{ click: onClick, mouseover: onMouseOver }"></button>
4.3.3 v-model
创建双向数据绑定:
<input v-model="message">
<textarea v-model="message"></textarea>
<select v-model="selected">
<option value="">请选择</option>
<option value="1">选项 1</option>
<option value="2">选项 2</option>
</select>
4.3.4 v-if, v-else-if, v-else
条件性渲染元素:
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else>Not A/B</div>
4.3.5 v-show
根据条件显示/隐藏元素(元素始终会被渲染):
<div v-show="isVisible">内容</div>
4.3.6 v-for
基于数组/对象渲染列表:
<ul>
<li v-for="item in items" :key="item.id">{
{ item.text }}</li>
</ul>
<ul>
<li v-for="(item, index) in items" :key="item.id">{
{ index }}: {
{ item.text }}</li>
</ul>
<div v-for="(value, key, index) in object">
{
{ index }}. {
{ key }}: {
{ value }}
</div>
4.3.7 v-once
只渲染元素和组件一次:
<span v-once>这个将不会改变: {
{ message }}</span>
4.3.8 v-html
更新元素的 innerHTML:
<div v-html="htmlContent"></div>
4.3.9 v-text
更新元素的文本内容:
<span v-text="message"></span>
<!-- 等同于 -->
<span>{
{ message }}</span>
4.3.10 v-cloak
用于隐藏尚未编译的 Mustache 标签,直到组件实例准备完毕:
<div v-cloak>{
{ message }}</div>
需要配合 CSS 规则使用:
[v-cloak] {
display: none;
}
4.3.11 v-pre
跳过这个元素和它的子元素的编译过程:
<span v-pre>{
{ 这里的内容不会被编译 }}</span>
4.4 数据绑定的响应式原理
Vue 的响应式系统是其核心特性之一,使得数据和视图之间保持同步。
4.4.1 Vue 2 响应式原理
Vue 2 使用 Object.defineProperty
劫持对象属性的 getter 和 setter,在访问或修改属性时进行依赖收集和通知更新。
基本流程:
- 将普通 JavaScript 对象传入 Vue 实例作为
data
选项。 - Vue 遍历该对象所有属性,并使用
Object.defineProperty
把这些属性全部转为 getter/setter。 - 这些 getter/setter 使 Vue 能够追踪依赖,在属性被访问和修改时通知变更。
- 每个组件实例都对应一个 watcher 实例,在组件渲染的过程中记录被访问的属性。
- 当 setter 被调用时,会通知 watcher,从而使它关联的组件重新渲染。
限制:
- 无法检测对象属性的添加或删除。
- 无法直接检测数组的变化,如通过索引设置值或修改数组长度。
4.4.2 Vue 3 响应式原理
Vue 3 使用 ES6 的 Proxy
代替 Object.defineProperty
,可以监听整个对象而不是个别属性,解决了 Vue 2 中的限制。
基本流程:
- 将普通 JavaScript 对象传入
reactive
函数。 - Vue 3 使用
Proxy
包装该对象,拦截对该对象的所有操作。 - 在渲染过程中,会自动建立依赖关系。
- 当对象被修改时,会触发相关组件的重新渲染。
优势:
- 可以检测到对象和数组的所有变化。
- 可以检测属性的添加和删除。
- 可以检测数组索引和长度的变化。
- 性能更好,不需要递归遍历对象的所有属性。
5. 计算属性与侦听器
5.1 计算属性
计算属性用于声明依赖于其他属性的复杂逻辑。计算属性是基于它们的依赖进行缓存的,只有在依赖变化时才会重新计算。
5.1.1 基本用法
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
}
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
}
}
在模板中使用:
<p>{
{ fullName }}</p>
5.1.2 计算属性 vs 方法
计算属性会基于其依赖进行缓存,只有在依赖发生变化时才会重新计算。而方法在每次重新渲染时都会执行。
// 计算属性
computed: {
expensiveComputation() {
console.log('计算属性被执行')
return this.items.filter(item => item.active).map(item => item.value)
}
}
// 方法
methods: {
expensiveMethod() {
console.log('方法被执行')
return this.items.filter(item => item.active).map(item => item.value)
}
}
当多次访问 expensiveComputation
时,如果依赖没有变化,计算结果会从缓存中返回,而不会重新执行过滤和映射操作。而每次访问 expensiveMethod()
都会导致函数重新执行。
5.1.3 计算属性的 setter
计算属性默认只有 getter,但我们也可以提供 setter:
computed: {
fullName: {
// getter
get() {
return this.firstName + ' ' + this.lastName
},
// setter
set(newValue) {
const names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
现在当我们运行 this.fullName = 'Jane Smith'
时,setter 会被调用,this.firstName
和 this.lastName
会被相应地更新。
5.1.4 Composition API 中的计算属性
在 Vue 3 的 Composition API 中,计算属性通过 computed
函数创建:
import { ref, computed } from 'vue'
export default {
setup() {
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed(() => {
return firstName.value + ' ' + lastName.value
})
// 带有 setter 的计算属性
const fullNameWithSetter = computed({
get: () => firstName.value + ' ' + lastName.value,
set: (newValue) => {
const names = newValue.split(' ')
firstName.value = names[0]
lastName.value = names[names.length - 1]
}
})
return {
firstName,
lastName,
fullName,
fullNameWithSetter
}
}
}
5.2 侦听器
侦听器(watch)用于在数据变化时执行异步或开销较大的操作。
5.2.1 基本用法
export default {
data() {
return {
question: '',
answer: '问题包含问号(?)才能得到答案。'
}
},
watch: {
// 监听 question 变化
question(newQuestion, oldQuestion) {
if (newQuestion.includes('?')) {
this.getAnswer()
}
}
},
methods: {
async getAnswer() {
this.answer = '思考中...'
try {
const response = await fetch('https://yesno.wtf/api')
const data = await response.json()
this.answer = data.answer
} catch (error) {
this.answer = '错误!无法获取答案。'
}
}
}
}
5.2.2 侦听复杂数据
对于复杂数据类型(对象、数组),默认只检测引用变化,要检测内部变化需要使用深度侦听:
watch: {
// 对象深度监听
userProfile: {
handler(newValue, oldValue) {
console.log('用户资料变化了')
},
deep: true
},
// 监听对象的特定属性
'userProfile.name'(newValue, oldValue) {
console.log('用户名变化了')
}
}
5.2.3 侦听器选项
侦听器支持以下选项:
- deep:深度监听对象或数组内部变化。
- immediate:侦听器创建后立即执行一次回调。
- flush(Vue 3):控制侦听器回调的时机(
'pre'
、'post'
或'sync'
)。
watch: {
searchQuery: {
handler(newQuery, oldQuery) {
// 执行搜索
this.fetchResults(newQuery)
},
// 立即执行一次
immediate: true,
// 在组件更新后调用
flush: 'post' // Vue 3 特有
}
}
5.2.4 $watch 方法
除了在组件选项中定义侦听器外,还可以使用实例方法 $watch
命令式地创建侦听器:
// 创建侦听器
const unwatch = this.$watch('question', function(newQuestion, oldQuestion) {
// 执行操作
if (newQuestion.includes('?')) {
this.getAnswer()
}
}, {
immediate: true,
deep: true
})
// 停止侦听
unwatch() // 当不再需要时
5.2.5 Composition API 中的侦听器
在 Vue 3 的 Composition API 中,侦听器通过 watch
和 watchEffect
函数创建:
import { ref, watch, watchEffect } from 'vue'
export default {
setup() {
const question = ref('')
const answer = ref('问题包含问号(?)才能得到答案。')
// 基本侦听
watch(question, (newQuestion, oldQuestion) => {
if (newQuestion.includes('?')) {
getAnswer()
}
})
// 侦听多个来源
const firstName = ref('John')
const lastName = ref('Doe')
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
console.log('名字变化了')
})
// 深度侦听
const userProfile = ref({
name: 'John',
age: 30
})
watch(userProfile, (newProfile, oldProfile) => {
console.log('用户资料变化了')
}, { deep: true })
// watchEffect 会自动跟踪所有依赖,并在依赖变化时重新执行
watchEffect(() => {
console.log(`问题是: ${question.value}`)
console.log(`回答是: ${answer.value}`)
})
async function getAnswer() {
answer.value = '思考中...'
try {
const response = await fetch('https://yesno.wtf/api')
const data = await response.json()
answer.value = data.answer
} catch (error) {
answer.value = '错误!无法获取答案。'
}
}
return {
question,
answer
}
}
}
5.3 计算属性 vs 侦听器
虽然计算属性和侦听器在很多情况下可以互换使用,但它们有不同的用途:
- 计算属性:适用于根据其他属性派生值的场景,自动缓存结果。适合同步转换数据。
- 侦听器:适用于需要在数据变化时执行异步或开销较大操作的场景。适合需要副作用的操作。
选择合适工具的基本原则:
- 需要派生/组合值 → 计算属性
- 需要执行副作用 → 侦听器
6. Class 与 Style 绑定
6.1 绑定 HTML Class
6.1.1 对象语法
通过传递对象给 :class
来动态切换 class:
<div :class="{ active: isActive, 'text-danger': hasError }"></div>
上面的语法表示 active
这个 class 存在与否将取决于数据属性 isActive
的真假值,text-danger
同理。
也可以绑定一个返回对象的计算属性:
data() {
return {
isActive: true,
error: null
}
},
computed: {
classObject() {
return {