Vue3 新增功能有哪些?
- 组合式 API:可以使用函数而不是声明选项的方式书写 Vue 组件。
- 单文件组件中的组合式 API 语法糖
<script setup>
- Teleport :将其插槽内容渲染到 DOM 中的另一个位置。
- Fragments :Vue3 现在支持了多根节点的组件,也就是片段!
- emits 选项 : ArrayEmitsOptions | ObjectEmitsOptions。可以以两种形式声明触发的事件,使用字符串数组的简易形式,或者,使用对象的完整形式。
- Suspense :内置的优化异步加载的组件。
vue3 中 vue2 非兼容性改变有哪些?
-
全局 Vue API 更改为使用应用程序实例:
//全局 Vue API Vue.config; //用程序实例 app import { createApp } from 'vue'; const app = createApp({}); app.config;
-
v-if
与v-for
的优先级:vue2 中v-for
优先,vue3 中v-if
优先; -
v-model
大改:现在可以在同一个组件上使用多个 v-model 绑定,默认名称value
改为modelValue
; -
key
:vue3 会自动生成唯一的 key; -
全局和内部 API 都经过了重构,现已支持 TreeShaking;
vue2 被移除的 API 有哪些?
- 移除 v-on 按键修饰符;
- 移除过滤器 (filter);
- 移除
this.$children
; - 移除$destroy 实例方法。用户不应该再手动管理单个 Vue 组件的生命周期。
- 移除全局函数 set 和 delete 以及实例方法 $set 和 $delete。
什么是组合式 API?
组合式 API (Composition API) 是一系列 API 的集合,使我们可以使用函数而不是声明选项的方式书写 Vue 组件。它是一个概括性的术语,涵盖了以下方面的 API:
- 响应式 API:例如 ref() 和 reactive(),使我们可以直接创建响应式状态、计算属性和侦听器。
- 生命周期钩子:例如 onMounted() 和 onUnmounted(),使我们可以在组件各个生命周期阶段添加逻辑。
- 依赖注入:例如 provide() 和 inject(),使我们可以在使用响应式 API 时,利用 Vue 的依赖注入系统。
什么是选项是 API?
选项式 API (Options API) 使用选项式 API,我们可以用包含多个选项的对象来描述组件的逻辑,例如 data
、methods
和 mounted
。选项所定义的属性都会暴露在函数内部的 this 上,它会指向当前的组件实例。
组合式 API 对比 选项是 API 的优势?
- 更灵活的代码组织:组件逻辑可以按功能模块分组,减少代码的分散,提高代码可读性和维护性。
- 更好的逻辑复用:组合式 API 最基本的优势是它使我们能够通过组合函数来实现更加简洁高效的逻辑复用。在选项式 API 中我们主要的逻辑复用机制是 mixins,而组合式 API 解决了 mixins 的所有缺陷。
- 更好的类型推导:组合式 API 提供了更好的类型推断支持,更适合与 TypeScript 一起使用。
- 更小的生产包体积:由于
<script setup>
形式书写的组件模板被编译为了一个内联函数,和<script setup>
中的代码位于同一作用域。不像选项式 API 需要依赖 this 上下文对象访问属性,被编译的模板可以直接访问<script setup>
中定义的变量,无需从实例中代理。这对代码压缩更友好,因为本地变量的名字可以被压缩,但对象的属性名则不能。
简述一下组合式 API 中的生命周期
在组合式 API 中,生命周期钩子通过 onXXX 函数使用
- setup():组件初始化阶段执行,(定义响应式状态、方法和生命周期钩子)
- onBeforeMount:在组件即将挂载到 DOM 之前被调用。(进行初始化或配置 DOM 相关的操作前的准备工作。)
- onMounted:在组件挂载到 DOM 后被调用。(访问或修改 DOM 元素、发送 AJAX 请求获取数据等。)
- onBeforeUpdate:数据更新之前被调用。(在组件更新前执行特定操作,如读取当前 DOM 状态。)
- onUpdated:数据更新之后被调用。(DOM 操作或清理工作。)
- onBeforeUnmount:在组件即将卸载和销毁之前被调用。(取消订阅、移除事件监听器等。)
- onUnmounted:在组件卸载和销毁后被调用。(进行最终的清理工作。)
ref 和 reactive
- 使用场景:
ref
更适合用于基本数据类型、单一变量、或需要在模板中绑定的值。而reactive
更适合用于复杂的数据结构,如嵌套对象、数组等。 - 访问方式:
ref
需要通过.value
,reactive
不需要,其实 ref 调用的也是 reactive,见下面代码; - 响应式处理:
reactive
修改整个对象都是响应式的,ref
只有第一层是响应式的。
// 封装一个 ref 函数
function ref(val) {
const wrapper = {
value: val
}
return reactive(wrapper)
}
什么是响应丢失问题,怎么解决?
- 原因:展开运算符(…)会导致 reactive 响应丢失
- 使用
toRef
、toRefs
这两个函数。
const obj = reactive({ foo: 1, bar: 2 })
const newObj = { ...toRefs(obj) }
const refFoo = toRef(obj, 'foo')
v-if 与 v-for 的优先级对比?
- vue2 中
v-for
会优先作用。vue3 中v-if
优先级高
Vue3 与 Vue2 中 v-model 的主要区别
- 非兼容:用于自定义组件时,v-model prop 和事件默认名称已更改:
prop:value -> modelValue
; - 事件:
input -> update:modelValue
; - 非兼容:v-bind 的 .sync 修饰符和组件的 model 选项已移除,可在
v-model
上加一个参数代替; - 新增:现在可以在同一个组件上使用多个
v-model
绑定; - 新增:现在可以自定义
v-model
修饰符。
各种 watch 侦听器的区别?
watch 用于声明在数据更改时调用的侦听回调。
- watch:其本质就是观测一个响应式数据,当数据发生变化时通知并执行相应的回调函数。
- watchEffect:立即运行。
- watchPostEffect(): DOM 更新完成后被调用。
- watchSyncEffect():它是一个同步的监视器,每当依赖变化时立即触发回调函数,而不会等待下一次事件循环。
参数说明
- immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined。
watchEffect
- deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。
- flush:调整回调函数的刷新时机。
watchPostEffect()
- onTrack / onTrigger:调试侦听器的依赖。仅会在开发模式下工作。
组件的类型有哪些?
- 全局组件:通过
app.component('ComponentName', ComponentDefinition)
注册,这些组件在整个 Vue 应用中都可以使用。 - 局部组件:通过
components
选项在父组件中注册。这些组件只能在特定的父组件模板中使用。 - 单文件组件:包含
<template></template><script></style>
部分。单文件组件是 Vue 组件的标准形式,每个组件都是一个.vue
文件。 - 函数式组件:主要用于渲染输出,通常接收 props 和 slots。无状态,无生命周期方法。
- 组合式 API 组件:利用 setup() 函数来定义组件逻辑。使用 组合式 API,便于状态逻辑的复用。
- 动态组件:使用 可以根据条件动态地渲染不同的组件。
- 异步组件:可以通过动态
AsyncComp: defineAsyncComponent(() => import('CompA'))
语法来定义。异步组件允许你按需加载组件,从而提高应用的初始加载速度。
Teleport 组件有什么用?
Teleport 组件可以跨越 DOM 层级完成渲染。这种能力在处理模态框(Modals)、悬浮窗口(Tooltips)、通知(Toasts)等 UI 组件时特别有用,因为这些组件通常需要覆盖整个页面或显示在页面的特定位置,而不是它们在组件结构中的位置。
- 可以避免渲染器逻辑代码“膨胀”;
- 可以利用 Tree-Shaking 机制在最终的 bundle 中删除 Teleport 相关的代码,使得最终构建包的体积变小。
怎么定义异步组件?
异步组件允许你按需加载组件,从而提高应用的初始加载速度。
defineAsyncComponent
:定义一个异步组件,它在运行时是懒加载的。参数可以是一个异步加载函数,或是对加载行为进行更具体定制的一个选项对象。Suspense
:是一个用于处理异步组件加载的高级特性,它允许你在等待一个异步组件或函数解析完成之前显示一个备用内容fallback
,解析完之后显示default
。
const AsyncComponent = defineAsyncComponent(() =>import('./components/MyComponent.vue') );
<template>
<Suspense>
<!-- 当异步组件加载完是显示 -->
<template #default>
<AsyncComponent />
</template>
<!-- 当异步组件加载时显示的内容 -->
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</template>
VUE3 有没有看过源码?核心模块有那些?
Vue 的三个核心模块:
- Reactivity Module 响应式模块
- Compiler Module 编译器模块
- Renderer Module 渲染模块
响应式模块:允许我们创建 JavaScript 响应对象并可以观察其变化。当使用这些对象的代码运行时,它们会被跟踪,因此,它们可以在响应对象发生变化后运行。
- Proxy 对象:Vue 3 使用 Proxy 对象来拦截对响应式对象的读写操作;
- track 函数:在 getter 函数内部,Vue 会调用
track
函数来记录依赖关系。这个函数会将当前执行的副作用函数(如渲染函数或计算属性)作为依赖记录下来,并与被访问的属性相关联。 - trigger 函数:在 setter 函数内部,Vue 会调用
trigger
函数来触发依赖更新。这个函数会通知所有依赖于被修改属性的观察者重新执行或重新渲染。
编译器模块:获取 HTML 模板并将它们编译成渲染函数。它的工作流程大致分为三个步骤:
- (1) 分析模板,将其解析为模板 AST。
- (2) 将模板 AST 转换为用于描述渲染函数的 JavaScript AST。
- (3) 根据 JavaScript AST 生成渲染函数代码。
渲染模块:将模板转换为真实的 DOM 节点,响应式数据变化时,更新节点。渲染组件的三个不同阶段:
- 渲染阶段:将调用 render 函数,它返回一个虚拟 DOM 节点。
- 挂载阶段:使用虚拟 DOM 节点并调用 DOM API 来创建网页。
- 补丁阶段:渲染器将旧的虚拟节点和新的虚拟节点进行比较并只更新网页变化的部分。
Proxy 相比于 defineProperty 的优势?
defineProperty:
- 只能对对象的单个属性进行监听和修改;
- 无法监听对象新增或删除属性的操作;
- 可以监听数组属性变化,但性能代价较大,Vue2 中因此放弃了这种方式。
Proxy:
- 可以对整个对象进行拦截和修改。
- 能够监听对象新增、删除属性的操作。
- 对数组的操作(如 push、pop 等)也可以被拦截和修改。
什么是虚拟 DOM (Virtual DOM 简写 VDOM)
VDOM 其实就是用 JavaScript 对象来描述真实的 DOM 结构。
const vnode = {
tag: 'div',
children: [{ tag: 'span', children: 'hello world' }],
};
什么是 diff 算法
渲染器的核心 Diff 算法。简单来说,当新旧 vnode 的子节点都是一组节点时,为了以最小的性能开销完成更
新操作,需要比较两组子节点,用于比较的算法就叫作 Diff 算法。
vue2 和 vue3 以及 react 中 diff 算法的区别
- vue1 简单 Diff 算法:简单 Diff 算法利用虚拟节点的 key 属性,尽可能地复用 DOM 元素,并通过移动 DOM 的方式来完成更新,从而减少不断地创建和销毁 DOM 元素带来的性能开销。
- vue2 双端 Diff 算法:新旧两组子节点的四个端点之间分别进行比较,并试图找到可复用的节点。相比简单 Diff 算法,双端 Diff 算法的优势在于,对于同样的更新场景,执行的 DOM 移动操作次数更少。
- vue3 快速 Diff 算法:在实测中性能最优。它借鉴了文本 Diff 中的预处理思路,先处理新旧两组子节点中相同的前置节点和相同的后置节点。当前置节点和后置节点全部处理完毕后,如果无法简单地通过挂载新节点或者卸载已经不存在的节点来完成更新,则需要根据节点的索引关系,构造出一个最长递增子序列。最长递增子序列所指向的节点即为不需要移动的节点。
- react 递增法:React 是 Fiber 架构的,Fiber 其实是一个单项链表,没有设置反向指针,没法使用双端比对的方式去优化 Diff 算法。
说说vue的渲染机制
Vue 是如何将一份模板转换为真实的 DOM 节点的,又是如何高效地更新这些节点
-
1、编译:Vue 模板被编译为渲染函数:即用来返回虚拟 DOM 树的函数。这一步骤可以通过构建步骤提前完成,也可以通过使用运行时编译器即时完成。
-
2、挂载:运行时渲染器调用渲染函数,遍历返回的虚拟 DOM 树,并基于它创建实际的 DOM 节点。这一步会作为响应式副作用执行,因此它会追踪其中所用到的所有响应式依赖。
-
3、更新:当一个依赖发生变化后,副作用会重新运行,这时候会创建一个更新后的虚拟 DOM 树。运行时渲染器遍历这棵新树,将它与旧树进行比较,然后将必要的更新应用到真实 DOM 上去。
vue3 做了哪些提升和优化
响应式模块优化
- 主要是 proxy 替代 defineProperty
- WeakMap 不会阻止垃圾回收器回收
渲染模块优化
- diff 算法优化:双端 diff 算法升级为 快速 Diff 算法;
- 组件渲染优化:Vue2 当中在父组件渲染同时,子组件也会渲染。 Vue3 就可以单独渲染父组件、子组件。
编译模块优化
编译优化的核心在于,区分动态节点与静态节点
- Vue3 会为动态节点打上补丁标志,即 patchFlag。
- Block 收集动态子节点,对动态节点的比对操作是忽略 DOM 层级结构的。
- 静态提升:能够减少更新时创建虚拟 DOM 带来的性能开销和内存占用。
- 预字符串化:在静态提升的基础上,对静态节点进行字符串化。减少创建虚拟节点产生的性能开销以及内存占用。
- 缓存内联事件处理函数:避免造成不必要的组件更新。
- v-once 指令:缓存全部或部分虚拟节点,能够避免组件更新时重新创建虚拟 DOM 带来的性能开销,也可以避免无用的 Diff 操作。
Pinia 相比 Vuex 有哪些优势
- 更简单的 API:Pinia 去掉了 Vuex 中的 Mutation,只保留了 state、getter 和 action,使得 API 更加简洁。
- 类型安全:Pinia 提供了类型安全的支持,使得在开发过程中更容易捕获潜在的错误。
- 更好的代码组织:Pinia 不需要使用模块(module)对 store 进行分割,每个 store 都是一个独立的模块,这使得代码更加清晰和易于维护。
- 性能优化:Pinia 只包含核心功能,相比 Vuex 的众多附加功能,其包体积更小,加载更快,减少了前端应用的资源占用。
Vite 相比 Webpack 有哪些优势?
- Vite 在开发阶段使用原生 ES 模块加载方式,跳过了打包操作,因此提供了更快的冷启动和即时的热模块更新。
- Vite 在构建阶段使用 Rollup 进行打包,以优化应用体积和性能。
点个关注:谢谢!