Vue相关基础
Vue基本原理
MVVM
1. MVVM 的定义
MVVM(Model-View-ViewModel)是一种软件架构模式,主要用于分离用户界面逻辑和业务逻辑。它由以下三个部分组成:
- Model(模型):表示数据和业务逻辑层,负责管理应用程序的数据。
- View(视图):表示用户界面层,负责展示数据给用户。
- ViewModel(视图模型):==作为 Model 和 View 之间的桥梁,负责将 Model 的数据转换为 View 可以使用的格式,并监听 Model 的变化来更新 View==。
2. MVVM 的核心特点
- 数据绑定:ViewModel 和 View 之间通过双向绑定机制保持同步。
- 命令绑定:View 上的用户交互(如点击按钮)可以通过命令绑定触发 ViewModel 中的方法。
- 数据驱动:通过修改 ViewModel 中的数据,自动更新 View。
Vue 的 MVVM 实现
Vue 是一个典型的 MVVM 框架,它的运作原理可以分为以下几个方面:
1. 数据绑定
Vue 使用数据绑定机制将 Model 和 View 连接起来。具体来说:
- 单向绑定:从 Model 到 View 的数据流动(即数据变化时自动更新视图)。
- 双向绑定:不仅支持从 Model 到 View 的数据流动,还支持从 View 到 Model 的数据流动(例如输入框的值会自动更新到数据模型中)。
2. 响应式系统
Vue 的响应式系统是其实现 MVVM 的核心。以下是其工作原理:
- 数据劫持:Vue 通过
Object.defineProperty或 Proxy(Vue 3 中使用)对数据对象进行拦截,监听数据的变化。 - 依赖收集:当模板中的数据被访问时,Vue 会自动记录哪些数据被使用,从而建立数据和视图之间的依赖关系。
- 视图更新:当数据发生变化时,Vue 会通知所有依赖该数据的视图进行更新。
3. 模板编译
Vue 的模板编译器会将 HTML 模板解析为虚拟 DOM(Virtual DOM),并生成渲染函数。具体流程如下:
- 解析模板:将模板字符串解析为抽象语法树(AST)。
- 优化:对 AST 进行静态节点标记,提升性能。
- 生成渲染函数:将 AST 转换为可执行的 JavaScript 渲染函数。
4. 虚拟 DOM
Vue 使用虚拟 DOM 来提高渲染性能。虚拟 DOM 是一种轻量级的 JavaScript 对象,用于表示真实的 DOM 结构。其主要作用包括:
- Diff 算法:当数据变化时,Vue 会比较新旧虚拟 DOM 树,计算出最小的差异。
- 批量更新:Vue 会将多个状态变更合并为一次 DOM 更新,减少操作次数。
Vue 的运作流程
以下是 Vue 在运行时的基本流程:
- 初始化阶段
- 创建 Vue 实例时,框架会初始化选项(如
data、methods、computed等)。 - 使用
Object.defineProperty或 Proxy 对data属性进行响应式处理。 - 编译模板,生成渲染函数。
- 创建 Vue 实例时,框架会初始化选项(如
- 挂载阶段
- 将 Vue 实例挂载到指定的 DOM 元素上(==通过
el配置项==)。 - 调用
beforeMount和mounted生命周期钩子。
- 将 Vue 实例挂载到指定的 DOM 元素上(==通过
- 渲染阶段
- 执行渲染函数,生成虚拟 DOM。
- 将虚拟 DOM 转换为真实 DOM 并插入页面。
- 更新阶段
- 当数据发生变化时,触发依赖收集机制。
- Vue 重新执行渲染函数,生成新的虚拟 DOM。
- 使用 Diff 算法比较新旧虚拟 DOM,更新真实 DOM。
- 销毁阶段
- 销毁 Vue 实例时,清理所有的事件监听器和子组件。
- 调用
beforeDestroy和destroyed生命周期钩子。
Vue 的 MVVM 特点
1. 数据驱动
Vue 的核心理念是“数据驱动视图”,开发者只需关注数据的变化,而无需手动操作 DOM。例如:
new Vue({ el: '#app', data: { message: 'Hello Vue!' }});当 message 的值发生变化时,Vue 会自动更新视图。
2. 双向绑定
Vue 提供了内置的 v-model 指令,实现了数据的双向绑定。例如:
<input v-model="message" /><p>{{ message }}</p>在上述代码中,输入框的值和 message 数据会相互同步。
3. 模板语法
Vue 提供了简洁的模板语法,方便开发者快速构建动态视图。例如:
<ul> <li v-for="item in items">{{ item.name }}</li></ul>4. 计算属性和监听器
- 计算属性:用于基于已有数据派生新数据。
- 监听器:用于监听数据变化并执行特定逻辑。
Vue生命周期
1.生命周期
- 创建阶段(Creation):Vue 实例被创建时的初始化过程。包括 beforeCreate 和 created 钩子。
- 挂载阶段(Mounting):Vue 实例将模板编译并挂载到 DOM 上的过程。包括 beforeMount 和 mounted 钩子。
- 更新阶段(Updating):当数据发生变化时,Vue 会对 DOM 进行重新渲染和更新的过程。包括 beforeUpdate 和 updated 钩子。
- 销毁阶段(Destroying):Vue 实例被销毁时的清理过程。包括 beforeDestroy 和 destroyed 钩子(在 Vue 3 中分别为 beforeUnmount 和 unmounted)。
2.八个核心生命周期钩子函数
1. beforeCreate
- 触发时机:实例刚刚被创建,尚未初始化数据和事件监听器。
- 特点:
- 数据观测 (
data) 和方法 (methods) 尚未初始化。 $el属性不可用。
- 数据观测 (
- 用途:几乎没有实际用途,通常用于调试或了解初始化前的状态。
beforeCreate() { console.log(this.$data); // undefined}2. created
- 触发时机:实例已经完成数据观测、计算属性、方法等初始化,但尚未挂载到 DOM。
- 特点:
- 可以访问
data和methods。 $el属性仍然不可用。
- 可以访问
- 用途:
- 发送异步请求(如获取 API 数据)。
- 初始化一些数据或绑定事件。
created() { console.log(this.$data); // 已经初始化 this.fetchData(); // 调用方法发送请求}3. beforeMount
- 触发时机:模板编译完成,但尚未挂载到 DOM。
- 特点:
- 模板已经编译为虚拟 DOM,但尚未渲染到页面上。
- ==
$el属性存在,但内容为空。==
- 用途:
- 类似于
created,可以在此处进行初始化操作。
- 类似于
beforeMount() { console.log(this.$el); // 存在,但内容为空}==4. mounted==
- 触发时机:实例已经挂载到 DOM,页面可以正常显示。
- 特点:
- ==
$el属性可用,且已替换为真实的 DOM 元素。== - 页面上的 DOM 已经渲染完成。
- ==
- 用途:
- 操作 DOM 元素。
- 初始化第三方插件(如地图、图表等)。
mounted() { console.log(this.$el); // 真实的 DOM 元素 this.initMap(); // 初始化地图}5. beforeUpdate
- 触发时机:数据发生变化,DOM 更新之前。
- 特点:
- 数据已经更新,但 DOM 尚未更新。
- 可以访问旧的 DOM 结构。
- 用途:
- 在 DOM 更新前进行额外的操作(如移除手动绑定的事件监听器)。
beforeUpdate() { console.log('DOM 即将更新');}6. updated
- 触发时机:数据变化导致的 DOM 更新完成后。
- 特点:
- DOM 已经更新完毕。
- 不建议在此修改状态,否则可能引发无限循环。
- 用途:
- 操作更新后的 DOM 元素。
updated() { console.log('DOM 已更新');}==7. beforeDestroy (Vue 2) / beforeUnmount (Vue 3)==
- 触发时机:实例即将被销毁。
- 特点:
- ==实例仍然完全可用。==
- 用途:
- 清理定时器、解绑事件监听器等。
beforeDestroy() { console.log('实例即将销毁'); clearInterval(this.timer); // 清理定时器}8. destroyed (Vue 2) / unmounted (Vue 3)
- 触发时机:实例已经被销毁。
- 特点:
- 所有指令和事件监听器已被移除。
- 子实例也被销毁。
- 用途:无特殊用途,主要用于确认实例已销毁。
destroyed() { console.log('实例已销毁');}关于$el结点
$el是 ==Vue 实例的一个属性,表示 Vue 组件或应用挂载到页面上的根 DOM 元素。==- 它==只有在组件被挂载(即
mounted钩子之后)时才可用==。
1.使用场景
- 访问挂载的 DOM 元素
在某些情况下,你可能需要直接操作 DOM,例如初始化第三方库(如地图、图表等),此时可以通过
$el获取挂载点的 DOM 元素。 - 调试和检查
使用
$el可以帮助开发者快速定位挂载的 DOM 元素,方便调试。 - 动态操作 DOM
如果需要对挂载后的 DOM 进行一些额外的操作(例如添加样式、绑定事件等),可以通过
$el来实现。
2.注意事项
- ==避免直接修改
$el的内容== ==Vue 的核心理念是通过数据驱动视图更新,而不是直接操作 DOM。==因此,尽量避免直接修改$el的内容,否则可能导致 Vue 的响应式系统失效。 - 确保
$el已经挂载 如果在mounted钩子之前尝试访问$el,会得到undefined或空值。 - Vue 3 中的变化
在 Vue 3 中,
$el的行为与 Vue 2 类似,但在使用 Composition API 时,由于没有this上下文,可以通过ref和模板引用的方式获取 DOM 元素。
模板语法
1. 插值(Interpolation)==单向绑定 用于文本数据==
1.1 基本用法
Vue 使用双大括号 {{ }} 来进行插值,将数据动态插入到模板中。
<div id="app"> <p>{{ message }}</p></div>
<script> new Vue({ el: '#app', data: { message: 'Hello Vue!' } });</script>- 特点:
- 双大括号中的内容会被解析为 JavaScript 表达式。
- 插值只能用于文本内容,不能用于属性绑定。
1.2 注意事项
- 如果需要绑定属性(如
src或class),应使用v-bind指令。 - 插值不支持多行表达式或复杂逻辑,仅限于简单的表达式。
2. 指令(Directives)
指令是 Vue 提供的特殊属性,以 v- 开头,用于增强 HTML 的功能。
2.1 v-html ==(单向插入html文本)==
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令:
<template> <div> <div v-html="content"></div> </div></template>
<script>export default { data() { return { content: '<p style="color: red;">这是通过 v-html 渲染的内容!</p>' }; }};</script>这个 span 的内容将会被替换成为 property 值 rawHtml,直接作为 HTML——会忽略解析 property 值中的数据绑定。注意,你不能使用 v-html 来复合局部模板,因为 Vue 不是基于字符串的模板引擎。反之,对于用户界面 (UI),组件更适合作为可重用和可组合的基本单位。
你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要 对用户提供的内容使用插值。
2.2 v-bind:属性绑定 ==单向绑定html属性值 v-bind :==
基本用法
v-bind 用于动态绑定 HTML 属性。
<img v-bind:src="imageSrc" alt="Vue Logo"><!-- 简写 --><img :src="imageSrc" alt="Vue Logo">示例
data: { imageSrc: 'https://example.com/vue-logo.png'}动态绑定多个属性
可以结合对象动态绑定多个属性。
<a v-bind="{ href: url, target: '_blank' }">链接</a><!-- 简写 --><a :href="url" :target="'_blank'">链接</a>注意事项
v-bind支持动态计算属性值。- 对于布尔属性(如
disabled),如果值为false,属性会被移除。
2.2 v-on:事件监听 ==v-on:事件 /@事件==
基本用法
v-on 用于监听 DOM 事件,并执行方法或表达式。
<button v-on:click="increment">点击我</button><!-- 简写 --><button @click="increment">点击我</button>示例
data: { count: 0},methods: { increment() { this.count++; }}内联处理器
可以在事件处理函数中直接传参或调用表达式。
<button @click="greet('你好')">打招呼</button>methods: { greet(message) { alert(message); }}修饰符
v-on 支持多种修饰符来改变事件行为:
| 修饰符 | 功能 |
|---|---|
.stop | 阻止事件冒泡 |
.prevent | 阻止默认行为 |
.capture | 在捕获阶段触发事件 |
.self | 只当事件从目标元素本身触发时才触发 |
.once | 只触发一次 |
1. .stop
- 作用:阻止事件冒泡(Event Bubbling)。
- 场景:当一个事件从子元素传播到父元素时,可以使用
.stop阻止这种传播。 - 示例:
<div @click="handleParentClick"> Parent <button @click.stop="handleChildClick">Child</button></div>在这个例子中,点击按钮时,handleChildClick 会被触发,但 handleParentClick 不会被触发,因为事件冒泡被阻止了。
2. .prevent
- 作用:阻止默认行为(Default Behavior)。
- 场景:适用于表单提交、链接跳转等需要阻止默认操作的场景。
- 示例:
<form @submit.prevent="onSubmit"> <button type="submit">Submit</button></form>在这个例子中,表单提交时不会刷新页面,而是调用 onSubmit 方法。
3. .capture
- 作用:在捕获阶段(Capture Phase)触发事件,而不是在冒泡阶段。
- 场景:当需要在事件传播的早期阶段处理事件时使用。
- 示例:
<div @click.capture="handleParentClick"> Parent <button @click="handleChildClick">Child</button></div>在这个例子中,点击按钮时,handleParentClick 会在 handleChildClick 之前被触发,因为事件在捕获阶段就被捕获了。
4. .self
- 作用:只有当事件是从目标元素本身触发时才触发处理函数。
- 场景:防止子元素的事件影响父元素。
- 示例:
<div @click.self="handleDivClick"> Parent <button>Child</button></div>在这个例子中,点击按钮时,handleDivClick 不会被触发,因为事件不是从 div 本身触发的。
5. .once
- 作用:事件只触发一次。
- 场景:适用于只需要响应一次的事件。
- 示例:
<button @click.once="handleClick">Click me</button>在这个例子中,点击按钮后,handleClick 只会执行一次,后续点击将不再触发。
总结
这些修饰符可以单独使用,也可以组合使用。例如:
<form @submit.prevent.stop="onSubmit"> <button type="submit">Submit</button></form>==在这个例子中,.prevent 阻止了表单的默认提交行为,.stop 阻止了事件冒泡。==
2.3 v-if / v-else-if / v-else:条件渲染
基本用法
根据条件决定是否渲染元素。
<div v-if="isVisible">显示此内容</div><div v-else>显示其他内容</div>示例
data: { isVisible: true}注意事项
v-if是惰性的,只有在条件为真时才会渲染。- ==如果需要频繁切换,建议使用
v-show。(反复渲染虚拟dom)==
2.4 v-show:条件显示
基本用法
无论条件如何,v-show 总是渲染元素,通过 CSS 的 display 属性控制显示/隐藏。
<div v-show="isVisible">显示此内容</div>区别
| 特性 | v-if | v-show |
|---|---|---|
| 渲染时机 | 条件为真时渲染 | 始终渲染 |
| 切换性能 | 第一次切换较慢 | 切换较快 |
| 场景 | 不经常切换 | 频繁切换 |
2.5 v-for:列表渲染
基本用法
用于循环渲染列表。
<ul> <li v-for="(item, index) in items" :key="index">{{ item }}</li></ul>示例
data: { items: ['苹果', '香蕉', '橙子']}参数说明
(item, index):item是当前项,index是索引。- ==
key:必须为每个节点指定唯一键值,推荐使用数组中的唯一标识。==
对象迭代
也可以用于遍历对象的键值对。
<ul> <li v-for="(value, key) in object">{{ key }}: {{ value }}</li></ul>data: { object: { name: 'Alice', age: 25 }}2.6 v-model:==双向绑定 v-model===
基本用法
==用于表单输入与数据的双向绑定==。
<input v-model="message" placeholder="编辑消息"><p>你输入的内容是:{{ message }}</p>示例
data: { message: ''}修饰符
v-model 支持以下修饰符:
| 修饰符 | 功能 |
|---|---|
.lazy | 在 change 事件后同步数据 |
.number | 自动将输入值转换为数字 |
.trim | 自动去除首尾空格 |
1. .lazy
- 功能:在
change事件后同步数据,而不是默认的input事件。 - 场景:适用于需要在用户完成输入(如失去焦点)后再更新绑定值的情况。
- 示例:
<input v-model.lazy="message" type="text">在这个例子中,message 的值只有在输入框失去焦点或按下回车键时才会更新。
2. .number
- 功能:自动将输入值转换为数字类型。
- 场景:适用于需要确保输入值为数字类型的场景,例如表单中的数字字段。
- 示例:
<input v-model.number="age" type="number">如果用户输入了 "42",age 的值会被自动转换为数字 42。如果输入的内容无法转换为数字(如 "abc"),则值会变为 NaN。
3. .trim
- 功能:自动去除输入值的首尾空格。
- 场景:适用于需要清理用户输入中的多余空格的情况。
- 示例:
<input v-model.trim="name" type="text">如果用户输入了 " Alice ",name 的值会被自动处理为 "Alice"。
总结
这些修饰符可以单独使用,也可以组合使用。例如:
<input v-model.lazy.trim.number="value" type="text">在这个例子中:
.lazy:只在change事件后同步数据。.trim:自动去除首尾空格。.number:将输入值转换为数字。
3. 表达式(Expressions)
Vue 的模板支持简单的 JavaScript 表达式。可放入插槽
示例
<p>{{ number + 1 }}</p><p>{{ ok ? 'YES' : 'NO' }}</p><p>{{ message.split('').reverse().join('') }}</p>限制
- ==模板中只能包含单个表达式,不能包含语句(如
if或for)。== - 复杂逻辑应提取到计算属性或方法中。
4. 过滤器(Filters)
过滤器用于格式化文本输出。
基本用法
<p>{{ message | capitalize }}</p>定义过滤器
filters: { capitalize(value) { if (!value) return ''; return value.charAt(0).toUpperCase() + value.slice(1); }}全局过滤器
可以通过 Vue.filter 定义全局过滤器。
Vue.filter('capitalize', function (value) { if (!value) return ''; return value.charAt(0).toUpperCase() + value.slice(1);});链式调用
可以同时应用多个过滤器。
<p>{{ message | filterA | filterB }}</p>5. 修饰符(Modifiers)
修饰符是以 .xxx 形式附加在指令后的特殊后缀,用于改变指令的行为。
常见修饰符
| 指令 | 修饰符 | 功能 |
|---|---|---|
v-on | .prevent | 阻止默认行为 |
v-on | .stop | 阻止事件冒泡 |
v-model | .lazy | 延迟更新 |
v-model | .number | 转换为数字 |
v-model | .trim | 去除首尾空格 |
6. 插槽(Slots)
插槽允许父组件向子组件传递内容。
6.1 默认插槽
<!-- 子组件 --><template> <div> <slot>默认内容</slot> </div></template>
<!-- 父组件 --><my-component> <p>这是从父组件传入的内容</p></my-component>6.2 具名插槽
允许多个插槽。
<!-- 子组件 --><template> <div> <slot name="header"></slot> <slot></slot> <slot name="footer"></slot> </div></template>
<!-- 父组件 --><my-component> <template v-slot:header> <h1>标题</h1> </template> <p>主体内容</p> <template #footer> <p>页脚</p> </template></my-component>6.3 作用域插槽
允许子组件向父组件传递数据。
<!-- 子组件 --><template> <div> <slot :user="user"></slot> </div></template>
<!-- 父组件 --><my-component v-slot:default="slotProps"> {{ slotProps.user.name }}</my-component>7. 计算属性和侦听器
7.1 计算属性
==computed中的计算函数,每当其内部依赖值发生改变就将重新计算== 用于复杂的数据处理逻辑。
computed: { // 计算总价 totalPrice() { return this.carlist .filter(item => { item.isSelected }) .reduce((total, item) => { total + item.Number * item.price }, 0) }},<div style=" width: 60%;text-align: center; margin-top: 20px;"> <h3>Total Price: {{ totalPrice }} $</h3></div>7.2 侦听器
用于监听数据变化并执行副作用。 ==监听属性,这里面的函数名是监听属性的名称 参数是传入新值与旧值==
watch: { message(newVal, oldVal) { console.log(`消息已从 ${oldVal} 更改为 ${newVal}`); }}如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
