面试相关
1. 为什么使用 虚拟DOM
TIP
- 创建真实 DOM 的代码高:真实 DOM 节点 node 的属性很多,而是用 vnode 仅实现一些必要的属性
- 触发多次浏览器的重绘和回流:使用 vnode,相当于加了一个缓存,让一次数据变动先在
vnode
进行修改,然后diff之后对所有产生差异的节点集中一次对DOM tree
进行修改,可以减少浏览器的 重绘 和 回流 - 虚拟DOM本质是一个js对象,具备跨平台的能力,可以实现不同平台的准确显示
- vnode 不仅在性能上有很好的收益,也使得 Vue 具备了现代框架应有的高级特性
2. Vue 组件通信
TIP
父向子组件传值
- 父组件发送的形式以属性的形式绑定到子组件上
- 然后子组件使用
props
接收 - 在
props
中使用驼峰形式,在模版中可以使用短横线形式
子向父组件传值
- 子组件使用
$emit()
触发事件 $emit()
第一个参数为自定义事件名称
,后续参数为所传递的 数据- 父组件使用
v-on
缩写为@
监听子组件的事件
- 子组件使用
兄弟之间的传值
provide inject
vuex pinia
eventBus
3. Vue 中 key 的作用,为什么不推荐使用 index 作为 key
TIP
- key 的作用主要是为了高效的更新虚拟 DOM(直接会根据key 的变化重新排列元素的属性,并移除 key 不存在的元素)
- 当以数组的下标index作为key时,其中一个元素(增删改查)发生了变化可能会导致所有元素的 key 值发生变化
4. v-show
和 v-if
的区别
TIP
v-show
原理是修改元素的 css 属性display: none
来决定是否显示元素v-if
是用通过 三元表达式 直接判断和操作 DOM 来切换显示
5. Vue 导航守卫的钩子函数有哪些
TIP
全局守卫
router.beforeEach
全局前置守卫,进入路由前router.beforeResolve
全局解析守卫,在beforeRouteEnter
调用之后调用router.afterEach
全局后置钩子,进入路由之后
路由组件内的守卫
beforeRouteEnter()
进入路由前beforeRouteUpdate()
路由复用同一个组件时beforeRouteLeave()
离开当前路由时
6. Vue 编程式导航跳转传参的方式有哪些
TIP
// 命名的路由
router.push({ name: 'user', params: { userId: 'xx' } })
// 带查询参数,变成 /register?plan=xxx
router.push({ path: 'register', query: { plan: 'xx' } })
7. Vue3 性能提升的几个方面
TIP
编译阶段
diff
算法优化 (最长递增子序列)(静态标记,会发生变化的地方添加flag
,发生变化时直接找该地方进行比较)- 静态提升(不参与更新的元素,做静态提升,只会被创建一次,在渲染时直接复用)
- 事件监听缓存(开启事件缓存,diff 算法时直接复用)
SSR
优化
源码体积
- 移除了一些不常用的 API
tree shanking
响应式系统
vue2
使用defineProperty
来劫持整个对象,然后深度遍历所有属性,给每个属性添加getter
和setter
来实现响应式vue3
使用proxy
重写了响应式系统,因为proxy
可以对真个对象进行监听,不需要深度遍历- 可以监听动态属性的添加
- 可以监听数组的
索引
和数组的length
属性 - 可以监听 删除属性
8. Composition API
和 Options API
对比
TIP
- 在 逻辑组织 和 逻辑复用 方面,前者优于后者
- 前者几乎都是函数,所有
ts
类型推断更好的支持 - 前者的
tree-shanking
更友好,代码更容易压缩 - 前者减少了
this
的使用,避免了出现this
指向不明的情况 - 对于小型组件,后者更为方便
9. Vue3 和 Vue2 对比,以及 Vue3 的新特性
TIP
- 速度更快
- 重写了
虚拟Dom
实现 - 编译模版的优化
- 更高效的组件初始化
undate
性能提升 1.3~2 倍SSR
速度提高了 2~3 倍
- 重写了
- 体积更小
- 对开发人员,能够对Vue实现更多的其它功能,不用担心包的体积过大
- 对使用者,打包出来的包体积变小了
- 更易维护
- 可与
Options API
一起使用 - 灵活的逻辑组合与复用
- Vue3 单模块可以与其他框架搭配使用
- 更好的 TS 支持
- 可与
- 更接近原生
- 可以自定义渲染器 API
- 更易使用
TIP
Vue3 的新特性
Framents
(多根节点)Teleport
(传送门)Suspense
(异步组件)Composition API
(组合式 API)createRenderer
(自定义渲染器)
10. diff 算法比较
TIP
简单 Diff 算法
简单 Diff 算法的核心逻辑是,拿新的一组子节点的节点去旧的一组子节点中寻找可复用的节点。如果找到了,则记录该节点的位置索引。我们把这个位置索引称为最大索引。在整个更新过程中,如果一个节点的索引值小于最大索引,则说明该节点对应的真实DOM元素需要移动。
双端 Diff 算法
双端 Diff 算法指的是,在新旧两组子节点的四个端点之间分别进行比较,并试图找到可复用的节点。相比简单 Diff 算法,双端 Diff 算法的优势在于,对于同样的更新场景,执行的DOM移动操作次数更少。
快速 Diff 算法
快速 Diff 算法在实测中性能更优,先处理新旧两组子节点中相同的前置节点和后置节点。当前置节点和后置节点都处理完成后,如果无法简单的通过挂载节点或卸载已经不存在的节点来完成更新,则需要根据节点的索引关系,构造出一个最长递增子序列。最长递增子序列所指向的节点即为不需要移动的节点。(其中元素值 为 -1 的则为需要新增的节点)
11. Vue3 中编译优化有哪些?
TIP
- 补丁标志 : 编译优化的核心在于区分
动态节点
和静态节点
。Vue3 会为 动态节点 打上补丁标志 patchFlag
Block
树 : 提出 Block 概念,本质上也是一个虚拟节点,但是会多出一个dynamicChildren
数组,会收集所有的动态节点,利用了createVNode
函数 和createBlock
函数的层层嵌套的特点,即以 ”由内向外“ 的方式执行。再配合一个临时存储动态节点的节点栈,即可完成动态子节点的收集- 静态提升 : 能够减少更新时创建虚拟 DOM 带来的性能开销和内存占用
- 预字符串化 : 在静态提升的基础上,对静态节点进行字符串化。这样做能够减少创建虚拟节点产生的性能开销和内存占用
v-once
指令 : 缓存全部或部分虚拟节点,能够避免组件更新时重新创建虚拟 DOM 带来的性能开销,也可以避免无用的 Diff 操作
12. Vue 如何重新加载页面
利用 nextTick 在刷新后重新设置为 true 来使得页面或 局部刷新
<template>
<!-- 局部 -->
<div v-if="load">xx</div>
<!-- 页面 -->
<router-view v-if="load" />
</template>
<script>
export default {
setup() {
const load = ref(true)
const reload = () => {
load.value = false
nextTick(() => {
load.value = true
})
}
}
}
</script>
13. Vue 常用指令及作用
v-on
: 给标签绑定事件监听器,可以缩写为@
,有如下修饰符:.stop
: 调用event.stopPropagation()
.prevent
: 调用event.preventDefault()
.capture
: 在捕获模式添加事件监听器.self
: 只有事件从元素本身发出才触发处理函数.{keyAlias}
: 只在某些按键下触发处理函数.once
: 最多触发一次处理函数.left
: 只在鼠标左键事件触发处理函数.right
: 只在鼠标右键事件触发处理函数.middle
: 只在鼠标中键事件触发处理函数.passive
: 通过{ passive: true }
附加一个 DOM 事件
v-bind
: 对属性进行动态绑定,可以缩写为:
,有如下修饰符:.camel
: 将短横线命名的attribute
转变为驼峰式命名.prop
: 强制绑定为DOM property
.attr
: 强制绑定为DOM attribute
v-slot
: 用于声明具名插槽或是期望接收 props 的作用域插槽,可以缩写为#
v-for
: 循环遍历数组元素v-show
: 通过display
css 属性控制是否显示内容v-if
: 通过 js 三元表达式来显示和隐藏标签v-else
: 与v-if
连用v-text
: 解析文本,更新元素的文本内容v-html
: 解析 html 标签,更新元素的innerHTML
v-model
: 在表单输入元素或组件上创建双向绑定,适用于<input>
<select>
<textarea>
components
, 有如下修饰符:.lazy
: 监听change
事件而不是input
.number
: 将输入的合法符串转为数字.trim
: 移除输入内容两端空格
v-pre
: 跳过该元素及其所有子元素的编译v-once
: 仅渲染元素和组件一次,并跳过之后的更新v-memo
: 缓存一个模板的子树。在元素和组件上都可以使用v-cloak
: 用于隐藏尚未完成编译的 DOM 模板
14. Vue 中 v-for 的作用
- key 的作用主要是为了更高效的更新 虚拟 DOM,因为可以精确找到相同节点
- Vue 在 patch 中,判断两个节点是否是相同节点,key 是必要条件。如果不写 key,会导致元素频繁更新,整个 patch 过程比较低效,影响性能
- 避免使用数组下标做 key,key值不唯一可能会导致视图错误,和不会触发过渡效果等
15. Vue 的生命周期
beforeCreate
: 在组件实例初始化完成之后立即调用created
: 在组件实例处理完所有与状态相关的选项后调用beforeMount
: 在组件被挂载之前调用mounted
: 在组件被挂载之后调用beforeUpdate
: 在组件即将因为一个响应式状态变更而更新其 DOM 树之前调用updated
: 在组件因为一个响应式状态变更而更新其 DOM 树之后调用beforeUnmount
: 在一个组件实例被卸载之前调用unmounted
: 在一个组件实例被卸载之后调用activated
: 若组件实例是<KeepAlive>
缓存树的一部分,当组件被插入到 DOM 中时调用deactivated
: 若组件实例是<KeepAlive>
缓存树的一部分,当组件从 DOM 中被移除时调用errorCapured
: 在捕获了后代组件传递的错误时调用
16. Vue 子组件和父组件执行顺序
**组件挂载更新时都是在
beforeMount
之后mounted
之前 进行patch
**
加载渲染过程:父 beforeCreate => 父 created => 父 beforeMount => 子 beforeCreate => 子 created => 子 beforeMount => 子 mounted => 父 mounted
更新过程:父组件 beforeUpdate =>子组件 beforeUpdate =>子组件 updated => 父组件 updated