当前位置: 首页 > article >正文

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 的核心特性包括:

  1. 声明式渲染:Vue 使用模板语法,允许我们以声明式的方式将数据渲染到 DOM。
  2. 响应式系统:Vue 自动追踪依赖关系并在数据变化时更新 DOM。
  3. 组件化开发:Vue 应用由独立、可复用的组件构建而成,每个组件都包含自己的 HTML、CSS 和 JavaScript。
  4. 虚拟 DOM:Vue 使用虚拟 DOM 来高效地更新实际 DOM。
  5. 指令系统:Vue 提供了一系列以 v- 开头的特殊属性,用于在 HTML 中实现各种功能。
  6. 过渡效果:Vue 提供了内置的过渡和动画系统。
  7. 状态管理:通过 Vuex 或 Pinia 提供集中式状态管理。
  8. 路由管理:通过 Vue Router 提供客户端路由管理。

1.3 Vue.js 的优势

Vue.js 相比其他框架具有以下优势:

  1. 易学易用:Vue 的 API 设计简洁直观,学习曲线平缓,特别适合初学者。
  2. 灵活渐进:可以逐步采用,从简单的 CDN 引入到完整的单页应用开发。
  3. 高性能:通过虚拟 DOM 和响应式系统实现高效的 DOM 更新。
  4. 轻量级:核心库非常小(约 20KB gzipped),不增加太多应用的加载时间。
  5. 生态系统:拥有丰富的插件、组件库和开发工具。
  6. 良好的文档:官方文档详细清晰,并有多语言版本。
  7. 活跃的社区:大量的开发者和企业支持,资源丰富。

1.4 Vue.js 的版本演进

