高优先级知识点,需要熟练掌握。
<div class="out-box" @click.stop.prevent="clickEvent(2)" ></div>
<svg :viewBox.camel="viewBox"></svg>
<div @scroll.passive="onScroll">...</div>
//普通键
.enter
.tab //<input @keydown.tab="handleTab"> 监听 Tab 键按下
.delete //<input @keyup.delete="handleDelete"> 监听 Delete 键松开
.backspace //<input @keydown.backspace="handleBackspace"> 监听 Backspace 键按下
.space
.esc //<input @keyup.esc="handleEsc"> 监听 Esc 键松开
.up
.down
.left
.right
//系统修饰键
.ctrl // <input @keyup.ctrl.enter="submitForm"> 监听 Ctrl + Enter 组合键
.alt // <button @click.alt="openMenu"> 监听 Alt 键按下并点击
.shift // <input @keyup.shift.enter="submitForm"> 监听 Shift + Enter 组合键
.meta //<input @keyup.meta.c="copy"> 监听 Windows/Command + C 组合键
// 非普通键
v-on:keyup.f1
click.left
click.right
click.middle
<a href="http://www.w3school.com.cn" @click.prevent="clickEvent(1)">点击A标签</a>
<div class="out-box" @click.self="clickEvent(2)" >
触发输出2
<div class="in-box" @click="clickEvent(1)">触发输出1</div>
</div>
<div class="out-box" @click.capture="clickEvent(2)" >
触发输出2
<div class="in-box" @click="clickEvent(1)">触发输出1</div>
</div>
点击‘触发输出1’先输出2,再输出1
<input type="text" v-model.lazy="lazyValue">
1:vue 基础(41 道题)
2:248 个知识点 地址 (opens new window)
3:Vue 最全知识点 地址 (opens new window)
4:vue 高频
5:vue 缓存及路由和生命周期触发的完整流程
在 Vue 中,异步组件是指那些在需要时才加载的组件,而不是在应用初始化时就立即加载。这种机制可以显著提升页面加载速度和用户体验,尤其是在大型单页应用(SPA)中,通过懒加载的方式按需加载组件,避免不必要的资源浪费。
const AsyncComponent = defineAsyncComponent(() => import('./MyComponent.vue'))
Vue 也支持通过 JavaScript 的 import() 动态导入语法来实现异步组件加载。
const AsyncComponent = () => import('./MyComponent.vue')
const AsyncComponent = defineAsyncComponent({
loader: () => import('./components/MyComponent.vue'),
loadingComponent: Loading, // 加载中的组件
errorComponent: ErrorComponent, // 错误组件
delay: 200, // 延迟 200ms 显示加载状态
timeout: 3000 // 超过 3 秒显示错误组件
})
Vue 的数据响应式系统和虚拟 DOM + Diff 算法是紧密协作的:
Vue 3 引入了一个全新的编译器,能够生成更高效的渲染函数。这个编译器在编译过程中进行了一系列优化,例如:
Vue 3 的更新机制基于单次异步队列(single asynchronous queue),它确保在同一事件循环中只进行一次批量更新。再加上现代浏览器的性能提升,使得 Vue 3 能够在大多数情况下提供流畅的用户体验,而无需借助时间分片等复杂的技术。
Vue 的事件机制允许组件之间进行通信,通过 $on、$off、$emit 和 $once 等方法进行事件的订阅、取消订阅、触发和一次性订阅。
class EventEmitter {
constructor() {
this.events = {}
}
// 监听事件
$on(event, callback) {
if (!this.events[event]) {
this.events[event] = []
}
this.events[event].push(callback)
}
// 停止监听事件
$off(event, callback) {
if (!this.events[event]) return
if (!callback) {
// 如果没有传递 callback,移除所有事件监听
this.events[event] = []
} else {
// 移除特定的事件监听
this.events[event] = this.events[event].filter(cb => cb !== callback)
}
}
// 触发事件
$emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(callback => callback.apply(this, args))
}
}
// 只监听一次事件
$once(event, callback) {
const wrapper = (...args) => {
callback.apply(this, args)
this.$off(event, wrapper)
}
this.$on(event, wrapper)
}
}
// 示例
const eventBus = new EventEmitter()
// 监听事件
eventBus.$on('test', msg => console.log('test event:', msg))
// 触发事件
eventBus.$emit('test', 'Hello, World!')
// 监听一次事件
eventBus.$once('once', msg => console.log('once event:', msg))
// 触发一次性事件
eventBus.$emit('once', 'This should appear once')
eventBus.$emit('once', 'This should not appear')
// 停止监听事件
eventBus.$off('test')
// 触发事件(已经移除监听)
eventBus.$emit('test', 'This should not appear')
computed 计算属性本质上是具有缓存功能的特殊方法。它们只有在其依赖的响应式属性发生变化时才会重新计算,否则返回缓存的值。
Vue 的模板编译过程主要分为三个步骤:
受到了 Flux 和 Redux 的影响
vue-loader(或 Vite 的 @vitejs/plugin-vue)解析到 <style scoped> 时,会给当前组件计算出一个形如 data-v-7ba5bd90 的稳定 hash 属性名。
简言之,scoped CSS 并没有黑科技,只是借助属性选择器 + 编译期自动重写把“全局”变成了“局部”。
Vue3:baseCompile() -> compile() -> generate()
Vue3 的响应式 = Proxy 做数据劫持 + WeakMap 做依赖收集/清理 + Effect 做副作用追踪 + 位运算标记追踪类型,最终把「数据变更」映射到「副作用函数重新执行」。
this.$set(this.list, index, value)
Vue3 的 computed 通过 dirty 标志 + 惰性求值 + 双向依赖图 + 调度器 实现缓存:
export class ComputedRefImpl<T> {
public dep?: Dep = undefined // 依赖自己的 effects(例如渲染函数)
private _value!: T // 缓存值
private _dirty = true // 脏标志:true 表示需要重新计算
public readonly effect: ReactiveEffect<T>
constructor(getter: () => T) {
// 关键:传进去一个自定义调度器
this.effect = new ReactiveEffect(getter, () => this.trigger())
}
get value() {
// 读 value 时
if (this._dirty) {
this._dirty = false
this._value = this.effect.run()!
}
trackRefValue(this) // 收集当前 computed 的依赖
return this._value
}
}
虚拟 DOM(Virtual DOM)是一种编程概念,它是对真实 DOM 的一种抽象,它描述了真实 DOM 的结构及内容,但不包含实际的 DOM 节点,而是用一个 JavaScript 对象来描述 DOM 节点及其属性。
Vue3 的 diff 算法是基于 key 和索引位置的,即通过比较新旧 VNode 的 key 和索引位置来判断是否需要更新,如果 key 相同,则认为是相同节点,否则认为是不同的节点。
Vue3 与 Vue2 的 diff 算法核心区别在于对比策略和性能优化逻辑,Vue3 通过双端对比+最长递增子序列(LIS)实现了更高效的 DOM 更新,具体差异可从以下 4 点展开:
Vue3 的 diff 算法的优化主要有:
Tree shaking 是一种通过清除多余代码方式来优化项目打包体积的技术,专业术语叫 Dead code elimination
使用 Proxy API 取代 defineProperty API 是为了提升性能、增强功能,并提供更好的开发体验和错误提示。这些改进使得 Vue 3.0 的响应式系统更加高效、灵活和可靠。
自定义指令(Custom Directive)是一种用于扩展 Vue 的模板语法的机制。通过自定义指令,你可以在 DOM 元素上添加自定义行为,并在元素插入、更新和移除时进行相应的操作。
应用场景:
修饰符处理了许多 DOM 事件的细节
keep-alive 是 vue 中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染 DOM
Vue 在进行虚拟 DOM 的 diff 算法时,会使用 key 来匹配新旧节点,以确定节点的更新、移动或删除。它通过 key 属性来判断两个节点是否代表相同的实体,而不仅仅是根据它们的内容是否相同。这样可以保留节点的状态和避免不必要的 DOM 操作。
key 必须是唯一且稳定的,最好使用具有唯一标识的值,例如使用数据的唯一 ID。同时,不推荐使用随机数作为 key,因为在每次更新时都会生成新的 key,导致所有节点都重新渲染,无法复用已有的节点,降低性能。
默认插槽
具名插槽
<!-- 子组件 -->
<template>
<slot>默认插槽后备的内容</slot>
<slot name="content">具名插槽后备的内容</slot>
</template>
<!-- 父组件 -->
<template v-slot:default>默认插槽内容</template>
<template v-slot:content>具名插槽⽤插槽名做参数...</template>
作用域插槽
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
// 3.4之前使用
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
<!-- Child.vue -->
<script setup>
const model = defineModel()
function update() {
model.value++
}
</script>
<template>
<div>Parent bound v-model is: {{ model }}</div>
<button @click="update">Increment</button>
</template>
<!-- Parent.vue -->
<Child v-model="countModel" />
组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在 Vue 中每一个.vue 文件都可以视为一个组件 组件的优势:
插件就是扩展 Vue 的功能,插件可以包含组件、指令、过滤器等各种功能,插件可以被安装到 Vue 实例上,也可以单独使用。 插件的优势:
函数作用域隔离组件之间的数据污染