3. Vue.js 核心概念


文档摘要

Vue.js 核心概念详解:声明式渲染、响应式系统与组件化架构 核心摘要:深入解析 Vue.js 核心概念,全面掌握声明式渲染、模板语法、Vue 3 响应式系统(Proxy)以及组件化架构。本文通过详实的代码实践与原理剖析,帮助开发者构建高效、可维护的现代前端应用,是进阶 Vue.js 开发的必备指南。 声明式渲染与模板语法 Vue.js 的核心特性之一是声明式渲染。与传统的命令式编程不同,开发者只需描述期望在屏幕上呈现的内容,Vue.js 会自动更新 DOM 以匹配声明状态。这极大地简化了前端开发流程,使开发者能够专注于业务逻辑,而非底层的 DOM 操作。 模板语法是 Vue.js 实现声明式渲染的桥梁。该语法允许在 HTML 中使用特殊标记来声明数据和 DOM 之间的绑定关系。Vue.

Vue.js 核心概念详解:声明式渲染、响应式系统与组件化架构

核心摘要:深入解析 Vue.js 核心概念,全面掌握声明式渲染、模板语法、Vue 3 响应式系统(Proxy)以及组件化架构。本文通过详实的代码实践与原理剖析,帮助开发者构建高效、可维护的现代前端应用,是进阶 Vue.js 开发的必备指南。

1. 声明式渲染与模板语法

Vue.js 的核心特性之一是声明式渲染。与传统的命令式编程不同,开发者只需描述期望在屏幕上呈现的内容,Vue.js 会自动更新 DOM 以匹配声明状态。这极大地简化了前端开发流程,使开发者能够专注于业务逻辑,而非底层的 DOM 操作。

模板语法是 Vue.js 实现声明式渲染的桥梁。该语法允许在 HTML 中使用特殊标记来声明数据和 DOM 之间的绑定关系。Vue.js 会将这些模板编译成高效的渲染函数,从而实现页面的动态更新。

1.1 插值 (Interpolation)

最常见的模板语法形式是文本插值,使用双大括号 {{ }} 将数据绑定到模板中。

代码实践:

<template> <div> <p>Message: {{ message }}</p> <p>Number: {{ number }}</p> <p>计算结果: {{ number + 10 }}</p> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const message = ref('Hello Vue.js 3!'); const number = ref(10); return { message, number, }; }, }; </script>

内容详解:

  • {{ message }}{{ number }}setup 函数中定义的 messagenumber 响应式变量的值插入到 <p> 标签中。
  • {{ number + 10 }} 展示了插值支持简单的 JavaScript 表达式计算。
  • messagenumber 的值发生变化时,模板中对应的内容会自动更新,无需手动操作 DOM。

1.2 指令 (Directives)

指令是以 v- 开头的特殊 attribute,用于指示 Vue.js 在 DOM 元素上执行特定操作。指令广泛应用于条件渲染、列表渲染、事件监听、表单绑定等多种场景。

1.2.1 v-bind

v-bind 指令用于动态绑定 HTML attribute,将属性值绑定到 Vue 实例的数据或表达式。其简写形式为 :

代码实践:

<template> <div> <a v-bind:href="url">官方链接</a> <img :src="imageUrl" alt="动态图片"> <div :class="{ active: isActive }">动态 Class</div> <div :style="{ color: textColor, fontSize: fontSize + 'px' }">动态 Style</div> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const url = ref('https://cn.vuejs.org/'); const imageUrl = ref('https://vuejs.org/images/logo.png'); const isActive = ref(true); const textColor = ref('blue'); const fontSize = ref(20); return { url, imageUrl, isActive, textColor, fontSize, }; }, }; </script>

内容详解:

  • v-bind:href="url"<a> 标签的 href attribute 绑定到 url 变量。
  • :src="imageUrl"v-bind:src 的简写形式,用于绑定 <img> 标签的 src attribute。
  • :class="{ active: isActive }" 动态绑定 class,当 isActive 为真时,元素会添加 active 类名。
  • :style="{ color: textColor, fontSize: fontSize + 'px' }" 动态绑定内联样式,设置元素的文本颜色和字体大小。

1.2.2 v-if, v-else-if, v-else

这组指令用于条件渲染,根据表达式的布尔值决定是否渲染元素或元素块。

代码实践:

<template> <div> <p v-if="type === 'A'">类型 A</p> <p v-else-if="type === 'B'">类型 B</p> <p v-else>未知类型</p> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const type = ref('B'); return { type }; }, }; </script>

内容详解:

  • 根据 type 变量的值,仅满足条件的 <p> 元素会被渲染到 DOM 中。
  • v-ifv-else-ifv-else 必须紧邻,形成一个完整的条件渲染块。
  • v-if 是“真正的”条件渲染,当条件为假时,元素及其子组件会被完全销毁和重建。