Vue.js 的主要版本演进:

  1. Vue 1.x:初始版本,奠定了 Vue 的基础。
  2. Vue 2.x:引入虚拟 DOM,提升性能和稳定性,增加了服务端渲染支持。
  3. 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 生命周期钩子
  • beforeCreatecreated
  • beforeMountmounted
  • beforeUpdateupdated
  • beforeDestroydestroyed(Vue 2)/ beforeUnmountunmounted(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 生命周期钩子函数详解
  1. beforeCreate

    • 实例初始化后,数据观测和事件配置之前调用。
    • 此时无法访问 data、computed、methods 等选项。
  2. created

    • 实例创建完成后立即调用。
    • 可以访问 data、computed、methods 等,但尚未挂载到 DOM。
    • 适合执行初始化数据获取。
  3. beforeMount

    • 挂载开始前调用。
    • 相关的 render 函数首次被调用。
  4. mounted

    • 实例挂载到 DOM 后调用。
    • 可以访问 DOM 元素,适合执行需要访问 DOM 的操作。
    • 不能保证所有子组件也都已经挂载完成。
  5. beforeUpdate

    • 数据更新时,虚拟 DOM 重新渲染前调用。
    • 适合在更新前访问现有的 DOM。
  6. updated

    • 数据更改导致的虚拟 DOM 重新渲染后调用。
    • 应避免在此钩子中修改数据,可能导致无限循环。
  7. beforeDestroy(Vue 2)/ beforeUnmount(Vue 3):

    • 实例销毁前调用。
    • 实例仍然完全可用,适合清理事件监听器、定时器等资源。
  8. destroyed(Vue 2)/ unmounted(Vue 3):

    • 实例销毁后调用。
    • 所有指令解绑,事件监听器移除,子实例也被销毁。
3.4.3 其他生命周期钩子
  1. activated

    • 被 keep-alive 缓存的组件激活时调用。
  2. deactivated

    • 被 keep-alive 缓存的组件停用时调用。
  3. errorCaptured

    • 捕获一个来自后代组件的错误时调用。
  4. renderTracked(Vue 3):

    • 跟踪虚拟 DOM 重新渲染时调用。
  5. renderTriggered(Vue 3):

    • 虚拟 DOM 重新渲染被触发时调用。
3.4.4 生命周期钩子使用示例
export default {data() {return {message: 'Hello Vue'}},beforeCreate() {console.log('beforeCreate: 实例初始化')// 此时无法访问 dataconsole.log('this.message:', this.message) // undefined},created() {console.log('created: 实例已创建')// 可以访问 dataconsole.log('this.message:', this.message) // 'Hello Vue'// 可以执行异步操作this.fetchData()},beforeMount() {console.log('beforeMount: 挂载前')// DOM 尚未创建console.log(this.$el) // 尚未挂载的元素},mounted() {console.log('mounted: 已挂载')// 可以访问 DOMconsole.log(this.$el) // 实际 DOM 元素// 适合执行需要访问 DOM 的操作this.setupChart()},beforeUpdate() {console.log('beforeUpdate: 数据更新前')// 可以在更新前访问现有的 DOM},updated() {console.log('updated: 数据更新后')// DOM 已更新},beforeUnmount() { // Vue 3 使用 beforeUnmount,Vue 2 使用 beforeDestroyconsole.log('beforeUnmount: 卸载前')// 清理资源this.cleanupResources()},unmounted() { // Vue 3 使用 unmounted,Vue 2 使用 destroyedconsole.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,在访问或修改属性时进行依赖收集和通知更新。

基本流程:

  1. 将普通 JavaScript 对象传入 Vue 实例作为 data 选项。
  2. Vue 遍历该对象所有属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。
  3. 这些 getter/setter 使 Vue 能够追踪依赖,在属性被访问和修改时通知变更。
  4. 每个组件实例都对应一个 watcher 实例,在组件渲染的过程中记录被访问的属性。
  5. 当 setter 被调用时,会通知 watcher,从而使它关联的组件重新渲染。

限制:

  • 无法检测对象属性的添加或删除。
  • 无法直接检测数组的变化,如通过索引设置值或修改数组长度。
4.4.2 Vue 3 响应式原理

Vue 3 使用 ES6 的 Proxy 代替 Object.defineProperty,可以监听整个对象而不是个别属性,解决了 Vue 2 中的限制。

基本流程:

  1. 将普通 JavaScript 对象传入 reactive 函数。
  2. Vue 3 使用 Proxy 包装该对象,拦截对该对象的所有操作。
  3. 在渲染过程中,会自动建立依赖关系。
  4. 当对象被修改时,会触发相关组件的重新渲染。

优势:

  • 可以检测到对象和数组的所有变化。
  • 可以检测属性的添加和删除。
  • 可以检测数组索引和长度的变化。
  • 性能更好,不需要递归遍历对象的所有属性。

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: {// getterget() {return this.firstName + ' ' + this.lastName},// setterset(newValue) {const names = newValue.split(' ')this.firstName = names[0]this.lastName = names[names.length - 1]}}
}

现在当我们运行 this.fullName = 'Jane Smith' 时,setter 会被调用,this.firstNamethis.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 中,侦听器通过 watchwatchEffect 函数创建:

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 {

相关文章:

Vue.js 完全指南:从入门到精通

1. Vue.js 简介 1.1 什么是 Vue.js? Vue.js(通常简称为 Vue)是一个用于构建用户界面的渐进式 JavaScript 框架。所谓"渐进式",意味着 Vue 的设计是由浅入深的,你可以根据自己的需求选择使用它的一部分或全部功能。 Vue 最初由尤雨溪(Evan You)在 2014 年创…...

在Git仓库的Readme上增加目录页

一般在编写Readme时想要增加像文章那样的目录&#xff0c;方便快速跳转&#xff0c;但是Markdown语法并没有提供这样的方法&#xff0c;但是可以通过超链接结合锚点的方式来实现&#xff0c;如下图是我之前一个项目里写的Readme&#xff1a; 例如有下面几个Readme内容&#xff…...

C# SolidWorks 二次开发 -各种菜单命令增加方式

今天给大家讲一讲solidworks中各种菜单界面&#xff0c;如下图&#xff0c;大概有13处&#xff0c;也许还不完整哈。 1.CommandManager选项卡2.下拉选项卡3.菜单栏4.下级菜单5.浮动工具栏6.快捷方式工具栏7.FeatureManager工具栏区域8.MontionManager区域 ModelView?9.任务窗…...

分布式架构-Spring技术如何能实现分布式事务

在Spring技术栈中实现分布式事务&#xff0c;可通过多种成熟方案实现跨服务或跨数据库的事务一致性管理。以下是主要实现方式及技术要点&#xff1a; 一、基于Seata框架的AT模式 ‌核心组件‌ ‌TC (Transaction Coordinator)‌&#xff1a;全局事务协调器&#xff08;独立部署…...

【RocketMQRocketMQ Dashbord】Springboot整合RocketMQ

【RocketMQ&&RocketMQ Dashbord】Springboot整合RocketMQ 【一】Mac安装RocketMQ和RocketMQ Dashbord【1】安装RocketMQ&#xff08;1&#xff09;下载&#xff08;2&#xff09;修改 JVM 参数&#xff08;3&#xff09;启动测试&#xff08;4&#xff09;关闭测试&…...

vue 3 深度指南:从基础到全栈开发实践

目录 一、环境搭建与项目初始化 1. 前置依赖安装 2. 项目初始化与结构解析 二、核心概念与语法深度解析 1. MVVM 模式与响应式原理 2. 模板语法与指令进阶 3. 组件化开发 三、进阶开发与全栈集成 1. 路由管理&#xff08;Vue Router&#xff09; 2. 状态管理&#xf…...

《白帽子讲 Web 安全》之跨站请求伪造

引言 在数字化时代&#xff0c;网络已深度融入人们生活的方方面面&#xff0c;Web 应用如雨后春笋般蓬勃发展&#xff0c;为人们提供着便捷高效的服务。然而&#xff0c;繁荣的背后却潜藏着诸多安全隐患&#xff0c;跨站请求伪造&#xff08;CSRF&#xff09;便是其中极为隐蔽…...

K8S学习之基础五十:k8s中pod时区问题并通过kibana查看日志

k8s中pod默认时区不是中国的&#xff0c;挂载一个时区可以解决 vi pod.yaml apiVersion: v1 kind: Pod metadata:name: counter spec:containers:- name: countimage: 172.16.80.140/busybox/busybox:latestimagePullPolicy: IfNotPresentargs: [/bin/sh,-c,i0;while true;do …...

nginx代理前端请求

一&#xff0c;项目配置 我在 ip 为 192.168.31.177 的机器上使用 vue3 开发前端项目&#xff0c;项目中使用 axios 调用后端接口。 这是 axios 的配置&#xff1a; import axios from axios;const request axios.create({baseURL: http://192.168.31.177:8001,// 设置请求…...

LibVLC —— 《基于Qt的LibVLC专业开发技术》视频教程

🔔 LibVLC/VLC 相关技术、疑难杂症文章合集(掌握后可自封大侠 ⓿_⓿)(记得收藏,持续更新中…) 《基于Qt的LibVLC专业开发技术》课程视频,(CSDN课程主页、51CTO课程主页) 适合具有一些C++/Qt编程基础,想要进一步提高或涉足音视频行业的。本课程分7章节,共计35小节。…...

Android生态大变革,谷歌调整开源政策,核心开发不再公开

“开源”这个词曾经是Android的护城河&#xff0c;如今却成了谷歌的烫手山芋。最近谷歌宣布调整Android的开源政策&#xff0c;核心开发将全面转向私有分支。翻译成人话就是&#xff1a;以后Android的核心更新&#xff0c;不再公开共享了。 这操作不就是开源变节吗&#xff0c;…...

Android Gradle 插件问题:The option ‘android.useDeprecatedNdk‘ is deprecated.

问题与处理策略 问题描述 在 Android 项目中&#xff0c;报如下警告 The option android.useDeprecatedNdk is deprecated. The current default is false. It has been removed from the current version of the Android Gradle plugin. NdkCompile is no longer supported…...

【web应用安全】关于web应用安全的几个主要问题的思考

文章目录 防重放攻击1. **Token机制&#xff08;一次性令牌&#xff09;**2. **时间戳 超时验证**3. **Nonce&#xff08;一次性随机数&#xff09;**4. **请求签名&#xff08;如HMAC&#xff09;**5. **HTTPS 安全Cookie**6. **幂等性设计****综合防御策略建议****注意事项…...

Git 基础入门:从概念到实践的版本控制指南

一、Git 核心概念解析 1. 仓库&#xff08;Repository&#xff09; Git 的核心存储单元&#xff0c;包含项目所有文件及其完整历史记录。分为本地仓库&#xff08;开发者本地副本&#xff09;和远程仓库&#xff08;如 GitHub、GitLab 等云端存储&#xff09;&#xff0c;支持…...

银行分布式新核心的部署架构(两地三中心)

银行的核心系统对可用性和性能要求均非常严苛&#xff0c;所以一般都采用两地三中心部署模式。 其中&#xff1a; 同城两个主数据中心各自部署一套热备&#xff0c;平时两个中心同时在线提供服务&#xff0c;进行负载均衡假如其中一个数据中心出现异常&#xff0c;则由另外一个…...

Spring 及 Spring Boot 条件化注解(15个)完整列表及示例

Spring 及 Spring Boot 条件化注解完整列表及示例 1. 所有条件化注解列表 Spring 和 Spring Boot 提供了以下条件化注解&#xff08;共 15 个&#xff09;&#xff0c;用于在配置类或方法上实现条件化注册 Bean 或配置&#xff1a; 注解名称作用来源框架Conditional自定义条件…...

MantisBT在Windows10上安装部署详细步骤

MantisBT 是一款基于 Web 的开源缺陷跟踪系统&#xff0c;以下是在 Windows 10 上安装部署 MantisBT 的详细步骤&#xff1a; 1. 安装必要的环境 MantisBT 是一个基于 PHP 的 Web 应用程序&#xff0c;因此需要安装 Web 服务器&#xff08;如 Apache&#xff09;、PHP 和数据…...

9.4分漏洞!Next.js Middleware鉴权绕过漏洞安全风险通告

今日&#xff0c;亚信安全CERT监控到安全社区研究人员发布安全通告&#xff0c;Next.js 存在一个授权绕过漏洞&#xff0c;编号为 CVE-2025-29927。攻击者可能通过发送精心构造的 x-middleware-subrequest 请求头绕过中间件安全控制&#xff0c;从而在未授权的情况下访问受保护…...

处理json,将接口返回的数据转成list<T>,和几个时间处理方法的工具类

接口或者其他方式返回json格式&#xff0c;也可以直接处理里边只有list的json数据 //第一种json格式&#xff0c;包含分页信息 {"code": 200,"msg": null,"data": {"records": [{"风速": "0.0","电流"…...

OpenCV图像拼接(5)图像拼接模块的用于创建权重图函数createWeightMap()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::detail::createWeightMap 是 OpenCV 库中用于图像拼接模块的一个函数&#xff0c;主要用于创建权重图。这个权重图在图像拼接过程中扮演着重…...

linux 运行脚本命令区别

文章目录 chmod 赋予权限运行sh script.sh适用场景 bash script.shsource 或 . 脚本 chmod 赋予权限运行 chmod x script.sh # 赋予执行权限 ./script.sh # 直接执行创建新的子进程&#xff0c;不会影响当前 shell 的环境变量。#!&#xff08;Shebang&#xff09; 指…...

【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置

噩梦终结&#xff1a;Flutter 配安卓、鸿蒙、iOS 真机调试环境 问题背景 很多开发者在配置 Flutter 项目环境时遇到困难&#xff0c;尤其是在处理 Android、鸿蒙和 iOS 真机调试环境时。卓伊凡最近接手了一个项目&#xff0c;发现很多“专业程序员”在环境搭建上花费了大量时…...

C++11QT复习 (六)

类型转换函数和类域 **Day6-3 类型转换函数和类域****1. 类型转换函数&#xff08;Type Conversion Functions&#xff09;****1.1 概述****1.2 代码示例****1.3 关键优化** **2. 类域&#xff08;Class Scope&#xff09;****2.1 作用域 vs 可见域****2.2 代码示例****2.3 关键…...

区块链技术在投票系统中的应用:安全、透明与去中心化

区块链技术在投票系统中的应用:安全、透明与去中心化 【引言】 近年来,电子投票系统因其便捷性受到广泛关注,但随之而来的安全问题也屡见不鲜,如选票篡改、重复投票、数据泄露等。如何确保投票的公平性、透明度和安全性? 区块链技术或许是解决方案之一! 区块链的 去中…...

CTF类题目复现总结-[MRCTF2020]ezmisc 1

一、题目地址 https://buuoj.cn/challenges#[MRCTF2020]ezmisc二、复现步骤 1、下载附件&#xff0c;得到一张图片&#xff1b; 2、利用010 Editor打开图片&#xff0c;提示CRC值校验错误&#xff0c;flag.png应该是宽和高被修改了&#xff0c;导致flag被隐藏掉&#xff1b;…...

MetInfo6.0.0目录遍历漏洞原理分析

所需进行代码审计的文件路径&#xff1a; C:\phpStudy\WWW\MetInfo6.0.0\include\thumb.php C:\phpStudy\WWW\MetInfo6.0.0\app\system\entrance.php C:\phpStudy\WWW\MetInfo6.0.0\app\system\include\class\load.class.php C:\phpStudy\WWW\MetInfo6.0.0\app\system\include…...

linux打包前端vue,后端springboot项目

第一步先对整个项目进行通过maven进行clean在进行compile 第二步直接进行打包package和install都可以 第三部把对应的jar放到服务器上 把jar包放到服务器上某个地址下&#xff0c;然后cd到这个目录下&#xff0c;然后执行命令 nohup java -jar ruoyi-admin.jar > springbo…...

Elasticsearch:使用 AI SDK 和 Elastic 构建 AI 代理

作者&#xff1a;来自 Elastic Carly Richmond 你是否经常听到 AI 代理&#xff08;AI agents&#xff09;这个词&#xff0c;但不太确定它们是什么&#xff0c;或者如何在 TypeScript&#xff08;或 JavaScript&#xff09;中构建一个&#xff1f;跟我一起深入了解 AI 代理的概…...

SQLAlchemy 支持特殊字符

postgresql 实践 pydantic 实践&#xff08;一&#xff09;基础 pydantic 实践&#xff08;二&#xff09;数据校验 SQLAlchemy 介绍与实践 SQLAlchemy 支持特殊字符 SQLAlchemy 支持特殊字符 1. 字符集介绍分析2. MySQL 支持特殊字符2.1. 更新 MySQL 字符集为 utf8mb42.2 更新…...

Docker 快速入门指南

Docker 快速入门指南 1. Docker 常用指令 Docker 是一个轻量级的容器化平台&#xff0c;可以帮助开发者快速构建、测试和部署应用程序。以下是一些常用的 Docker 命令。 1.1 镜像管理 # 搜索镜像 docker search <image_name># 拉取镜像 docker pull <image_name>…...