1.2.3 v-show

v-show 同样用于条件渲染,但与 v-if 不同的是,它仅通过切换元素的 display CSS 属性来控制显隐。无论条件真假,元素始终会被渲染并保留在 DOM 中。

代码实践:

<template> <div> <p v-show="isVisible">显示文本</p> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const isVisible = ref(false); return { isVisible }; }, }; </script>

内容详解:

  • isVisible 为真时,元素显示;为假时,元素隐藏(应用 display: none;)。
  • v-show 适用于频繁切换显示状态的场景,因为其初始渲染开销较高,但切换开销比 v-if 更小。

1.2.4 v-for

v-for 指令用于列表渲染,根据数组或对象的项渲染多个相似的元素或模板块。

代码实践:

<template> <div> <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 + 1 }}. {{ item.text }} </li> </ul> <div v-for="(value, key, index) in myObject" :key="key"> {{ index + 1 }}. {{ key }}: {{ value }} </div> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const items = ref([ { id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }, { id: 3, text: 'Item 3' }, ]); const myObject = ref({ name: 'Vue.js', version: '3.x', author: 'Evan You', }); return { items, myObject }; }, }; </script>

内容详解:

  • v-for="item in items" 遍历数组,为每个 item 渲染一个 <li> 元素。
  • (item, index) in items 可同时获取当前项及其索引 index
  • (value, key, index) in myObject 用于遍历对象的键值对。
  • :key="item.id"v-for 的必要属性,用于 Vue.js 跟踪节点身份,从而更高效地更新列表。key 必须是唯一且稳定的标识符。

1.2.5 v-on

v-on 指令用于监听 DOM 事件,并在触发时执行 JavaScript 代码。其简写形式为 @

代码实践:

<template> <div> <button v-on:click="increment">增加计数</button> <button @mouseover="showMessage">显示消息</button> <p>Count: {{ count }}</p> <p v-if="message">{{ message }}</p> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const count = ref(0); const message = ref(''); const increment = () => { count.value++; }; const showMessage = () => { message.value = '鼠标悬停事件触发!'; }; return { count, message, increment, showMessage }; }, }; </script>

内容详解:

  • v-on:click="increment" 监听点击事件并执行 increment 方法。
  • @mouseover="showMessage"v-on:mouseover 的简写,监听鼠标悬停事件。
  • 尽管支持内联语句(如 @click="count++"),但为了代码可维护性,建议将复杂逻辑封装在独立的方法中。

1.2.6 v-model

v-model 指令用于在表单输入元素或组件上创建双向数据绑定,实现表单元素值与 Vue 实例数据的同步更新。

代码实践:

<template> <div> <input type="text" v-model="inputText" placeholder="请输入文本"> <p>输入内容: {{ inputText }}</p> <textarea v-model="textareaText" placeholder="请输入多行文本"></textarea> <p>多行输入内容: {{ textareaText }}</p> <input type="checkbox" id="checkbox" v-model="isChecked"> <label for="checkbox">复选框: {{ isChecked }}</label> <div> <input type="radio" id="radio-a" value="A" v-model="picked" /> <label for="radio-a">A</label> <input type="radio" id="radio-b" value="B" v-model="picked" /> <label for="radio-b">B</label> <p>单选按钮: {{ picked }}</p> </div> <select v-model="selected"> <option value="option1">Option 1</option> <option value="option2">Option 2</option> <option value="option3">Option 3</option> </select> <p>选择框: {{ selected }}</p> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const inputText = ref(''); const textareaText = ref(''); const isChecked = ref(false); const picked = ref(''); const selected = ref('option1'); return { inputText, textareaText, isChecked, picked, selected }; }, }; </script>

内容详解:

  • v-model 将输入框的值双向绑定到响应式变量。输入框值改变时,变量同步更新;反之亦然。
  • 支持 input (text, textarea, checkbox, radio) 和 select 等多种表单元素。
  • v-model 本质上是语法糖,底层实现了绑定 value attribute 和监听 input 事件。

1.3 模板引用 (Template Refs)

尽管 Vue.js 提倡声明式编程,但在某些场景下(如聚焦输入框、集成第三方 DOM 库),需要直接访问底层 DOM 元素。模板引用 ref 提供了获取 DOM 元素或组件实例的途径。

代码实践:

<template> <div> <input ref="myInput" type="text" placeholder="聚焦输入框" /> <button @click="focusInput">聚焦</button> </div> </template> <script> import { ref, onMounted } from 'vue'; export default { setup() { const myInput = ref(null); const focusInput = () => { myInput.value.focus(); // 通过 .value 访问 DOM 元素 }; onMounted(() => { console.log('Input Element:', myInput.value); // 组件挂载后访问 DOM }); return { myInput, focusInput }; }, }; </script>

内容详解:

  • <input> 元素上添加 ref="myInput" attribute。
  • setup 函数中,使用 const myInput = ref(null) 创建同名响应式变量。
  • 在组件挂载后(onMounted 生命周期),myInput.value 将指向真实的 DOM 元素。
  • 通过 myInput.value 可调用原生 DOM 方法,如 focus()

2. 响应式系统 (Reactivity System)

Vue.js 最核心的特性之一是其响应式系统。它使得数据变化能够自动驱动视图更新,免去了手动操作 DOM 的繁琐。Vue.js 3 的响应式系统基于 ES6 Proxy 实现,提供了更高效、更灵活的响应式机制。

2.1 refreactive

Vue.js 3 提供了两个核心 API 来创建响应式数据:

  • ref: 用于创建基本类型(数字、字符串、布尔值)和引用类型的响应式数据。在 JavaScript 中访问其值需使用 .value,在模板中则自动解包。
  • reactive: 用于创建引用类型(对象、数组)的深层响应式数据。直接访问其属性即可,无需 .value

代码实践:

<template> <div> <p>Ref Number: {{ refNumber }}</p> <p>Reactive Object Name: {{ reactiveObject.name }}</p> <button @click="updateData">更新数据</button> </div> </template> <script> import { ref, reactive } from 'vue'; export default { setup() { const refNumber = ref(10); const reactiveObject = reactive({ name: 'Initial Name', count: 0, }); const updateData = () => { refNumber.value++; reactiveObject.name = 'Updated Name'; reactiveObject.count++; }; return { refNumber, reactiveObject, updateData }; }, }; </script>

图示:响应式系统数据流

数据流解析:

  1. Data (ref/reactive): 使用 refreactive 创建响应式数据源。
  2. Reactivity System: Vue.js 的响应式系统拦截数据的读取与修改。
  3. Dependency Tracking: 系统记录模板中哪些部分依赖于哪些具体数据。
  4. Template: 模板渲染时消费响应式数据。
  5. Virtual DOM: 数据变化时,Vue.js 生成新的虚拟 DOM 树。
  6. Real DOM: 通过 Diff 算法,将虚拟 DOM 的差异高效同步到真实 DOM。
  7. View: 最终用户看到更新后的界面。
  8. User Interaction: 用户交互触发事件,修改数据源,开启新一轮响应式循环。

2.2 计算属性 (Computed Properties)

计算属性用于声明依赖于其他响应式数据的派生状态。Vue.js 会缓存计算属性的结果,仅当依赖项发生变化时才重新计算,从而避免不必要的性能开销。

代码实践:

<template> <div> <p>原始价格: {{ price }}</p> <p>折扣: {{ discount }}%</p> <p>折后价格 (计算属性): {{ discountedPrice }}</p> </div> </template> <script> import { ref, computed } from 'vue'; export default { setup() { const price = ref(100); const discount = ref(20); const discountedPrice = computed(() => { return price.value * (1 - discount.value / 100); }); return { price, discount, discountedPrice }; }, }; </script>

内容详解:

  • computed 接收一个 getter 函数,返回一个只读的响应式 ref 对象。
  • 计算结果会被缓存,discountedPrice 仅在 pricediscount 改变时重新计算。
  • 在模板中访问计算属性时,无需添加 .value

2.3 侦听器 (Watchers)

侦听器用于在响应式数据变化时执行自定义的回调函数。与计算属性不同,侦听器更适合处理副作用 (Side Effects),如异步数据请求、DOM 操作或复杂的业务逻辑联动。

代码实践:

<template> <div> <p>Count: {{ count }}</p> <button @click="increment">增加计数</button> </div> </template> <script> import { ref, watch } from 'vue'; export default { setup() { const count = ref(0); watch(count, (newValue, oldValue) => { console.log(`Count changed from ${oldValue} to ${newValue}`); // 此处可执行异步操作,如发送网络请求或本地存储 }); const increment = () => { count.value++; }; return { count, increment }; }, }; </script>

内容详解:

  • watch 接收数据源和回调函数。当 count.value 变化时,回调函数被触发。
  • 回调函数接收 newValue(新值)和 oldValue(旧值)作为参数。
  • 支持侦听多个数据源、reactive 对象或计算属性,满足复杂的监听需求。

3. 组件化架构 (Component-Based Architecture)

Vue.js 是一个基于组件的框架。组件是 Vue 应用的基本构建块,它们是可复用、独立且可组合的代码单元,封装了视图(HTML)、逻辑(JS)和样式(CSS)。组件化架构使得大型应用的开发与维护更加模块化。

3.1 组件的定义与注册

在 Vue.js 3 中,通常使用单文件组件 (Single-File Components, SFCs) 来定义组件。SFC 使用 .vue 扩展名,将 <template><script><style> 整合在一个文件中。

代码实践:

MyComponent.vue (子组件):

<template> <div class="my-component"> <h3>{{ title }}</h3> <p>{{ content }}</p> </div> </template> <script> export default { name: 'MyComponent', props: { title: { type: String, required: true, }, content: { type: String, default: '默认内容', }, }, setup(props) { // 组件内部逻辑 return {}; }, }; </script> <style scoped> /* scoped 确保样式仅作用于当前组件 */ .my-component h3 { color: green; } </style>

App.vue (父组件):

<template> <div> <h1>父组件应用</h1> <MyComponent title="组件标题 1" content="组件内容 1" /> <MyComponent title="组件标题 2" /> </div> </template> <script> import MyComponent from './MyComponent.vue'; export default { components: { MyComponent, // 局部注册子组件 }, setup() { return {}; }, }; </script>

内容详解:

  • SFC 结构: <template> 定义 UI,<script> 处理逻辑与 Props,<style scoped> 实现样式隔离。
  • Props 定义: 在子组件中声明接收的数据类型及默认值,增强代码健壮性。
  • 组件注册: 父组件通过 import 导入并在 components 选项中注册,随后即可在模板中作为自定义标签使用。

3.2 Props 数据传递

Props 是父组件向子组件传递数据的标准机制。Vue 遵循单向数据流原则:父组件的数据更新会向下流动到子组件,但子组件不应直接修改 Props 的值。若需修改,应通过事件通知父组件。

3.3 Events 事件通信

Events 是子组件向父组件传递数据或通知状态变化的机制。子组件通过 emit 方法触发自定义事件,父组件在模板中监听并处理。

代码实践:

ChildComponent.vue:

<template> <div> <button @click="emitEvent">触发事件</button> </div> </template> <script> export default { setup(props, { emit }) { const emitEvent = () => { // 触发名为 'my-event' 的自定义事件,并携带数据 emit('my-event', '来自子组件的数据'); }; return { emitEvent }; }, }; </script>

ParentComponent.vue:

<template> <div> <h1>父组件</h1> <ChildComponent @my-event="handleMyEvent" /> <p v-if="message">{{ message }}</p> </div> </template> <script> import { ref } from 'vue'; import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, setup() { const message = ref(''); const handleMyEvent = (data) => { message.value = `接收到子组件事件,数据为: ${data}`; }; return { message, handleMyEvent }; }, }; </script>

3.4 组件生命周期钩子 (Lifecycle Hooks)

每个 Vue 组件实例在创建、挂载、更新和销毁的过程中,都会经历一系列生命周期阶段。Vue.js 提供了生命周期钩子函数,允许开发者在特定阶段执行自定义逻辑(如数据获取、DOM 操作、定时器清理)。

常用的 Vue.js 3 组合式 API (Composition API) 生命周期钩子:

  • onBeforeMount: 组件挂载到 DOM 之前调用。
  • onMounted: 组件挂载完成之后调用(DOM 已渲染,适合进行 DOM 操作或发起网络请求)。
  • onBeforeUpdate: 响应式数据更新,但 DOM 尚未重新渲染之前调用。
  • onUpdated: DOM 重新渲染完成之后调用。
  • onBeforeUnmount: 组件卸载之前调用(适合清理定时器或取消网络请求)。
  • onUnmounted: 组件卸载完成之后调用。

代码实践:

<script> import { ref, onMounted, onUnmounted } from 'vue'; export default { setup() { const timer = ref(null); const count = ref(0); onMounted(() => { console.log('组件已挂载,开始定时器'); timer.value = setInterval(() => { count.value++; }, 1000); }); onUnmounted(() => { console.log('组件已卸载,清理定时器'); clearInterval(timer.value); }); return { count }; }, }; </script>

4. 总结

Vue.js 凭借其优雅的 声明式渲染、强大的 Proxy 响应式系统 以及高度模块化的 组件化架构,成为了现代前端开发的主流框架之一。

  • 声明式渲染与模板语法 降低了 DOM 操作的心智负担,让开发者专注于状态与视图的映射。
  • 响应式系统 通过 refreactive、计算属性与侦听器,构建了精准且高效的数据驱动模型。
  • 组件化架构 结合 Props、Events 与生命周期钩子,提供了清晰的代码组织方式,极大提升了大型项目的可维护性与复用性。

深入理解并熟练运用这些核心概念,是构建高性能、可扩展 Vue.js 应用的坚实基础。在实际开发中,合理搭配 Vue Router 与 Pinia 等生态工具,将进一步释放 Vue.js 的工程化潜力。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U